Beispiel #1
0
            /// <summary>
            /// Adapted from https://github.com/google/brotli/blob/master/c/enc/encode.c (EncodeData).
            /// </summary>
            public (MetaBlock MetaBlock, BrotliEncodeInfo Next) Encode(BrotliEncodeInfo info)
            {
                var builder = info.NewBuilder();

                do
                {
                    position = lastProcessedPos;
                    // TODO extend last command somehow???

                    int chunkLength = Math.Min(input.Length - position - lastInsertLen, InputBlockSize(lgBlock));

                    if (chunkLength == 0)
                    {
                        break;
                    }

                    hasher.StitchToPreviousBlock(chunkLength, position);

                    lastProcessedPos = position + chunkLength;
                    CreateBackwardReferences(builder, chunkLength);
                }while(lastProcessedPos + lastInsertLen < input.Length && ShouldContinueThisBlock(builder));

                if (lastInsertLen > 0)
                {
                    builder.AddInsert(Literal.FromBytes(info.Bytes.Slice(builder.OutputSize, lastInsertLen)));
                    lastInsertLen = 0;
                }

                return(builder.Build(info));
            }
            private NextStep EmitRemainder(CompressedMetaBlockBuilder builder)
            {
                nextIpStart += blockSize;
                inputSize   -= blockSize;
                blockSize    = Math.Min(inputSize, MergeBlockSize);

                if (inputSize > 0 && totalBlockSize + blockSize <= (1 << 20) && ShouldMergeBlock(nextIpStart, blockSize))
                {
                    totalBlockSize += blockSize;
                    return(NextStep.EmitCommands);
                }

                if (nextEmit < ipEnd)
                {
                    int insert = ipEnd - nextEmit;

                    if (insert >= 6210 && ShouldUseUncompressedMode(insert))
                    {
                        outputTo = ipEnd;
                        return(NextStep.OutputUncompressed);
                    }
                    else
                    {
                        builder.AddInsert(Literal.FromBytes(input, nextEmit, insert));
                    }
                }

                nextEmit = ipEnd;
                return(NextStep.OutputCompressed);
            }
Beispiel #3
0
        public (MetaBlock, BrotliEncodeInfo) Encode(BrotliEncodeInfo info)
        {
            var bytes = CollectionHelper.SliceAtMost(info.Bytes, DataLength.MaxUncompressedBytes).ToArray();

            return(info.NewBuilder()
                   .AddInsert(Literal.FromBytes(bytes))
                   .Build(info));
        }
Beispiel #4
0
            /// <summary>
            /// Adapted from https://github.com/google/brotli/blob/master/c/enc/backward_references.c (BrotliCreateBackwardReferences).
            /// </summary>
            private void CreateBackwardReferences(CompressedMetaBlockBuilder builder, int chunkLength)
            {
                int maxBackwardLimit = fileParameters.WindowSize.Bytes;
                int insertLength     = lastInsertLen;

                int posEnd   = position + chunkLength;
                int storeEnd = chunkLength >= hasher.StoreLookahead ? posEnd - hasher.StoreLookahead + 1 : position;

                int applyRandomHeuristics = position + LiteralSpreeLengthForSparseSearch;

                while (position + hasher.HashTypeLength < posEnd)
                {
                    int maxLength       = posEnd - position;
                    int maxDistance     = Math.Min(position, maxBackwardLimit);
                    int dictionaryStart = Math.Min(position, maxBackwardLimit);

                    var result = hasher.FindLongestMatch(position, maxLength, maxDistance, dictionaryStart, builder.LastDistance, 0);

                    if (result.FoundAnything)
                    {
                        int delayedBackwardReferencesInRow = 0;
                        --maxLength;

                        while (true)
                        {
                            maxDistance     = Math.Min(position + 1, maxBackwardLimit);
                            dictionaryStart = Math.Min(position + 1, maxBackwardLimit);

                            var bestLenIn = features.HasFlag(Features.ExtensiveReferenceSearch) ? 0 : Math.Min(result.Copy.OutputLength - 1, maxLength);
                            var result2   = hasher.FindLongestMatch(position + 1, maxLength, maxDistance, dictionaryStart, builder.LastDistance, bestLenIn);

                            if (result2.Score >= result.Score + CostDiffLazy)
                            {
                                ++position;
                                ++insertLength;
                                result = result2;

                                if (++delayedBackwardReferencesInRow < 4 && position + hasher.HashTypeLength < posEnd)
                                {
                                    --maxLength;
                                    continue;
                                }
                            }

                            break;
                        }

                        var copy = result.Copy;
                        int len  = copy.OutputLength;

                        applyRandomHeuristics = position + (2 * len) + LiteralSpreeLengthForSparseSearch;

                        copy.AddCommand(builder, Literal.FromBytes(input, position - insertLength, insertLength));
                        insertLength = 0;

                        int rangeStart = position + 2;
                        int rangeEnd   = Math.Min(position + len, storeEnd);

                        if (copy is Copy.BackReference backReference && backReference.Distance < len >> 2)
                        {
                            rangeStart = Math.Min(rangeEnd, Math.Max(rangeStart, position + len - (backReference.Distance << 2)));
                        }

                        hasher.StoreRange(rangeStart, rangeEnd);
                        position += len;
                    }
                    else
                    {
                        ++insertLength;
                        ++position;

                        if (position > applyRandomHeuristics)
                        {
                            int skipStep;
                            int nextStopOffset;

                            if (position > applyRandomHeuristics + (4 * LiteralSpreeLengthForSparseSearch))
                            {
                                skipStep       = 4;
                                nextStopOffset = 16;
                            }
                            else
                            {
                                skipStep       = 2;
                                nextStopOffset = 8;
                            }

                            int margin  = Math.Max(hasher.StoreLookahead - 1, skipStep);
                            int posJump = Math.Min(position + nextStopOffset, posEnd - margin);

                            while (position < posJump)
                            {
                                hasher.Store(position);
                                insertLength += skipStep;
                                position     += skipStep;
                            }
                        }
                    }
                }

                insertLength += posEnd - position;
                lastInsertLen = insertLength;
            }
            private NextStep Trawl(CompressedMetaBlockBuilder builder)
            {
                do
                {
                    uint hash = nextHash;
                    int  bytesBetweenHashLookups = (skip++) >> 5;

                    ip     = nextIp;
                    nextIp = ip + bytesBetweenHashLookups;

                    if (nextIp > ipLimit)
                    {
                        return(NextStep.EmitRemainder);
                    }

                    nextHash  = table.Hash(nextIp);
                    candidate = ip - lastDistance;

                    if (Match.Check(input, ip, candidate, MinMatchLen) && candidate < ip)
                    {
                        table[hash] = ip - baseIp;
                        break;
                    }

                    candidate   = baseIp + table[hash];
                    table[hash] = ip - baseIp;
                }while(!Match.Check(input, ip, candidate, MinMatchLen));

                if (ip - candidate > SupportedWindowSize.Bytes)
                {
                    return(NextStep.Trawl);
                }

                {
                    int @base    = ip;
                    int matched  = MinMatchLen + Match.DetermineLength(input, candidate + MinMatchLen, ip + MinMatchLen, ipEnd - ip - MinMatchLen);
                    int distance = @base - candidate;
                    int insert   = @base - nextEmit;

                    ip += matched;

                    if (insert >= 6210 && ShouldUseUncompressedMode(insert))
                    {
                        outputTo = @base;
                        return(NextStep.OutputUncompressed);
                    }

                    if (distance == lastDistance)
                    {
                        builder.AddInsertCopy(Literal.FromBytes(input, nextEmit, insert), 2, DistanceInfo.ExplicitCodeZero);
                    }
                    else
                    {
                        builder.AddInsertCopy(Literal.FromBytes(input, nextEmit, insert), 2, distance);
                        lastDistance = distance;
                    }

                    builder.AddCopy(matched - 2, distance);

                    nextEmit = ip;

                    if (ip >= ipLimit)
                    {
                        return(NextStep.EmitRemainder);
                    }

                    candidate = table.UpdateAndGetCandidate(ip, baseIp);
                }

                while (Match.Check(input, ip, candidate, MinMatchLen))
                {
                    int @base   = ip;
                    int matched = MinMatchLen + Match.DetermineLength(input, candidate + MinMatchLen, ip + MinMatchLen, ipEnd - ip - MinMatchLen);

                    if (ip - candidate > SupportedWindowSize.Bytes)
                    {
                        break;
                    }

                    ip          += matched;
                    lastDistance = @base - candidate;

                    builder.AddCopy(matched, lastDistance);
                    nextEmit = ip;

                    if (ip >= ipLimit)
                    {
                        return(NextStep.EmitRemainder);
                    }

                    candidate = table.UpdateAndGetCandidate(ip, baseIp);
                }

                nextHash = table.Hash(++ip);
                return(NextStep.EmitCommandsNextHash);
            }