private static int LZ4_decompress_generic <TEndCondition, TEarlyEnd, TDict, TEndian>( ref byte *src, ref byte *dst, int srcSize, int outputSize, /* If TEndCondition==EndOnInputSize, this value is `dstCapacity` */ int targetOutputSize, /* only used if TEarlyEnd==Partial*/ byte *lowPrefix, /* always <= dst, == dst when no prefix */ byte *dictStart, /* only if TDict==UsingExtDict */ uint dictSize) /* note : = 0 if TDict==NoDict */ where TEndCondition : struct, IEndCondition where TEarlyEnd : struct, IEarlyEnd where TDict : struct, IDict where TEndian : struct, IEndian { var ip = src; var iend = ip + srcSize; var op = dst; var oend = op + outputSize; var oexit = op + targetOutputSize; var dictEnd = dictStart + dictSize; /* Set up the "end" pointers for the shortcut. */ var shortiend = iend - (typeof(TEndCondition) == typeof(EndOnInputSize) ? 14 : 8) /*maxLL*/ - 2 /*offset*/; var shortoend = oend - (typeof(TEndCondition) == typeof(EndOnInputSize) ? 14 : 8) /*maxLL*/ - 18 /*maxML*/; Debug.WriteLine($"LZ4_decompress_generic (srcSize:{srcSize})"); /* Special cases */ if (typeof(TEarlyEnd) == typeof(Partial) && oexit > oend - MFLIMIT) { oexit = oend - MFLIMIT; /* targetOutputSize too high => just decode everything */ } if (typeof(TEndCondition) == typeof(EndOnInputSize) && outputSize == 0) { return(srcSize == 1 && *ip == 0 ? 0 : -1); /* Empty output buffer */ } if (typeof(TEndCondition) == typeof(EndOnOutputSize) && outputSize == 0) { return(*ip == 0 ? 1 : -1); } if (typeof(TEndCondition) == typeof(EndOnInputSize) && srcSize == 0) { return(-1); } /* Main Loop : decode sequences */ byte *cpy; while (true) { byte * match; ushort offset; var token = *ip++; var length = token >> ML_BITS; /* literal length */ assert(typeof(TEndCondition) != typeof(EndOnInputSize) || 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 ((typeof(TEndCondition) == typeof(EndOnInputSize) ? length != RUN_MASK : length <= 8) /* strictly "less than" on input, to re-enter the loop with at least one byte */ && (typeof(TEndCondition) != typeof(EndOnInputSize) || ip < shortiend) & (op <= shortoend)) { /* Copy the literals */ if (typeof(TEndCondition) == typeof(EndOnInputSize)) { Mem.Copy16(op, ip); } else { Mem.Copy8(op, ip); } 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 = LZ4_readLE16 <TEndian>(ip); ip += 2; match = op - offset; /* Do not deal with overlapping matches. */ if (length != ML_MASK && offset >= 8 && (typeof(TDict) == typeof(WithPrefix64K) || match >= lowPrefix)) { /* Copy the match. */ Unsafe.CopyBlock(op, match, 18); 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) { byte s; if (typeof(TEndCondition) == typeof(EndOnInputSize)) { if (ip >= iend - RUN_MASK) { goto _output_error; /* overflow detection */ } do { s = *ip++; length += s; } while (ip < iend - RUN_MASK && s == 255); if (op + length < op || ip + length < ip) { goto _output_error; /* overflow detection */ } } else { do { s = *ip++; length += s; } while (s == 255); } } /* copy literals */ cpy = op + length; if (typeof(TEndCondition) == typeof(EndOnInputSize) && (cpy > (typeof(TEarlyEnd) == typeof(Partial) ? oexit : oend - MFLIMIT) || ip + length > iend - (2 + 1 + LASTLITERALS)) || typeof(TEndCondition) == typeof(EndOnOutputSize) && cpy > oend - WILDCOPYLENGTH) { if (typeof(TEarlyEnd) == typeof(Partial)) { if (cpy > oend) { goto _output_error; /* Error : write attempt beyond end of output buffer */ } if (typeof(TEndCondition) == typeof(EndOnInputSize) && ip + length > iend) { goto _output_error; /* Error : read attempt beyond end of input buffer */ } } else { if (typeof(TEndCondition) != typeof(EndOnInputSize) && cpy != oend) { goto _output_error; /* Error : block decoding must stop exactly there */ } if (typeof(TEndCondition) == typeof(EndOnInputSize) && (ip + length != iend || cpy > oend)) { goto _output_error; /* Error : input must be consumed */ } } Unsafe.CopyBlock(op, ip, (uint)length); ip += length; op += length; break; /* Necessarily EOF, due to parsing restrictions */ } Mem.WildCopy(op, ip, cpy); ip += length; op = cpy; /* get offset */ offset = LZ4_readLE16 <TEndian>(ip); ip += 2; match = op - offset; /* get matchlength */ length = token & ML_MASK; _copy_match: if (typeof(TEndCondition) == typeof(EndOnInputSize) && dictSize < _64KB && !(match + dictSize < lowPrefix)) { goto _output_error; /* Error : offset outside buffers */ } Mem.Write(op, (uint)offset); /* costs ~1%; silence an msan warning when offset==0 */ if (length == ML_MASK) { byte s; do { s = *ip++; if (typeof(TEndCondition) == typeof(EndOnInputSize) && ip > iend - LASTLITERALS) { goto _output_error; } length += s; } while (s == 255); if (typeof(TEndCondition) == typeof(EndOnInputSize) && op + length < op) { goto _output_error; /* overflow detection */ } } length += MINMATCH; /* check external dictionary */ if (typeof(TDict) == typeof(UsingExtDict) && match < lowPrefix) { if (op + length > oend - LASTLITERALS) { goto _output_error; /* doesn't respect parsing restriction */ } if (length <= lowPrefix - match) { /* match can be copied as a single segment from external dictionary */ Platform.Move(op, dictEnd - (lowPrefix - match), length); op += length; } else { /* match encompass external dictionary and current block */ var copySize = (uint)(lowPrefix - match); var restSize = (uint)length - copySize; Unsafe.CopyBlock(op, dictEnd - copySize, copySize); op += copySize; if (restSize > (uint)(op - lowPrefix)) { /* overlap copy */ var endOfMatch = op + restSize; var copyFrom = lowPrefix; while (op < endOfMatch) { *op++ = *copyFrom++; } } else { Unsafe.CopyBlock(op, lowPrefix, restSize); op += restSize; } } continue; } /* copy match within block */ cpy = op + length; if (offset < 8) { op[0] = match[0]; op[1] = match[1]; op[2] = match[2]; op[3] = match[3]; match += inc32table[offset]; Unsafe.CopyBlock(op + 4, match, 4); match -= dec64table[offset]; } else { Unsafe.CopyBlock(op, match, 8); match += 8; } op += 8; if (cpy > oend - 12) { var oCopyLimit = oend - (WILDCOPYLENGTH - 1); if (cpy > oend - LASTLITERALS) { goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ } if (op < oCopyLimit) { Mem.WildCopy(op, match, oCopyLimit); match += oCopyLimit - op; op = oCopyLimit; } while (op < cpy) { *op++ = *match++; } } else { Mem.Copy8(op, match); if (length > 16) { Mem.WildCopy(op + 8, match + 8, cpy); } } op = cpy; /* correction */ } var wrote = (int)(op - dst); var read = (int)(ip - src); dst += wrote; src += read; /* end of decoding */ if (typeof(TEndCondition) == typeof(EndOnInputSize)) { return(wrote); /* Nb of output bytes decoded */ } return(read); /* Nb of input bytes read */ /* Overflow error detected */ _output_error: return((int)(-(ip - src)) - 1); }
public static int LZ4_decompress_generic( byte *src, byte *dst, int srcSize, int outputSize, endCondition_directive endOnInput, earlyEnd_directive partialDecoding, int targetOutputSize, dict_directive dict, byte *lowPrefix, byte *dictStart, int dictSize) { var ip = src; var iend = ip + srcSize; var op = dst; var oend = op + outputSize; var oexit = op + targetOutputSize; var dictEnd = dictStart + dictSize; var safeDecode = endOnInput == endCondition_directive.endOnInputSize; var checkOffset = safeDecode && dictSize < 64 * KB; if (partialDecoding != earlyEnd_directive.full && oexit > oend - MFLIMIT) { oexit = oend - MFLIMIT; } if (endOnInput == endCondition_directive.endOnInputSize && outputSize == 0) { return(srcSize == 1 && *ip == 0 ? 0 : -1); } if (endOnInput != endCondition_directive.endOnInputSize && outputSize == 0) { return(*ip == 0 ? 1 : -1); } for (;;) { int length; uint token = *ip++; if (ip + 14 + 2 <= iend && op + 14 + 18 <= oend && token < 15 << ML_BITS && (token & ML_MASK) != 15) { var ll = (int)(token >> ML_BITS); int off = Mem.Peek16(ip + ll); var matchPtr = op + ll - off; if (off >= 18 && matchPtr >= lowPrefix) { var ml = (int)((token & ML_MASK) + MINMATCH); Mem.Copy16(op, ip); op += ll; ip += ll + 2; Mem.Copy18(op, matchPtr); op += ml; continue; } } if ((length = (int)(token >> ML_BITS)) == RUN_MASK) { uint s; do { s = *ip++; length += (int)s; }while ( (endOnInput != endCondition_directive.endOnInputSize || ip < iend - RUN_MASK) && s == 255); if (safeDecode && op + length < op) { goto _output_error; } if (safeDecode && ip + length < ip) { goto _output_error; } } var cpy = op + length; if (endOnInput == endCondition_directive.endOnInputSize && ( cpy > (partialDecoding == earlyEnd_directive.partial ? oexit : oend - MFLIMIT) || ip + length > iend - (2 + 1 + LASTLITERALS) ) || endOnInput != endCondition_directive.endOnInputSize && cpy > oend - WILDCOPYLENGTH) { if (partialDecoding == earlyEnd_directive.partial) { if (cpy > oend) { goto _output_error; } if (endOnInput == endCondition_directive.endOnInputSize && ip + length > iend) { goto _output_error; } } else { if (endOnInput != endCondition_directive.endOnInputSize && cpy != oend) { goto _output_error; } if (endOnInput == endCondition_directive.endOnInputSize && (ip + length != iend || cpy > oend)) { goto _output_error; } } Mem.Copy(op, ip, length); ip += length; op += length; break; } Mem.WildCopy(op, ip, cpy); ip += length; op = cpy; int offset = Mem.Peek16(ip); ip += 2; var match = op - offset; if (checkOffset && match + dictSize < lowPrefix) { goto _output_error; } Mem.Poke32(op, (uint)offset); length = (int)(token & ML_MASK); if (length == ML_MASK) { uint s; do { s = *ip++; if ((endOnInput == endCondition_directive.endOnInputSize) && (ip > iend - LASTLITERALS)) { goto _output_error; } length += (int)s; }while (s == 255); if (safeDecode && op + length < op) { goto _output_error; } } length += MINMATCH; if (dict == dict_directive.usingExtDict && match < lowPrefix) { if (op + length > oend - LASTLITERALS) { goto _output_error; } if (length <= lowPrefix - match) { Mem.Move(op, dictEnd - (lowPrefix - match), length); op += length; } else { var copySize = (int)(lowPrefix - match); var restSize = length - copySize; Mem.Copy(op, dictEnd - copySize, copySize); op += copySize; if (restSize > (int)(op - lowPrefix)) { var endOfMatch = op + restSize; var copyFrom = lowPrefix; while (op < endOfMatch) { *op++ = *copyFrom++; } } else { Mem.Copy(op, lowPrefix, restSize); op += restSize; } } continue; } cpy = op + length; if (offset < 8) { op[0] = match[0]; op[1] = match[1]; op[2] = match[2]; op[3] = match[3]; match += inc32table[offset]; Mem.Copy(op + 4, match, 4); match -= dec64table[offset]; } else { Mem.Copy8(op, match); match += 8; } op += 8; if (cpy > oend - 12) { var oCopyLimit = oend - (WILDCOPYLENGTH - 1); if (cpy > oend - LASTLITERALS) { goto _output_error; } if (op < oCopyLimit) { Mem.WildCopy(op, match, oCopyLimit); match += oCopyLimit - op; op = oCopyLimit; } while (op < cpy) { *op++ = *match++; } } else { Mem.Copy8(op, match); if (length > 16) { Mem.WildCopy(op + 8, match + 8, cpy); } } op = cpy; /* correction */ } /* end of decoding */ if (endOnInput == endCondition_directive.endOnInputSize) { return((int)(op - dst)); /* Nb of output bytes decoded */ } return((int)(ip - src)); /* Nb of input bytes read */ /* Overflow error detected */ _output_error: return((int)-(ip - src) - 1); }