/* Stores code in table[0], table[step], table[2*step], ..., table[end] */ /* Assumes that end is an integer multiple of step */ public static void ReplicateValue(HuffmanCode[] table, int i, int step, int end, HuffmanCode code) { do { end -= step; table[i + end] = new HuffmanCode(code.Bits, code.Value); } while (end > 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); }
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); }
public static int ReadHuffmanCode(int alphabet_size, HuffmanCode[] tables, int table, BitReader br) { int table_size = 0; byte simple_code_or_skip; byte[] code_lengths = new byte[alphabet_size]; br.ReadMoreInput(); /* simple_code_or_skip is used as follows: * 1 for simple code; * 0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths */ simple_code_or_skip = (byte)br.ReadBits(2); if (simple_code_or_skip == 1) { /* Read symbols, codes & code lengths directly. */ int max_bits_counter = alphabet_size - 1; int max_bits = 0; int[] symbols = new int[4]; int num_symbols = (int)br.ReadBits(2) + 1; while (max_bits_counter > 0) { max_bits_counter >>= 1; max_bits++; } for (int i = 0; i < num_symbols; i++) { symbols[i] = (int)br.ReadBits(max_bits) % alphabet_size; code_lengths[symbols[i]] = 2; } code_lengths[symbols[0]] = 1; switch (num_symbols) { case 1: break; case 3: if ((symbols[0] == symbols[1]) || (symbols[0] == symbols[2]) || (symbols[1] == symbols[2])) { throw new Exception("[ReadHuffmanCode] invalid symbols"); } break; case 2: if (symbols[0] == symbols[1]) { throw new Exception("[ReadHuffmanCode] invalid symbols"); } code_lengths[symbols[1]] = 1; break; case 4: if ((symbols[0] == symbols[1]) || (symbols[0] == symbols[2]) || (symbols[0] == symbols[3]) || (symbols[1] == symbols[2]) || (symbols[1] == symbols[3]) || (symbols[2] == symbols[3])) { throw new Exception("[ReadHuffmanCode] invalid symbols"); } if (br.ReadBits(1) == 1) { code_lengths[symbols[2]] = 3; code_lengths[symbols[3]] = 3; } else { code_lengths[symbols[0]] = 2; } break; } } else /* Decode Huffman-coded code lengths. */ { byte[] code_length_code_lengths = new byte[CODE_LENGTH_CODES]; int space = 32; int num_codes = 0; for (int i = simple_code_or_skip; i < CODE_LENGTH_CODES && space > 0; i++) { int code_len_idx = kCodeLengthCodeOrder[i]; int p = 0; ushort v; br.FillBitWindow(); p += (int)(br.val_ >> br.bit_pos_) & 15; HuffmanCode huff = Huffman.CodeLengthCodeLengths[p]; br.bit_pos_ += huff.Bits; v = huff.Value; code_length_code_lengths[code_len_idx] = (byte)v; if (v != 0) { space -= (32 >> v); ++num_codes; } } if (!(num_codes == 1 || space == 0)) { throw new Exception("[ReadHuffmanCode] invalid num_codes or space"); } ReadHuffmanCodeLengths(code_length_code_lengths, alphabet_size, code_lengths, br); } table_size = Huffman.BuildHuffmanTable(tables, table, HUFFMAN_TABLE_BITS, code_lengths, alphabet_size); if (table_size == 0) { throw new Exception("[ReadHuffmanCode] Huffman.BuildHuffmanTable failed: "); } return(table_size); }
public static void ReadHuffmanCodeLengths(byte[] code_length_code_lengths, int num_symbols, byte[] code_lengths, BitReader br) { int symbol = 0; int prev_code_len = kDefaultCodeLength; int repeat = 0; int repeat_code_len = 0; int space = 32768; HuffmanCode[] table = new HuffmanCode[32]; for (int i = 0; i < 32; i++) { table[i] = new HuffmanCode(0, 0); } Huffman.BuildHuffmanTable(table, 0, 5, code_length_code_lengths, CODE_LENGTH_CODES); while (symbol < num_symbols && space > 0) { int p = 0; byte code_len; br.ReadMoreInput(); br.FillBitWindow(); p += (int)((br.val_ >> br.bit_pos_) & 31); br.bit_pos_ += table[p].Bits; code_len = (byte)table[p].Value; if (code_len < kCodeLengthRepeatCode) { repeat = 0; code_lengths[symbol++] = (byte)code_len; if (code_len != 0) { prev_code_len = code_len; space -= 32768 >> code_len; } } else { int extra_bits = code_len - 14; int old_repeat; int repeat_delta; int new_len = 0; if (code_len == kCodeLengthRepeatCode) { new_len = prev_code_len; } if (repeat_code_len != new_len) { repeat = 0; repeat_code_len = new_len; } old_repeat = repeat; if (repeat > 0) { repeat -= 2; repeat <<= extra_bits; } repeat += (int)br.ReadBits(extra_bits) + 3; repeat_delta = repeat - old_repeat; if (symbol + repeat_delta > num_symbols) { throw new Exception("[ReadHuffmanCodeLengths] symbol + repeat_delta > num_symbols"); } for (int x = 0; x < repeat_delta; x++) { code_lengths[symbol + x] = (byte)repeat_code_len; } symbol += repeat_delta; if (repeat_code_len != 0) { space -= repeat_delta << (15 - repeat_code_len); } } } if (space != 0) { throw new Exception("[ReadHuffmanCodeLengths] space = " + space); } for (; symbol < num_symbols; symbol++) { code_lengths[symbol] = 0; } }
public static int BuildHuffmanTable(HuffmanCode[] root_table, int table, int root_bits, byte[] code_lengths, int code_lengths_size) { int start_table = table; HuffmanCode code; /* current table entry */ int len; /* current code length */ int symbol; /* symbol index in original or sorted table */ int key; /* reversed prefix code */ int step; /* step size to replicate values in current table */ int low; /* low bits for current root entry */ int mask; /* mask for low bits */ int table_bits; /* key length of current table */ int table_size; /* size of current table */ int total_size; /* sum of root table size and 2nd level table sizes */ int[] sorted; /* symbols sorted by code length */ ushort[] count = new ushort[MAX_LENGTH + 1]; /* number of codes of each length */ int[] offset = new int[MAX_LENGTH + 1]; /* offsets in sorted table for each length */ sorted = new int[code_lengths_size]; /* build histogram of code lengths */ for (symbol = 0; symbol < code_lengths_size; symbol++) { count[code_lengths[symbol]]++; } /* generate offsets into sorted symbol table by code length */ offset[1] = 0; for (len = 1; len < MAX_LENGTH; len++) { offset[len + 1] = offset[len] + count[len]; } /* sort symbols by length, by symbol order within each length */ for (symbol = 0; symbol < code_lengths_size; symbol++) { if (code_lengths[symbol] != 0) { sorted[offset[code_lengths[symbol]]++] = symbol; } } table_bits = root_bits; table_size = 1 << table_bits; total_size = table_size; /* special case code with only one value */ if (offset[MAX_LENGTH] == 1) { for (key = 0; key < total_size; key++) { root_table[table + key] = new HuffmanCode(0, (ushort)sorted[0]); } return(total_size); } /* fill in root table */ key = 0; symbol = 0; for (len = 1, step = 2; len <= root_bits; ++len, step <<= 1) { for (; count[len] > 0; count[len]--) { code = new HuffmanCode((byte)len, (ushort)sorted[symbol++]); ReplicateValue(root_table, table + key, step, table_size, code); key = GetNextKey(key, len); } } /* fill in 2nd level tables and add pointers to root table */ mask = total_size - 1; low = -1; for (len = root_bits + 1, step = 2; len <= MAX_LENGTH; ++len, step <<= 1) { for (; count[len] > 0; count[len]--) { if ((key & mask) != low) { table += table_size; table_bits = NextTableBitSize(count, len, root_bits); table_size = 1 << table_bits; total_size += table_size; low = key & mask; root_table[start_table + low] = new HuffmanCode((byte)(table_bits + root_bits), (ushort)((table - start_table) - low)); } code = new HuffmanCode((byte)(len - root_bits), (ushort)sorted[symbol++]); ReplicateValue(root_table, table + (key >> root_bits), step, table_size, code); key = GetNextKey(key, len); } } return(total_size); }