authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2025-08-07 23:40:08+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-08-07 14:40:08-07:00
log6de23100352b9c94cc8c92737687091917951df3
treebe8e7a019f147e1e41db5846c7e91380e6d0bd9b
parent8843631f7ecfe0a7756a2f1f0bc99a24b57c2fcc
signaturebadge-check Signed by PGP key B5690EEEBB952194

flate change bit reader Bits to usize (#24719)

Don't see why byte returned from specialPeek needs to be shifted by remaining_needed_bits. I believe that decision in specialPeek should be done on the number of the remaining bits not of the content of that bits. Some test result are changed, but they are now consistent with the original state as found in: https://github.com/ziglang/zig/blame/5f790464b0d5da3c4c1a7252643e7cdd4c4b605e/lib/std/compress/flate/Decompress.zig Changing Bits from usize to u32 or u64 now returns same results. * flate: simplify peekBitsEnding `peekBits` returns at most asked number of bits. Fails with EndOfStream when there are no available bits. If there are less bits available than asked still returns that available bits. Hopefully this change better reflects intention. On first input stream peek error we break the loop.

1 files changed, 12 insertions(+), 23 deletions(-)

lib/std/compress/flate/Decompress.zig+12-23
...@@ -25,8 +25,7 @@ state: State,...@@ -25,8 +25,7 @@ state: State,
2525
26err: ?Error,26err: ?Error,
2727
28/// TODO: change this to usize28const Bits = usize;
29const Bits = u64;
3029
31const BlockType = enum(u2) {30const BlockType = enum(u2) {
32 stored = 0,31 stored = 0,
...@@ -550,29 +549,19 @@ fn peekBitsEnding(d: *Decompress, comptime U: type) !U {...@@ -550,29 +549,19 @@ fn peekBitsEnding(d: *Decompress, comptime U: type) !U {
550 var u: Bits = 0;549 var u: Bits = 0;
551 var remaining_needed_bits = @bitSizeOf(U) - remaining_bits;550 var remaining_needed_bits = @bitSizeOf(U) - remaining_bits;
552 var i: usize = 0;551 var i: usize = 0;
553 while (remaining_needed_bits >= 8) {552 while (remaining_needed_bits > 0) {
554 const byte = try specialPeek(in, next_bits, i);553 const peeked = in.peek(i + 1) catch |err| switch (err) {
555 u |= @as(Bits, byte) << @intCast(i * 8);554 error.ReadFailed => return error.ReadFailed,
556 remaining_needed_bits -= 8;555 error.EndOfStream => break,
556 };
557 u |= @as(Bits, peeked[i]) << @intCast(i * 8);
558 remaining_needed_bits -|= 8;
557 i += 1;559 i += 1;
558 }560 }
559 if (remaining_needed_bits != 0) {561 if (remaining_bits == 0 and i == 0) return error.EndOfStream;
560 const byte = try specialPeek(in, next_bits, i);
561 u |= @as(Bits, byte) << @intCast((i * 8) + remaining_needed_bits);
562 }
563 return @truncate((u << remaining_bits) | next_bits);562 return @truncate((u << remaining_bits) | next_bits);
564}563}
565564
566/// If there is any unconsumed data, handles EndOfStream by pretending there
567/// are zeroes afterwards.
568fn specialPeek(in: *Reader, next_bits: Bits, i: usize) Reader.Error!u8 {
569 const peeked = in.peek(i + 1) catch |err| switch (err) {
570 error.ReadFailed => return error.ReadFailed,
571 error.EndOfStream => if (next_bits == 0 and i == 0) return error.EndOfStream else return 0,
572 };
573 return peeked[i];
574}
575
576fn tossBits(d: *Decompress, n: u4) !void {565fn tossBits(d: *Decompress, n: u4) !void {
577 const remaining_bits = d.remaining_bits;566 const remaining_bits = d.remaining_bits;
578 const next_bits = d.next_bits;567 const next_bits = d.next_bits;
...@@ -1032,7 +1021,7 @@ test "failing invalid-tree01" {...@@ -1032,7 +1021,7 @@ test "failing invalid-tree01" {
1032 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree01.input"), error.IncompleteHuffmanTree);1021 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree01.input"), error.IncompleteHuffmanTree);
1033}1022}
1034test "failing invalid-tree02" {1023test "failing invalid-tree02" {
1035 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree02.input"), error.EndOfStream);1024 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree02.input"), error.IncompleteHuffmanTree);
1036}1025}
1037test "failing invalid-tree03" {1026test "failing invalid-tree03" {
1038 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree03.input"), error.IncompleteHuffmanTree);1027 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree03.input"), error.IncompleteHuffmanTree);
...@@ -1065,7 +1054,7 @@ test "failing puff10" {...@@ -1065,7 +1054,7 @@ test "failing puff10" {
1065 try testFailure(.raw, @embedFile("testdata/fuzz/puff10.input"), error.InvalidCode);1054 try testFailure(.raw, @embedFile("testdata/fuzz/puff10.input"), error.InvalidCode);
1066}1055}
1067test "failing puff11" {1056test "failing puff11" {
1068 try testFailure(.raw, @embedFile("testdata/fuzz/puff11.input"), error.EndOfStream);1057 try testFailure(.raw, @embedFile("testdata/fuzz/puff11.input"), error.InvalidMatch);
1069}1058}
1070test "failing puff12" {1059test "failing puff12" {
1071 try testFailure(.raw, @embedFile("testdata/fuzz/puff12.input"), error.InvalidDynamicBlockHeader);1060 try testFailure(.raw, @embedFile("testdata/fuzz/puff12.input"), error.InvalidDynamicBlockHeader);
...@@ -1137,7 +1126,7 @@ test "deflate-stream" {...@@ -1137,7 +1126,7 @@ test "deflate-stream" {
1137}1126}
11381127
1139test "empty-distance-alphabet01" {1128test "empty-distance-alphabet01" {
1140 try testFailure(.raw, @embedFile("testdata/fuzz/empty-distance-alphabet01.input"), error.EndOfStream);1129 try testDecompress(.raw, @embedFile("testdata/fuzz/empty-distance-alphabet01.input"), "");
1141}1130}
11421131
1143test "empty-distance-alphabet02" {1132test "empty-distance-alphabet02" {