예제 #1
0
        internal static void BrotliDecompress(Stream input, OutputStream output)
        {
            int  i;
            int  pos         = 0;
            bool input_end   = false;
            int  window_bits = 0;
            int  max_backward_distance;
            int  max_distance = 0;
            int  ringbuffer_size;
            int  ringbuffer_mask;

            byte[] ringbuffer;
            int    ringbuffer_end;

            /* This ring buffer holds a few past copy distances that will be used by */
            /* some special distance codes. */
            byte[] dist_rb     = new byte[] { 16, 15, 11, 4 };
            int    dist_rb_idx = 0;
            /* The previous 2 bytes used for context. */
            byte prev_byte1 = 0;
            byte prev_byte2 = 0;

            HuffmanTreeGroup[] hgroup = new HuffmanTreeGroup[] {
                new HuffmanTreeGroup(0, 0), new HuffmanTreeGroup(0, 0), new HuffmanTreeGroup(0, 0)
            };

            /* We need the slack region for the following reasons:
             *               - always doing two 8-byte copies for fast backward copying
             *               - transforms
             *               - flushing the input ringbuffer when decoding uncompressed blocks */
            const int kRingBufferWriteAheadSlack = 128 + BitReader.READ_SIZE;

            BitReader br = new BitReader(input);

            /* Decode window size. */
            window_bits           = DecodeWindowBits(br);
            max_backward_distance = (1 << window_bits) - 16;

            ringbuffer_size = 1 << window_bits;
            ringbuffer_mask = ringbuffer_size - 1;
            ringbuffer      = new byte[ringbuffer_size + kRingBufferWriteAheadSlack + Dictionary.MaxDictionaryWordLength];
            ringbuffer_end  = ringbuffer_size;

            HuffmanCode[] block_type_trees = new HuffmanCode[3 * HUFFMAN_MAX_TABLE_SIZE];
            HuffmanCode[] block_len_trees  = new HuffmanCode[3 * HUFFMAN_MAX_TABLE_SIZE];

            for (int x = 0; x < 3 * HUFFMAN_MAX_TABLE_SIZE; x++)
            {
                block_type_trees[x] = new HuffmanCode(0, 0);
                block_len_trees[x]  = new HuffmanCode(0, 0);
            }

            while (!input_end)
            {
                int    meta_block_remaining_len = 0;
                bool   is_uncompressed;
                int[]  block_length        = new int[] { 1 << 28, 1 << 28, 1 << 28 };
                byte[] block_type          = new byte[] { 0 };
                byte[] num_block_types     = new byte[] { 1, 1, 1 };
                byte[] block_type_rb       = new byte[] { 0, 1, 0, 1, 0, 1 };
                byte[] block_type_rb_index = new byte[] { 0 };
                int    distance_postfix_bits;
                int    num_direct_distance_codes;
                int    distance_postfix_mask;
                int    num_distance_codes;
                byte[] context_map   = null;
                byte[] context_modes = null;
                int    num_literal_htrees;
                byte[] dist_context_map = null;
                int    num_dist_htrees;
                int    context_offset         = 0;
                int    context_map_slice      = 0;
                int    literal_htree_index    = 0;
                int    dist_context_offset    = 0;
                int    dist_context_map_slice = 0;
                int    dist_htree_index       = 0;
                int    context_lookup_offset1 = 0;
                int    context_lookup_offset2 = 0;
                byte   context_mode;
                uint   htree_command;

                for (i = 0; i < 3; ++i)
                {
                    hgroup[i].codes  = null;
                    hgroup[i].htrees = null;
                }

                br.ReadMoreInput();

                MetaBlockLength _out = DecodeMetaBlockLength(br);
                meta_block_remaining_len = _out.meta_block_length;

                if (pos + meta_block_remaining_len > output.buffer.Length)
                {
                    if (output.FixedSize)
                    {
                        throw new Exception("Brotli decompressor: The provided buffer to decompress into was too small. Brotli is used by WOFF2 files.");
                    }

                    /* We need to grow the output buffer to fit the additional data. */
                    byte[] tmp = new byte[pos + meta_block_remaining_len];

                    System.Array.Copy(output.buffer, 0, tmp, 0, output.pos_);

                    output.buffer = tmp;
                }

                input_end       = _out.input_end;
                is_uncompressed = _out.is_uncompressed;

                if (_out.is_metadata)
                {
                    JumpToByteBoundary(br);

                    for (; meta_block_remaining_len > 0; --meta_block_remaining_len)
                    {
                        br.ReadMoreInput();
                        /* Read one byte and ignore it. */
                        br.ReadBits(8);
                    }

                    continue;
                }

                if (meta_block_remaining_len == 0)
                {
                    continue;
                }

                if (is_uncompressed)
                {
                    br.bit_pos_ = (br.bit_pos_ + 7) & ~7;
                    CopyUncompressedBlockToOutput(output, meta_block_remaining_len, pos,
                                                  ringbuffer, ringbuffer_mask, br);
                    pos += meta_block_remaining_len;
                    continue;
                }

                for (i = 0; i < 3; ++i)
                {
                    num_block_types[i] = (byte)(DecodeVarLenUint8(br) + 1);
                    if (num_block_types[i] >= 2)
                    {
                        ReadHuffmanCode(num_block_types[i] + 2, block_type_trees, i * HUFFMAN_MAX_TABLE_SIZE, br);
                        ReadHuffmanCode(kNumBlockLengthCodes, block_len_trees, i * HUFFMAN_MAX_TABLE_SIZE, br);
                        block_length[i]        = ReadBlockLength(block_len_trees, i * HUFFMAN_MAX_TABLE_SIZE, br);
                        block_type_rb_index[i] = 1;
                    }
                }

                br.ReadMoreInput();

                distance_postfix_bits     = (int)br.ReadBits(2);
                num_direct_distance_codes = NUM_DISTANCE_SHORT_CODES + ((int)br.ReadBits(4) << distance_postfix_bits);
                distance_postfix_mask     = (1 << distance_postfix_bits) - 1;
                num_distance_codes        = (num_direct_distance_codes + (48 << distance_postfix_bits));
                context_modes             = new byte[num_block_types[0]];

                for (i = 0; i < num_block_types[0]; ++i)
                {
                    br.ReadMoreInput();
                    context_modes[i] = (byte)(br.ReadBits(2) << 1);
                }

                ContextMap _o1 = DecodeContextMap(num_block_types[0] << kLiteralContextBits, br);
                num_literal_htrees = _o1.num_htrees;
                context_map        = _o1.context_map;

                ContextMap _o2 = DecodeContextMap(num_block_types[2] << kDistanceContextBits, br);
                num_dist_htrees  = _o2.num_htrees;
                dist_context_map = _o2.context_map;

                hgroup[0] = new HuffmanTreeGroup(kNumLiteralCodes, num_literal_htrees);
                hgroup[1] = new HuffmanTreeGroup(kNumInsertAndCopyCodes, num_block_types[1]);
                hgroup[2] = new HuffmanTreeGroup(num_distance_codes, num_dist_htrees);

                for (i = 0; i < 3; ++i)
                {
                    hgroup[i].Decode(br);
                }

                context_map_slice      = 0;
                dist_context_map_slice = 0;
                context_mode           = context_modes[block_type[0]];
                context_lookup_offset1 = Context.LookupOffsets[context_mode];
                context_lookup_offset2 = Context.LookupOffsets[context_mode + 1];
                htree_command          = hgroup[1].htrees[0];

                while (meta_block_remaining_len > 0)
                {
                    int  cmd_code;
                    int  range_idx;
                    int  insert_code;
                    int  copy_code;
                    int  insert_length;
                    int  copy_length;
                    int  distance_code;
                    int  distance;
                    byte context;
                    int  j;
                    int  copy_dst;

                    br.ReadMoreInput();

                    if (block_length[1] == 0)
                    {
                        DecodeBlockType(num_block_types[1],
                                        block_type_trees, 1, block_type, block_type_rb,
                                        block_type_rb_index, br);
                        block_length[1] = ReadBlockLength(block_len_trees, HUFFMAN_MAX_TABLE_SIZE, br);
                        htree_command   = hgroup[1].htrees[block_type[1]];
                    }

                    --block_length[1];
                    cmd_code  = ReadSymbol(hgroup[1].codes, (int)htree_command, br);
                    range_idx = cmd_code >> 6;

                    if (range_idx >= 2)
                    {
                        range_idx    -= 2;
                        distance_code = -1;
                    }
                    else
                    {
                        distance_code = 0;
                    }

                    insert_code = Prefix.kInsertRangeLut[range_idx] + ((cmd_code >> 3) & 7);

                    copy_code = Prefix.kCopyRangeLut[range_idx] + (cmd_code & 7);

                    insert_length = Prefix.kInsertLengthPrefixCode[insert_code].Offset +
                                    (int)br.ReadBits(Prefix.kInsertLengthPrefixCode[insert_code].NBits);

                    copy_length = Prefix.kCopyLengthPrefixCode[copy_code].Offset +
                                  (int)br.ReadBits(Prefix.kCopyLengthPrefixCode[copy_code].NBits);

                    prev_byte1 = ringbuffer[pos - 1 & ringbuffer_mask];
                    prev_byte2 = ringbuffer[pos - 2 & ringbuffer_mask];

                    for (j = 0; j < insert_length; ++j)
                    {
                        br.ReadMoreInput();

                        if (block_length[0] == 0)
                        {
                            DecodeBlockType(num_block_types[0],
                                            block_type_trees, 0, block_type, block_type_rb,
                                            block_type_rb_index, br);
                            block_length[0]        = ReadBlockLength(block_len_trees, 0, br);
                            context_offset         = block_type[0] << kLiteralContextBits;
                            context_map_slice      = context_offset;
                            context_mode           = context_modes[block_type[0]];
                            context_lookup_offset1 = Context.LookupOffsets[context_mode];
                            context_lookup_offset2 = Context.LookupOffsets[context_mode + 1];
                        }

                        context = Context.Lookup[context_lookup_offset1 + prev_byte1];

                        if (context == 0)
                        {
                            context = Context.Lookup[context_lookup_offset2 + prev_byte2];
                        }

                        literal_htree_index = context_map[context_map_slice + context];
                        --block_length[0];

                        prev_byte2 = prev_byte1;
                        prev_byte1 = (byte)ReadSymbol(hgroup[0].codes, (int)hgroup[0].htrees[literal_htree_index], br);
                        ringbuffer[pos & ringbuffer_mask] = prev_byte1;

                        if ((pos & ringbuffer_mask) == ringbuffer_mask)
                        {
                            output.Write(ringbuffer, ringbuffer_size);
                        }

                        ++pos;
                    }

                    meta_block_remaining_len -= insert_length;

                    if (meta_block_remaining_len <= 0)
                    {
                        break;
                    }

                    if (distance_code < 0)
                    {
                        context = 0;

                        br.ReadMoreInput();

                        if (block_length[2] == 0)
                        {
                            DecodeBlockType(num_block_types[2],
                                            block_type_trees, 2, block_type, block_type_rb,
                                            block_type_rb_index, br);
                            block_length[2]        = ReadBlockLength(block_len_trees, 2 * HUFFMAN_MAX_TABLE_SIZE, br);
                            dist_context_offset    = block_type[2] << kDistanceContextBits;
                            dist_context_map_slice = dist_context_offset;
                        }

                        --block_length[2];

                        context          = (byte)(copy_length > 4 ? 3 : copy_length - 2);
                        dist_htree_index = dist_context_map[dist_context_map_slice + context];
                        distance_code    = ReadSymbol(hgroup[2].codes, (int)hgroup[2].htrees[dist_htree_index], br);

                        if (distance_code >= num_direct_distance_codes)
                        {
                            int nbits;
                            int postfix;
                            int offset;
                            distance_code  -= num_direct_distance_codes;
                            postfix         = distance_code & distance_postfix_mask;
                            distance_code >>= distance_postfix_bits;
                            nbits           = (distance_code >> 1) + 1;
                            offset          = ((2 + (distance_code & 1)) << nbits) - 4;
                            distance_code   = num_direct_distance_codes +
                                              ((offset + (int)br.ReadBits(nbits)) <<
                                             distance_postfix_bits) + postfix;
                        }
                    }

                    /* Convert the distance code to the actual distance by possibly looking */
                    /* up past distnaces from the ringbuffer. */
                    distance = TranslateShortCodes(distance_code, dist_rb, dist_rb_idx);

                    if (distance < 0)
                    {
                        throw new Exception("[BrotliDecompress] invalid distance");
                    }

                    if (pos < max_backward_distance &&
                        max_distance != max_backward_distance)
                    {
                        max_distance = pos;
                    }
                    else
                    {
                        max_distance = max_backward_distance;
                    }

                    copy_dst = pos & ringbuffer_mask;

                    if (distance > max_distance)
                    {
                        if (copy_length >= Dictionary.MinDictionaryWordLength &&
                            copy_length <= Dictionary.MaxDictionaryWordLength)
                        {
                            int offset        = (int)Dictionary.OffsetsByLength[copy_length];
                            int word_id       = distance - max_distance - 1;
                            int shift         = Dictionary.SizeBitsByLength[copy_length];
                            int mask          = (1 << shift) - 1;
                            int word_idx      = word_id & mask;
                            int transform_idx = word_id >> shift;
                            offset += word_idx * copy_length;

                            if (transform_idx < Transforms.kNumTransforms)
                            {
                                int len = Transforms.TransformDictionaryWord(ringbuffer, copy_dst, offset, copy_length, transform_idx);
                                copy_dst += len;
                                pos      += len;
                                meta_block_remaining_len -= len;
                                if (copy_dst >= ringbuffer_end)
                                {
                                    output.Write(ringbuffer, ringbuffer_size);

                                    for (int _x = 0; _x < (copy_dst - ringbuffer_end); _x++)
                                    {
                                        ringbuffer[_x] = ringbuffer[ringbuffer_end + _x];
                                    }
                                }
                            }
                            else
                            {
                                throw new Exception("Invalid backward reference. pos: " + pos + " distance: " + distance +
                                                    " len: " + copy_length + " bytes left: " + meta_block_remaining_len);
                            }
                        }
                        else
                        {
                            throw new Exception("Invalid backward reference. pos: " + pos + " distance: " + distance +
                                                " len: " + copy_length + " bytes left: " + meta_block_remaining_len);
                        }
                    }
                    else
                    {
                        if (distance_code > 0)
                        {
                            dist_rb[dist_rb_idx & 3] = (byte)distance;
                            ++dist_rb_idx;
                        }

                        if (copy_length > meta_block_remaining_len)
                        {
                            throw new Exception("Invalid backward reference. pos: " + pos + " distance: " + distance +
                                                " len: " + copy_length + " bytes left: " + meta_block_remaining_len);
                        }

                        for (j = 0; j < copy_length; ++j)
                        {
                            ringbuffer[pos & ringbuffer_mask] = ringbuffer[(pos - distance) & ringbuffer_mask];

                            if ((pos & ringbuffer_mask) == ringbuffer_mask)
                            {
                                output.Write(ringbuffer, ringbuffer_size);
                            }

                            ++pos;
                            --meta_block_remaining_len;
                        }
                    }

                    /* When we get here, we must have inserted at least one literal and */
                    /* made a copy of at least length two, therefore accessing the last 2 */
                    /* bytes is valid. */
                    prev_byte1 = ringbuffer[(pos - 1) & ringbuffer_mask];
                    prev_byte2 = ringbuffer[(pos - 2) & ringbuffer_mask];
                }

                /* Protect pos from overflow, wrap it around at every GB of input data */
                pos &= 0x3fffffff;
            }

            output.Write(ringbuffer, pos & ringbuffer_mask);
        }
예제 #2
0
        internal static ContextMap DecodeContextMap(int context_map_size, BitReader br)
        {
            ContextMap ctx = new ContextMap();
            bool       use_rle_for_zeros;
            int        max_run_length_prefix = 0;

            br.ReadMoreInput();
            int num_htrees = ctx.num_htrees = DecodeVarLenUint8(br) + 1;

            byte[] context_map = ctx.context_map = new byte[context_map_size];

            if (num_htrees <= 1)
            {
                return(ctx);
            }

            use_rle_for_zeros = br.ReadBits(1) == 1;

            if (use_rle_for_zeros)
            {
                max_run_length_prefix = (int)br.ReadBits(4) + 1;
            }

            HuffmanCode[] table = new HuffmanCode[HUFFMAN_MAX_TABLE_SIZE];

            for (int i = 0; i < HUFFMAN_MAX_TABLE_SIZE; i++)
            {
                table[i] = new HuffmanCode(0, 0);
            }

            ReadHuffmanCode(num_htrees + max_run_length_prefix, table, 0, br);

            for (int i = 0; i < context_map_size;)
            {
                br.ReadMoreInput();
                int code = ReadSymbol(table, 0, br);

                if (code == 0)
                {
                    context_map[i] = 0;
                    i++;
                }
                else if (code <= max_run_length_prefix)
                {
                    int reps = 1 + (1 << code) + (int)br.ReadBits(code);

                    while ((--reps) > 0)
                    {
                        if (i >= context_map_size)
                        {
                            throw new Exception("[DecodeContextMap] i >= context_map_size");
                        }

                        context_map[i] = 0;
                        i++;
                    }
                }
                else
                {
                    context_map[i] = (byte)(code - max_run_length_prefix);
                    i++;
                }
            }

            if (br.ReadBits(1) == 1)
            {
                InverseMoveToFrontTransform(context_map, context_map_size);
            }

            return(ctx);
        }