| ... | @@ -146,8 +146,8 @@ fn dynamicCodeLength(self: *Decompress, code: u16, lens: []u4, pos: usize) !usiz | ... | @@ -146,8 +146,8 @@ fn dynamicCodeLength(self: *Decompress, code: u16, lens: []u4, pos: usize) !usiz |
| 146 | // used. Shift bit reader for that much bits, those bits are used. And | 146 | // used. Shift bit reader for that much bits, those bits are used. And |
| 147 | // return symbol. | 147 | // return symbol. |
| 148 | fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol { | 148 | fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol { |
| 149 | const sym = try decoder.find(try self.peekBitsReverseBuffered(u15)); | 149 | const sym = try decoder.find(@bitReverse(try self.peekBits(u15))); |
| 150 | try self.shiftBits(sym.code_bits); | 150 | try self.tossBits(sym.code_bits); |
| 151 | return sym; | 151 | return sym; |
| 152 | } | 152 | } |
| 153 | | 153 | |
| ... | @@ -245,8 +245,8 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S | ... | @@ -245,8 +245,8 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S |
| 245 | var dec_lens: [286 + 30]u4 = @splat(0); | 245 | var dec_lens: [286 + 30]u4 = @splat(0); |
| 246 | var pos: usize = 0; | 246 | var pos: usize = 0; |
| 247 | while (pos < hlit + hdist) { | 247 | while (pos < hlit + hdist) { |
| 248 | const sym = try cl_dec.find(try d.peekBitsReverse(u7)); | 248 | const sym = try cl_dec.find(@bitReverse(try d.peekBits(u7))); |
| 249 | try d.shiftBits(sym.code_bits); | 249 | try d.tossBits(sym.code_bits); |
| 250 | pos += try d.dynamicCodeLength(sym.symbol, &dec_lens, pos); | 250 | pos += try d.dynamicCodeLength(sym.symbol, &dec_lens, pos); |
| 251 | } | 251 | } |
| 252 | if (pos > hlit + hdist) { | 252 | if (pos > hlit + hdist) { |
| ... | @@ -291,7 +291,7 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S | ... | @@ -291,7 +291,7 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S |
| 291 | // Handles fixed block non literal (length) code. | 291 | // Handles fixed block non literal (length) code. |
| 292 | // Length code is followed by 5 bits of distance code. | 292 | // Length code is followed by 5 bits of distance code. |
| 293 | const length = try d.decodeLength(@intCast(code - 257)); | 293 | const length = try d.decodeLength(@intCast(code - 257)); |
| 294 | const distance = try d.decodeDistance(try d.takeBitsReverseBuffered(u5)); | 294 | const distance = try d.decodeDistance(@bitReverse(try d.takeBits(u5))); |
| 295 | remaining = try writeMatch(w, length, distance, remaining); | 295 | remaining = try writeMatch(w, length, distance, remaining); |
| 296 | }, | 296 | }, |
| 297 | else => return error.InvalidCode, | 297 | else => return error.InvalidCode, |
| ... | @@ -384,24 +384,47 @@ fn takeBits(d: *Decompress, comptime T: type) !T { | ... | @@ -384,24 +384,47 @@ fn takeBits(d: *Decompress, comptime T: type) !T { |
| 384 | }; | 384 | }; |
| 385 | } | 385 | } |
| 386 | | 386 | |
| 387 | fn takeBitsReverseBuffered(d: *Decompress, comptime T: type) !T { | 387 | fn peekBits(d: *Decompress, comptime T: type) !T { |
| 388 | _ = d; | 388 | const U = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); |
| 389 | @panic("TODO"); | 389 | const remaining_bits = d.remaining_bits; |
| 390 | } | 390 | const next_bits = d.next_bits; |
| 391 | | 391 | if (remaining_bits >= @bitSizeOf(T)) { |
| 392 | fn takeNBitsBuffered(d: *Decompress, n: u4) !u16 { | 392 | const u: U = @truncate(next_bits); |
| 393 | _ = d; | 393 | return switch (@typeInfo(T)) { |
| 394 | _ = n; | 394 | .int => u, |
| 395 | @panic("TODO"); | 395 | .@"enum" => @enumFromInt(u), |
| | 396 | else => @bitCast(u), |
| | 397 | }; |
| | 398 | } |
| | 399 | const in = d.input; |
| | 400 | const next_int = try in.peekInt(usize, .little); |
| | 401 | const needed_bits = @bitSizeOf(T) - remaining_bits; |
| | 402 | const u: U = @intCast((next_bits << needed_bits) | (next_int & ((@as(usize, 1) << needed_bits) - 1))); |
| | 403 | return switch (@typeInfo(T)) { |
| | 404 | .int => u, |
| | 405 | .@"enum" => @enumFromInt(u), |
| | 406 | else => @bitCast(u), |
| | 407 | }; |
| 396 | } | 408 | } |
| 397 | | 409 | |
| 398 | fn peekBitsReverse(d: *Decompress, comptime T: type) !T { | 410 | fn tossBits(d: *Decompress, n: u6) !void { |
| 399 | _ = d; | 411 | const remaining_bits = d.remaining_bits; |
| 400 | @panic("TODO"); | 412 | const next_bits = d.next_bits; |
| | 413 | if (remaining_bits >= n) { |
| | 414 | d.next_bits = next_bits >> n; |
| | 415 | d.remaining_bits = remaining_bits - n; |
| | 416 | } else { |
| | 417 | const in = d.input; |
| | 418 | const next_int = try in.takeInt(usize, .little); |
| | 419 | const needed_bits = n - remaining_bits; |
| | 420 | d.next_bits = next_int >> needed_bits; |
| | 421 | d.remaining_bits = @intCast(@bitSizeOf(usize) - @as(usize, needed_bits)); |
| | 422 | } |
| 401 | } | 423 | } |
| 402 | | 424 | |
| 403 | fn peekBitsReverseBuffered(d: *Decompress, comptime T: type) !T { | 425 | fn takeNBitsBuffered(d: *Decompress, n: u4) !u16 { |
| 404 | _ = d; | 426 | _ = d; |
| | 427 | _ = n; |
| 405 | @panic("TODO"); | 428 | @panic("TODO"); |
| 406 | } | 429 | } |
| 407 | | 430 | |
| ... | @@ -422,15 +445,26 @@ fn alignBitsToByte(d: *Decompress) void { | ... | @@ -422,15 +445,26 @@ fn alignBitsToByte(d: *Decompress) void { |
| 422 | d.next_bits = 0; | 445 | d.next_bits = 0; |
| 423 | } | 446 | } |
| 424 | | 447 | |
| 425 | fn shiftBits(d: *Decompress, n: u6) !void { | 448 | /// Reads first 7 bits, and then maybe 1 or 2 more to get full 7,8 or 9 bit code. |
| 426 | _ = d; | 449 | /// ref: https://datatracker.ietf.org/doc/html/rfc1951#page-12 |
| 427 | _ = n; | 450 | /// Lit Value Bits Codes |
| 428 | @panic("TODO"); | 451 | /// --------- ---- ----- |
| 429 | } | 452 | /// 0 - 143 8 00110000 through |
| 430 | | 453 | /// 10111111 |
| | 454 | /// 144 - 255 9 110010000 through |
| | 455 | /// 111111111 |
| | 456 | /// 256 - 279 7 0000000 through |
| | 457 | /// 0010111 |
| | 458 | /// 280 - 287 8 11000000 through |
| | 459 | /// 11000111 |
| 431 | fn readFixedCode(d: *Decompress) !u16 { | 460 | fn readFixedCode(d: *Decompress) !u16 { |
| 432 | _ = d; | 461 | const code7 = @bitReverse(try d.takeBits(u7)); |
| 433 | @panic("TODO"); | 462 | return switch (code7) { |
| | 463 | 0...0b0010_111 => @as(u16, code7) + 256, |
| | 464 | 0b0010_111 + 1...0b1011_111 => (@as(u16, code7) << 1) + @as(u16, try d.takeBits(u1)) - 0b0011_0000, |
| | 465 | 0b1011_111 + 1...0b1100_011 => (@as(u16, code7 - 0b1100000) << 1) + try d.takeBits(u1) + 280, |
| | 466 | else => (@as(u16, code7 - 0b1100_100) << 2) + @as(u16, @bitReverse(try d.takeBits(u2))) + 144, |
| | 467 | }; |
| 434 | } | 468 | } |
| 435 | | 469 | |
| 436 | pub const Symbol = packed struct { | 470 | pub const Symbol = packed struct { |
| ... | @@ -731,20 +765,21 @@ test "encode/decode literals" { | ... | @@ -731,20 +765,21 @@ test "encode/decode literals" { |
| 731 | } | 765 | } |
| 732 | } | 766 | } |
| 733 | | 767 | |
| 734 | test "basic" { | 768 | test "non compressed block (type 0)" { |
| 735 | // non compressed block (type 0) | | |
| 736 | try testBasicCase(&[_]u8{ | 769 | try testBasicCase(&[_]u8{ |
| 737 | 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen | 770 | 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen |
| 738 | 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data | 771 | 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data |
| 739 | }, "Hello world\n"); | 772 | }, "Hello world\n"); |
| | 773 | } |
| 740 | | 774 | |
| 741 | // fixed code block (type 1) | 775 | test "fixed code block (type 1)" { |
| 742 | try testBasicCase(&[_]u8{ | 776 | try testBasicCase(&[_]u8{ |
| 743 | 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, // deflate data block type 1 | 777 | 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, // deflate data block type 1 |
| 744 | 0x2f, 0xca, 0x49, 0xe1, 0x02, 0x00, | 778 | 0x2f, 0xca, 0x49, 0xe1, 0x02, 0x00, |
| 745 | }, "Hello world\n"); | 779 | }, "Hello world\n"); |
| | 780 | } |
| 746 | | 781 | |
| 747 | // dynamic block (type 2) | 782 | test "dynamic block (type 2)" { |
| 748 | try testBasicCase(&[_]u8{ | 783 | try testBasicCase(&[_]u8{ |
| 749 | 0x3d, 0xc6, 0x39, 0x11, 0x00, 0x00, 0x0c, 0x02, // deflate data block type 2 | 784 | 0x3d, 0xc6, 0x39, 0x11, 0x00, 0x00, 0x0c, 0x02, // deflate data block type 2 |
| 750 | 0x30, 0x2b, 0xb5, 0x52, 0x1e, 0xff, 0x96, 0x38, | 785 | 0x30, 0x2b, 0xb5, 0x52, 0x1e, 0xff, 0x96, 0x38, |