| ... | ... | @@ -27,6 +27,8 @@ const FIXLCODES = 288; |
| 27 | 27 | const PREFIX_LUT_BITS = 9; |
| 28 | 28 | |
| 29 | 29 | const Huffman = struct { |
| 30 | const LUTEntry = packed struct { symbol: u16 align(4), len: u16 }; |
| 31 | |
| 30 | 32 | // Number of codes for each possible length |
| 31 | 33 | count: [MAXBITS + 1]u16, |
| 32 | 34 | // Mapping between codes and symbols |
| ... | ... | @@ -40,19 +42,23 @@ const Huffman = struct { |
| 40 | 42 | // canonical Huffman code and we have to decode it using a slower method. |
| 41 | 43 | // |
| 42 | 44 | // [1] https://github.com/madler/zlib/blob/v1.2.11/doc/algorithm.txt#L58 |
| 43 | | prefix_lut: [1 << PREFIX_LUT_BITS]u16, |
| 44 | | prefix_lut_len: [1 << PREFIX_LUT_BITS]u16, |
| 45 | prefix_lut: [1 << PREFIX_LUT_BITS]LUTEntry, |
| 45 | 46 | // The following info refer to the codes of length PREFIX_LUT_BITS+1 and are |
| 46 | 47 | // used to bootstrap the bit-by-bit reading method if the fast-path fails. |
| 47 | 48 | last_code: u16, |
| 48 | 49 | last_index: u16, |
| 49 | 50 | |
| 51 | min_code_len: u16, |
| 52 | |
| 50 | 53 | fn construct(self: *Huffman, code_length: []const u16) !void { |
| 51 | 54 | for (self.count) |*val| { |
| 52 | 55 | val.* = 0; |
| 53 | 56 | } |
| 54 | 57 | |
| 58 | self.min_code_len = math.maxInt(u16); |
| 55 | 59 | for (code_length) |len| { |
| 60 | if (len != 0 and len < self.min_code_len) |
| 61 | self.min_code_len = len; |
| 56 | 62 | self.count[len] += 1; |
| 57 | 63 | } |
| 58 | 64 | |
| ... | ... | @@ -85,39 +91,38 @@ const Huffman = struct { |
| 85 | 91 | } |
| 86 | 92 | } |
| 87 | 93 | |
| 88 | | self.prefix_lut_len = mem.zeroes(@TypeOf(self.prefix_lut_len)); |
| 94 | self.prefix_lut = mem.zeroes(@TypeOf(self.prefix_lut)); |
| 89 | 95 | |
| 90 | 96 | for (code_length) |len, symbol| { |
| 91 | 97 | if (len != 0) { |
| 92 | 98 | // Fill the symbol table. |
| 93 | 99 | // The symbols are assigned sequentially for each length. |
| 94 | 100 | self.symbol[offset[len]] = @truncate(u16, symbol); |
| 95 | | // Track the last assigned offset |
| 101 | // Track the last assigned offset. |
| 96 | 102 | offset[len] += 1; |
| 97 | 103 | } |
| 98 | 104 | |
| 99 | 105 | if (len == 0 or len > PREFIX_LUT_BITS) |
| 100 | 106 | continue; |
| 101 | 107 | |
| 102 | | // Given a Huffman code of length N we have to massage it so |
| 103 | | // that it becomes an index in the lookup table. |
| 104 | | // The bit order is reversed as the fast path reads the bit |
| 105 | | // sequence MSB to LSB using an &, the order is flipped wrt the |
| 106 | | // one obtained by reading bit-by-bit. |
| 107 | | // The codes are prefix-free, if the prefix matches we can |
| 108 | | // safely ignore the trail bits. We do so by replicating the |
| 109 | | // symbol info for each combination of the trailing bits. |
| 108 | // Given a Huffman code of length N we transform it into an index |
| 109 | // into the lookup table by reversing its bits and filling the |
| 110 | // remaining bits (PREFIX_LUT_BITS - N) with every possible |
| 111 | // combination of bits to act as a wildcard. |
| 110 | 112 | const bits_to_fill = @intCast(u5, PREFIX_LUT_BITS - len); |
| 111 | | const rev_code = bitReverse(codes[len], len); |
| 112 | | // Track the last used code, but only for lengths < PREFIX_LUT_BITS |
| 113 | const rev_code = bitReverse(u16, codes[len], len); |
| 114 | |
| 115 | // Track the last used code, but only for lengths < PREFIX_LUT_BITS. |
| 113 | 116 | codes[len] += 1; |
| 114 | 117 | |
| 115 | 118 | var j: usize = 0; |
| 116 | 119 | while (j < @as(usize, 1) << bits_to_fill) : (j += 1) { |
| 117 | 120 | const index = rev_code | (j << @intCast(u5, len)); |
| 118 | | assert(self.prefix_lut_len[index] == 0); |
| 119 | | self.prefix_lut[index] = @truncate(u16, symbol); |
| 120 | | self.prefix_lut_len[index] = @truncate(u16, len); |
| 121 | assert(self.prefix_lut[index].len == 0); |
| 122 | self.prefix_lut[index] = .{ |
| 123 | .symbol = @truncate(u16, symbol), |
| 124 | .len = @truncate(u16, len), |
| 125 | }; |
| 121 | 126 | } |
| 122 | 127 | } |
| 123 | 128 | |
| ... | ... | @@ -126,14 +131,10 @@ const Huffman = struct { |
| 126 | 131 | } |
| 127 | 132 | }; |
| 128 | 133 | |
| 129 | | // Reverse bit-by-bit a N-bit value |
| 130 | | fn bitReverse(x: usize, N: usize) usize { |
| 131 | | var tmp: usize = 0; |
| 132 | | var i: usize = 0; |
| 133 | | while (i < N) : (i += 1) { |
| 134 | | tmp |= ((x >> @intCast(u5, i)) & 1) << @intCast(u5, N - i - 1); |
| 135 | | } |
| 136 | | return tmp; |
| 134 | // Reverse bit-by-bit a N-bit code. |
| 135 | fn bitReverse(comptime T: type, value: T, N: usize) T { |
| 136 | const r = @bitReverse(T, value); |
| 137 | return r >> @intCast(math.Log2Int(T), @typeInfo(T).Int.bits - N); |
| 137 | 138 | } |
| 138 | 139 | |
| 139 | 140 | pub fn InflateStream(comptime ReaderType: type) type { |
| ... | ... | @@ -269,8 +270,8 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 269 | 270 | hdist: *Huffman, |
| 270 | 271 | hlen: *Huffman, |
| 271 | 272 | |
| 272 | | // Temporary buffer for the bitstream, only bits 0..`bits_left` are |
| 273 | | // considered valid. |
| 273 | // Temporary buffer for the bitstream. |
| 274 | // Bits 0..`bits_left` are filled with data, the remaining ones are zeros. |
| 274 | 275 | bits: u32, |
| 275 | 276 | bits_left: usize, |
| 276 | 277 | |
| ... | ... | @@ -280,7 +281,8 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 280 | 281 | self.bits |= @as(u32, byte) << @intCast(u5, self.bits_left); |
| 281 | 282 | self.bits_left += 8; |
| 282 | 283 | } |
| 283 | | return self.bits & ((@as(u32, 1) << @intCast(u5, bits)) - 1); |
| 284 | const mask = (@as(u32, 1) << @intCast(u5, bits)) - 1; |
| 285 | return self.bits & mask; |
| 284 | 286 | } |
| 285 | 287 | fn readBits(self: *Self, bits: usize) !u32 { |
| 286 | 288 | const val = self.peekBits(bits); |
| ... | ... | @@ -293,8 +295,8 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 293 | 295 | } |
| 294 | 296 | |
| 295 | 297 | fn stored(self: *Self) !void { |
| 296 | | // Discard the remaining bits, the lenght field is always |
| 297 | | // byte-aligned (and so is the data) |
| 298 | // Discard the remaining bits, the length field is always |
| 299 | // byte-aligned (and so is the data). |
| 298 | 300 | self.discardBits(self.bits_left); |
| 299 | 301 | |
| 300 | 302 | const length = try self.inner_reader.readIntLittle(u16); |
| ... | ... | @@ -481,32 +483,52 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 481 | 483 | } |
| 482 | 484 | |
| 483 | 485 | fn decode(self: *Self, h: *Huffman) !u16 { |
| 484 | | // Fast path, read some bits and hope they're prefixes of some code |
| 485 | | const prefix = try self.peekBits(PREFIX_LUT_BITS); |
| 486 | | if (h.prefix_lut_len[prefix] != 0) { |
| 487 | | self.discardBits(h.prefix_lut_len[prefix]); |
| 488 | | return h.prefix_lut[prefix]; |
| 486 | // Using u32 instead of u16 to reduce the number of casts needed. |
| 487 | var prefix: u32 = 0; |
| 488 | |
| 489 | // Fast path, read some bits and hope they're the prefix of some code. |
| 490 | // We can't read PREFIX_LUT_BITS as we don't want to read past the |
| 491 | // deflate stream end, use an incremental approach instead. |
| 492 | var code_len = h.min_code_len; |
| 493 | while (true) { |
| 494 | _ = try self.peekBits(code_len); |
| 495 | // Small optimization win, use as many bits as possible in the |
| 496 | // table lookup. |
| 497 | prefix = self.bits & ((1 << PREFIX_LUT_BITS) - 1); |
| 498 | |
| 499 | const lut_entry = &h.prefix_lut[prefix]; |
| 500 | // The code is longer than PREFIX_LUT_BITS! |
| 501 | if (lut_entry.len == 0) |
| 502 | break; |
| 503 | // If the code lenght doesn't increase we found a match. |
| 504 | if (lut_entry.len <= code_len) { |
| 505 | self.discardBits(code_len); |
| 506 | return lut_entry.symbol; |
| 507 | } |
| 508 | |
| 509 | code_len = lut_entry.len; |
| 489 | 510 | } |
| 490 | 511 | |
| 491 | 512 | // The sequence we've read is not a prefix of any code of length <= |
| 492 | | // PREFIX_LUT_BITS, keep decoding it using a slower method |
| 493 | | self.discardBits(PREFIX_LUT_BITS); |
| 513 | // PREFIX_LUT_BITS, keep decoding it using a slower method. |
| 514 | prefix = try self.readBits(PREFIX_LUT_BITS); |
| 494 | 515 | |
| 495 | 516 | // Speed up the decoding by starting from the first code length |
| 496 | | // that's not covered by the table |
| 517 | // that's not covered by the table. |
| 497 | 518 | var len: usize = PREFIX_LUT_BITS + 1; |
| 498 | 519 | var first: usize = h.last_code; |
| 499 | 520 | var index: usize = h.last_index; |
| 500 | 521 | |
| 501 | 522 | // Reverse the prefix so that the LSB becomes the MSB and make space |
| 502 | | // for the next bit |
| 503 | | var code = bitReverse(prefix, PREFIX_LUT_BITS + 1); |
| 523 | // for the next bit. |
| 524 | var code = bitReverse(u32, prefix, PREFIX_LUT_BITS + 1); |
| 504 | 525 | |
| 505 | 526 | while (len <= MAXBITS) : (len += 1) { |
| 506 | 527 | code |= try self.readBits(1); |
| 507 | 528 | const count = h.count[len]; |
| 508 | | if (code < first + count) |
| 529 | if (code < first + count) { |
| 509 | 530 | return h.symbol[index + (code - first)]; |
| 531 | } |
| 510 | 532 | index += count; |
| 511 | 533 | first += count; |
| 512 | 534 | first <<= 1; |
| ... | ... | @@ -520,7 +542,7 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 520 | 542 | while (true) { |
| 521 | 543 | switch (self.state) { |
| 522 | 544 | .DecodeBlockHeader => { |
| 523 | | // The compressed stream is done |
| 545 | // The compressed stream is done. |
| 524 | 546 | if (self.seen_eos) return; |
| 525 | 547 | |
| 526 | 548 | const last = @intCast(u1, try self.readBits(1)); |
| ... | ... | @@ -528,7 +550,7 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 528 | 550 | |
| 529 | 551 | self.seen_eos = last != 0; |
| 530 | 552 | |
| 531 | | // The next state depends on the block type |
| 553 | // The next state depends on the block type. |
| 532 | 554 | switch (kind) { |
| 533 | 555 | 0 => try self.stored(), |
| 534 | 556 | 1 => try self.fixed(), |
| ... | ... | @@ -553,7 +575,7 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 553 | 575 | var tmp: [1]u8 = undefined; |
| 554 | 576 | if ((try self.inner_reader.read(&tmp)) != 1) { |
| 555 | 577 | // Unexpected end of stream, keep this error |
| 556 | | // consistent with the use of readBitsNoEof |
| 578 | // consistent with the use of readBitsNoEof. |
| 557 | 579 | return error.EndOfStream; |
| 558 | 580 | } |
| 559 | 581 | self.window.appendUnsafe(tmp[0]); |