Commit 73e5594c78

Andrew Kelley <andrew@ziglang.org>
2025-07-28 23:36:26
std.compress.flate.Decompress: fix bit read at eof
1 parent 9c8cb77
Changed files (1)
lib
std
compress
lib/std/compress/flate/Decompress.zig
@@ -372,7 +372,10 @@ fn takeBits(d: *Decompress, comptime T: type) !T {
         };
     }
     const in = d.input;
-    const next_int = try in.takeInt(usize, .little);
+    const next_int = in.takeInt(usize, .little) catch |err| switch (err) {
+        error.ReadFailed => return error.ReadFailed,
+        error.EndOfStream => return takeBitsEnding(d, T),
+    };
     const needed_bits = @bitSizeOf(T) - remaining_bits;
     const u: U = @intCast((next_bits << needed_bits) | (next_int & ((@as(usize, 1) << needed_bits) - 1)));
     d.next_bits = next_int >> needed_bits;
@@ -384,6 +387,35 @@ fn takeBits(d: *Decompress, comptime T: type) !T {
     };
 }
 
+fn takeBitsEnding(d: *Decompress, comptime T: type) !T {
+    const remaining_bits = d.remaining_bits;
+    const next_bits = d.next_bits;
+    const in = d.input;
+    const U = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } });
+    var u: U = 0;
+    var remaining_needed_bits = @bitSizeOf(U) - remaining_bits;
+    while (@bitSizeOf(U) >= 8 and remaining_needed_bits >= 8) {
+        const byte = try in.takeByte();
+        u = (u << 8) | byte;
+        remaining_needed_bits -= 8;
+    }
+    if (remaining_needed_bits == 0) {
+        d.next_bits = 0;
+        d.remaining_bits = 0;
+    } else {
+        const byte = try in.takeByte();
+        u = @intCast((@as(usize, u) << remaining_needed_bits) | (byte & ((@as(usize, 1) << remaining_needed_bits) - 1)));
+        d.next_bits = @as(usize, byte) >> remaining_needed_bits;
+        d.remaining_bits = @intCast(8 - remaining_needed_bits);
+    }
+    u = @intCast((@as(usize, u) << remaining_bits) | next_bits);
+    return switch (@typeInfo(T)) {
+        .int => u,
+        .@"enum" => @enumFromInt(u),
+        else => @bitCast(u),
+    };
+}
+
 fn peekBits(d: *Decompress, comptime T: type) !T {
     const U = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } });
     const remaining_bits = d.remaining_bits;
@@ -397,7 +429,10 @@ fn peekBits(d: *Decompress, comptime T: type) !T {
         };
     }
     const in = d.input;
-    const next_int = try in.peekInt(usize, .little);
+    const next_int = in.peekInt(usize, .little) catch |err| switch (err) {
+        error.ReadFailed => return error.ReadFailed,
+        error.EndOfStream => return peekBitsEnding(d, T),
+    };
     const needed_bits = @bitSizeOf(T) - remaining_bits;
     const u: U = @intCast((next_bits << needed_bits) | (next_int & ((@as(usize, 1) << needed_bits) - 1)));
     return switch (@typeInfo(T)) {
@@ -407,6 +442,11 @@ fn peekBits(d: *Decompress, comptime T: type) !T {
     };
 }
 
+fn peekBitsEnding(d: *Decompress, comptime T: type) !T {
+    _ = d;
+    @panic("TODO");
+}
+
 fn tossBits(d: *Decompress, n: u6) !void {
     const remaining_bits = d.remaining_bits;
     const next_bits = d.next_bits;
@@ -415,13 +455,22 @@ fn tossBits(d: *Decompress, n: u6) !void {
         d.remaining_bits = remaining_bits - n;
     } else {
         const in = d.input;
-        const next_int = try in.takeInt(usize, .little);
+        const next_int = in.takeInt(usize, .little) catch |err| switch (err) {
+            error.ReadFailed => return error.ReadFailed,
+            error.EndOfStream => return tossBitsEnding(d, n),
+        };
         const needed_bits = n - remaining_bits;
         d.next_bits = next_int >> needed_bits;
         d.remaining_bits = @intCast(@bitSizeOf(usize) - @as(usize, needed_bits));
     }
 }
 
+fn tossBitsEnding(d: *Decompress, n: u6) !void {
+    _ = d;
+    _ = n;
+    @panic("TODO");
+}
+
 fn takeNBitsBuffered(d: *Decompress, n: u4) !u16 {
     _ = d;
     _ = n;