private static void LZ4_memcpy_using_offset_base( byte *dstPtr, byte *srcPtr, byte *dstEnd, uint offset) { if (offset < 8) { dstPtr[0] = srcPtr[0]; dstPtr[1] = srcPtr[1]; dstPtr[2] = srcPtr[2]; dstPtr[3] = srcPtr[3]; srcPtr += inc32table[offset]; Mem.Copy4(dstPtr + 4, srcPtr); srcPtr -= dec64table[offset]; dstPtr += 8; } else { Mem.Copy8(dstPtr, srcPtr); dstPtr += 8; srcPtr += 8; } Mem.WildCopy8(dstPtr, srcPtr, dstEnd); }
protected static int LZ4_compress_generic( LZ4_stream_t *cctx, byte *source, byte *dest, int inputSize, int *inputConsumed, /* only written when outputDirective == fillOutput */ int maxOutputSize, limitedOutput_directive outputDirective, tableType_t tableType, dict_directive dictDirective, dictIssue_directive dictIssue, int acceleration) { int result; byte *ip = (byte *)source; uint startIndex = cctx->currentOffset; byte * @base = (byte *)source - startIndex; byte * lowLimit; LZ4_stream_t *dictCtx = (LZ4_stream_t *)cctx->dictCtx; byte * dictionary = dictDirective == dict_directive.usingDictCtx ? dictCtx->dictionary : cctx->dictionary; uint dictSize = dictDirective == dict_directive.usingDictCtx ? dictCtx->dictSize : cctx->dictSize; uint dictDelta = (dictDirective == dict_directive.usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; bool maybe_extMem = (dictDirective == dict_directive.usingExtDict) || (dictDirective == dict_directive.usingDictCtx); uint prefixIdxLimit = startIndex - dictSize; byte *dictEnd = dictionary + dictSize; byte *anchor = (byte *)source; byte *iend = ip + inputSize; byte *mflimitPlusOne = iend - MFLIMIT + 1; byte *matchlimit = iend - LASTLITERALS; /* the dictCtx currentOffset is indexed on the start of the dictionary, * while a dictionary in the current context precedes the currentOffset */ byte *dictBase = (dictDirective == dict_directive.usingDictCtx) ? dictionary + dictSize - dictCtx->currentOffset : dictionary + dictSize - startIndex; byte *op = (byte *)dest; byte *olimit = op + maxOutputSize; uint offset = 0; uint forwardH; if (outputDirective == limitedOutput_directive.fillOutput && maxOutputSize < 1) { return(0); } if ((uint)inputSize > (uint)LZ4_MAX_INPUT_SIZE) { return(0); } if ((tableType == tableType_t.byU16) && (inputSize >= LZ4_64Klimit)) { return(0); } if (tableType == tableType_t.byPtr) { Assert(dictDirective == dict_directive.noDict); } Assert(acceleration >= 1); lowLimit = (byte *)source - (dictDirective == dict_directive.withPrefix64k ? dictSize : 0); /* Update context state */ if (dictDirective == dict_directive.usingDictCtx) { /* Subsequent linked blocks can't use the dictionary. */ /* Instead, they use the block we just compressed. */ cctx->dictCtx = null; cctx->dictSize = (uint)inputSize; } else { cctx->dictSize += (uint)inputSize; } cctx->currentOffset += (uint)inputSize; cctx->tableType = tableType; if (inputSize < LZ4_minLength) { goto _last_literals; } /* First Byte */ LZ4_putPosition(ip, cctx->hashTable, tableType, @base); ip++; forwardH = LZ4_hashPosition(ip, tableType); /* Main Loop */ for (;;) { byte *match; byte *token; byte *filledIp; /* Find a match */ if (tableType == tableType_t.byPtr) { byte *forwardIp = ip; int step = 1; int searchMatchNb = acceleration << LZ4_skipTrigger; do { uint h = forwardH; ip = forwardIp; forwardIp += step; step = (searchMatchNb++ >> LZ4_skipTrigger); if ((forwardIp > mflimitPlusOne)) { goto _last_literals; } Assert(ip < mflimitPlusOne); match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, @base); forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, @base); }while ((match + LZ4_DISTANCE_MAX < ip) || (Mem.Peek4(match) != Mem.Peek4(ip))); } else { /* byU32, byU16 */ byte *forwardIp = ip; int step = 1; int searchMatchNb = acceleration << LZ4_skipTrigger; do { uint h = forwardH; uint current = (uint)(forwardIp - @base); uint matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); Assert(matchIndex <= current); Assert(forwardIp - @base < (2 * GB - 1)); ip = forwardIp; forwardIp += step; step = (searchMatchNb++ >> LZ4_skipTrigger); if ((forwardIp > mflimitPlusOne)) { goto _last_literals; } Assert(ip < mflimitPlusOne); if (dictDirective == dict_directive.usingDictCtx) { if (matchIndex < startIndex) { Assert(tableType == tableType_t.byU32); matchIndex = LZ4_getIndexOnHash( h, dictCtx->hashTable, tableType_t.byU32); match = dictBase + matchIndex; matchIndex += dictDelta; lowLimit = dictionary; } else { match = @base + matchIndex; lowLimit = (byte *)source; } } else if (dictDirective == dict_directive.usingExtDict) { if (matchIndex < startIndex) { Assert(startIndex - matchIndex >= MINMATCH); match = dictBase + matchIndex; lowLimit = dictionary; } else { match = @base + matchIndex; lowLimit = (byte *)source; } } else { match = @base + matchIndex; } forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); if ((dictIssue == dictIssue_directive.dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } Assert(matchIndex < current); if (((tableType != tableType_t.byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) && (matchIndex + LZ4_DISTANCE_MAX < current)) { continue; } Assert((current - matchIndex) <= LZ4_DISTANCE_MAX); if (Mem.Peek4(match) == Mem.Peek4(ip)) { if (maybe_extMem) { offset = current - matchIndex; } break; } }while (true); } filledIp = ip; while (((ip > anchor) & (match > lowLimit)) && ((ip[-1] == match[-1]))) { ip--; match--; } { var litLength = (uint)(ip - anchor); token = op++; if ((outputDirective == limitedOutput_directive.limitedOutput) && ((op + litLength + (2 + 1 + LASTLITERALS) + (litLength / 255) > olimit))) { return(0); } if ((outputDirective == limitedOutput_directive.fillOutput) && ((op + (litLength + 240) / 255 + litLength + 2 + 1 + MFLIMIT - MINMATCH > olimit))) { op--; goto _last_literals; } if (litLength >= RUN_MASK) { int len = (int)(litLength - RUN_MASK); *token = (byte)(RUN_MASK << ML_BITS); for (; len >= 255; len -= 255) { *op++ = 255; } *op++ = (byte)len; } else { *token = (byte)(litLength << ML_BITS); } Mem.WildCopy8(op, anchor, op + litLength); op += litLength; } _next_match: /* at this stage, the following variables must be correctly set : * - ip : at start of LZ operation * - match : at start of previous pattern occurence; can be within current prefix, or within extDict * - offset : if maybe_ext_memSegment==1 (constant) * - lowLimit : must be == dictionary to mean "match is within extDict"; must be == source otherwise * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written */ if ((outputDirective == limitedOutput_directive.fillOutput) && (op + 2 + 1 + MFLIMIT - MINMATCH > olimit)) { /* the match was too close to the end, rewind and go to last literals */ op = token; goto _last_literals; } /* Encode Offset */ if (maybe_extMem) { /* static test */ Assert(offset <= LZ4_DISTANCE_MAX && offset > 0); Mem.Poke2(op, (ushort)offset); op += 2; } else { Assert(ip - match <= LZ4_DISTANCE_MAX); Mem.Poke2(op, (ushort)(ip - match)); op += 2; } /* Encode MatchLength */ { uint matchCode; if ((dictDirective == dict_directive.usingExtDict || dictDirective == dict_directive.usingDictCtx) && (lowLimit == dictionary) /* match within extDict */) { byte *limit = ip + (dictEnd - match); Assert(dictEnd > match); if (limit > matchlimit) { limit = matchlimit; } matchCode = LZ4_count(ip + MINMATCH, match + MINMATCH, limit); ip += (uint)matchCode + MINMATCH; if (ip == limit) { uint more = LZ4_count(limit, (byte *)source, matchlimit); matchCode += more; ip += more; } } else { matchCode = LZ4_count(ip + MINMATCH, match + MINMATCH, matchlimit); ip += (uint)matchCode + MINMATCH; } if ((outputDirective != 0) && ((op + (1 + LASTLITERALS) + (matchCode + 240) / 255 > olimit))) { if (outputDirective == limitedOutput_directive.fillOutput) { /* Match description too long : reduce it */ uint newMatchCode = 15 - 1 + ((uint)(olimit - op) - 1 - LASTLITERALS) * 255; ip -= matchCode - newMatchCode; Assert(newMatchCode < matchCode); matchCode = newMatchCode; if ((ip <= filledIp)) { /* We have already filled up to filledIp so if ip ends up less than filledIp * we have positions in the hash table beyond the current position. This is * a problem if we reuse the hash table. So we have to remove these positions * from the hash table. */ byte *ptr; for (ptr = ip; ptr <= filledIp; ++ptr) { uint h = LZ4_hashPosition(ptr, tableType); LZ4_clearHash(h, cctx->hashTable, tableType); } } } else { Assert(outputDirective == limitedOutput_directive.limitedOutput); return(0); } } if (matchCode >= ML_MASK) { *token += (byte)ML_MASK; //!!! matchCode -= ML_MASK; Mem.Poke4(op, 0xFFFFFFFF); while (matchCode >= 4 * 255) { op += 4; Mem.Poke4(op, 0xFFFFFFFF); matchCode -= 4 * 255; } op += matchCode / 255; *op++ = (byte)(matchCode % 255); } else { *token += (byte)(matchCode); } } /* Ensure we have enough space for the last literals. */ Assert( !(outputDirective == limitedOutput_directive.fillOutput && op + 1 + LASTLITERALS > olimit)); anchor = ip; /* Test end of chunk */ if (ip >= mflimitPlusOne) { break; } /* Fill table */ LZ4_putPosition(ip - 2, cctx->hashTable, tableType, @base); /* Test next position */ if (tableType == tableType_t.byPtr) { match = LZ4_getPosition(ip, cctx->hashTable, tableType, @base); LZ4_putPosition(ip, cctx->hashTable, tableType, @base); if ((match + LZ4_DISTANCE_MAX >= ip) && (Mem.Peek4(match) == Mem.Peek4(ip))) { token = op++; *token = 0; goto _next_match; } } else { /* byU32, byU16 */ uint h = LZ4_hashPosition(ip, tableType); uint current = (uint)(ip - @base); uint matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); Assert(matchIndex < current); if (dictDirective == dict_directive.usingDictCtx) { if (matchIndex < startIndex) { matchIndex = LZ4_getIndexOnHash( h, dictCtx->hashTable, tableType_t.byU32); match = dictBase + matchIndex; lowLimit = dictionary; matchIndex += dictDelta; } else { match = @base + matchIndex; lowLimit = (byte *)source; } } else if (dictDirective == dict_directive.usingExtDict) { if (matchIndex < startIndex) { match = dictBase + matchIndex; lowLimit = dictionary; } else { match = @base + matchIndex; lowLimit = (byte *)source; } } else { match = @base + matchIndex; } LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); Assert(matchIndex < current); if (((dictIssue != dictIssue_directive.dictSmall) || (matchIndex >= prefixIdxLimit)) && (((tableType == tableType_t.byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) || (matchIndex + LZ4_DISTANCE_MAX >= current)) && (Mem.Peek4(match) == Mem.Peek4(ip))) { token = op++; *token = 0; if (maybe_extMem) { offset = current - matchIndex; } goto _next_match; } } forwardH = LZ4_hashPosition(++ip, tableType); } _last_literals: { var lastRun = (size_t)(iend - anchor); if ((outputDirective != 0) && (op + lastRun + 1 + ((lastRun + 255 - RUN_MASK) / 255) > olimit)) { if (outputDirective == limitedOutput_directive.fillOutput) { Assert(olimit >= op); lastRun = (size_t)(olimit - op) - 1; lastRun -= (lastRun + 240) / 255; } else { Assert(outputDirective == limitedOutput_directive.limitedOutput); return(0); } } if (lastRun >= RUN_MASK) { var accumulator = (size_t)(lastRun - RUN_MASK); *op++ = (byte)(RUN_MASK << ML_BITS); for (; accumulator >= 255; accumulator -= 255) { *op++ = 255; } *op++ = (byte)accumulator; } else { *op++ = (byte)(lastRun << ML_BITS); } Mem.Copy(op, anchor, (int)lastRun); ip = anchor + lastRun; op += lastRun; } if (outputDirective == limitedOutput_directive.fillOutput) { *inputConsumed = (int)(((byte *)ip) - source); } result = (int)(((byte *)op) - dest); Assert(result > 0); return(result); }
public static int LZ4_decompress_generic( byte *src, byte *dst, int srcSize, int outputSize, bool endOnInput, // endCondition_directive bool partialDecoding, // earlyEnd_directive dict_directive dict, byte *lowPrefix, byte *dictStart, size_t dictSize) { if (src == null) { return(-1); } { byte *ip = (byte *)src; byte *iend = ip + srcSize; byte *op = (byte *)dst; byte *oend = op + outputSize; byte *cpy; byte *dictEnd = (dictStart == null) ? null : dictStart + dictSize; bool safeDecode = endOnInput; bool checkOffset = ((safeDecode) && (dictSize < (int)(64 * KB))); /* Set up the "end" pointers for the shortcut. */ byte *shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/; byte *shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/; byte * match; size_t offset; uint token; size_t length; /* Special cases */ Assert(lowPrefix <= op); if ((endOnInput) && ((outputSize == 0))) { /* Empty output buffer */ if (partialDecoding) { return(0); } return(((srcSize == 1) && (*ip == 0)) ? 0 : -1); } if ((!endOnInput) && ((outputSize == 0))) { return(*ip == 0 ? 1 : -1); } if ((endOnInput) && (srcSize == 0)) { return(-1); } /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ while (true) { token = *ip++; length = (size_t)(token >> ML_BITS); /* literal length */ Assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ /* A two-stage shortcut for the most common case: * 1) If the literal length is 0..14, and there is enough space, * enter the shortcut and copy 16 bytes on behalf of the literals * (in the fast mode, only 8 bytes can be safely copied this way). * 2) Further if the match length is 4..18, copy 18 bytes in a similar * manner; but we ensure that there's enough space in the output for * those 18 bytes earlier, upon entering the shortcut (in other words, * there is a combined check for both stages). */ if ((endOnInput ? length != RUN_MASK : length <= 8) /* strictly "less than" on input, to re-enter the loop with at least one byte */ && ((!endOnInput || ip < shortiend) & (op <= shortoend))) { /* Copy the literals */ if (endOnInput) { Mem.Copy16(op, ip); } else { Mem.Copy8(op, ip); } // Mem.Copy(op, ip, endOnInput ? 16 : 8); op += length; ip += length; /* The second stage: prepare for match copying, decode full info. * If it doesn't work out, the info won't be wasted. */ length = token & ML_MASK; /* match length */ offset = Mem.Peek2(ip); ip += 2; match = op - offset; Assert(match <= op); /* check overflow */ /* Do not deal with overlapping matches. */ if ((length != ML_MASK) && (offset >= 8) && (dict == dict_directive.withPrefix64k || match >= lowPrefix)) { /* Copy the match. */ Mem.Copy18(op, match); op += length + MINMATCH; /* Both stages worked, load the next token. */ continue; } /* The second stage didn't work out, but the info is ready. * Propel it right to the point of match copying. */ goto _copy_match; } /* decode literal length */ if (length == RUN_MASK) { variable_length_error error = variable_length_error.ok; length += LZ4_readVLE(&ip, iend - RUN_MASK, endOnInput, endOnInput, &error); if (error == variable_length_error.initial_error) { goto _output_error; } if ((safeDecode) && ((op) + length < (op))) { goto _output_error; } /* overflow detection */ if ((safeDecode) && ((ip) + length < (ip))) { goto _output_error; } /* overflow detection */ } /* copy literals */ cpy = op + length; if (((endOnInput) && ((cpy > oend - MFLIMIT) || (ip + length > iend - (2 + 1 + LASTLITERALS)))) || ((!endOnInput) && (cpy > oend - WILDCOPYLENGTH))) { /* We've either hit the input parsing restriction or the output parsing restriction. * If we've hit the input parsing condition then this must be the last sequence. * If we've hit the output parsing condition then we are either using partialDecoding * or we've hit the output parsing condition. */ if (partialDecoding) { /* Since we are partial decoding we may be in this block because of the output parsing * restriction, which is not valid since the output buffer is allowed to be undersized. */ Assert(endOnInput); /* If we're in this block because of the input parsing condition, then we must be on the * last sequence (or invalid), so we must check that we exactly consume the input. */ if ((ip + length > iend - (2 + 1 + LASTLITERALS)) && (ip + length != iend)) { goto _output_error; } Assert(ip + length <= iend); /* We are finishing in the middle of a literals segment. * Break after the copy. */ if (cpy > oend) { cpy = oend; Assert(op <= oend); length = (size_t)(oend - op); } Assert(ip + length <= iend); } else { /* We must be on the last sequence because of the parsing limitations so check * that we exactly regenerate the original size (must be exact when !endOnInput). */ if ((!endOnInput) && (cpy != oend)) { goto _output_error; } /* We must be on the last sequence (or invalid) because of the parsing limitations * so check that we exactly consume the input and don't overrun the output buffer. */ if ((endOnInput) && ((ip + length != iend) || (cpy > oend))) { goto _output_error; } } Mem.Move(op, ip, (int)length); /* supports overlapping memory regions, which only matters for in-place decompression scenarios */ ip += length; op += length; /* Necessarily EOF when !partialDecoding. When partialDecoding * it is EOF if we've either filled the output buffer or hit * the input parsing restriction. */ if (!partialDecoding || (cpy == oend) || (ip == iend)) { break; } } else { Mem.WildCopy8( op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */ ip += length; op = cpy; } /* get offset */ offset = Mem.Peek2(ip); ip += 2; match = op - offset; /* get matchlength */ length = token & ML_MASK; _copy_match: if (length == ML_MASK) { variable_length_error error = variable_length_error.ok; length += LZ4_readVLE( &ip, iend - LASTLITERALS + 1, endOnInput, false, &error); if (error != variable_length_error.ok) { goto _output_error; } if ((safeDecode) && ((op) + length < op)) { goto _output_error; /* overflow detection */ } } length += MINMATCH; if ((checkOffset) && ((match + dictSize < lowPrefix))) { goto _output_error; /* Error : offset outside buffers */ } /* match starting within external dictionary */ if ((dict == dict_directive.usingExtDict) && (match < lowPrefix)) { if ((op + length > oend - LASTLITERALS)) { if (partialDecoding) { length = MIN(length, (size_t)(oend - op)); } else { goto _output_error; /* doesn't respect parsing restriction */ } } if (length <= (size_t)(lowPrefix - match)) { /* match fits entirely within external dictionary : just copy */ Mem.Move(op, dictEnd - (lowPrefix - match), (int)length); op += length; } else { /* match stretches into both external dictionary and current block */ size_t copySize = (size_t)(lowPrefix - match); size_t restSize = length - copySize; Mem.Copy(op, dictEnd - copySize, (int)copySize); op += copySize; if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ byte *endOfMatch = op + restSize; byte *copyFrom = lowPrefix; while (op < endOfMatch) { *op++ = *copyFrom++; } } else { Mem.Copy(op, lowPrefix, (int)restSize); op += restSize; } } continue; } Assert(match >= lowPrefix); /* copy match within block */ cpy = op + length; /* partialDecoding : may end anywhere within the block */ Assert(op <= oend); if (partialDecoding && (cpy > oend - MATCH_SAFEGUARD_DISTANCE)) { size_t mlen = MIN(length, (size_t)(oend - op)); byte * matchEnd = match + mlen; byte * copyEnd = op + mlen; if (matchEnd > op) { /* overlap copy */ while (op < copyEnd) { *op++ = *match++; } } else { Mem.Copy(op, match, (int)mlen); } op = copyEnd; if (op == oend) { break; } continue; } if ((offset < 8)) { // Mem.Poke4(op, 0); /* silence msan warning when offset==0 */ op[0] = match[0]; op[1] = match[1]; op[2] = match[2]; op[3] = match[3]; match += inc32table[offset]; Mem.Copy4(op + 4, match); match -= dec64table[offset]; } else { Mem.Copy8(op, match); match += 8; } op += 8; if ((cpy > oend - MATCH_SAFEGUARD_DISTANCE)) { byte *oCopyLimit = oend - (WILDCOPYLENGTH - 1); if (cpy > oend - LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ if (op < oCopyLimit) { Mem.WildCopy8(op, match, oCopyLimit); match += oCopyLimit - op; op = oCopyLimit; } while (op < cpy) { *op++ = *match++; } } else { Mem.Copy8(op, match); if (length > 16) { Mem.WildCopy8(op + 8, match + 8, cpy); } } op = cpy; /* wildcopy correction */ } /* end of decoding */ if (endOnInput) { return((int)(((byte *)op) - dst)); /* Nb of output bytes decoded */ } else { return((int)(((byte *)ip) - src)); /* Nb of input bytes read */ } /* Overflow error detected */ _output_error: return((int)(-(((byte *)ip) - src)) - 1); } }