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); }
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); }