Beispiel #1
0
        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);
        }