authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-28 18:59:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-31 22:10:11-07:00
log8ab91a6fe9d37ffc51645718a077b781be5d8873
tree7622bbd5fbbf864f378a094afe122e158bce1628
parentf644f40702fa6c6d9ec1a1d396ddd896048c966e

error.EndOfStream disambiguation


1 files changed, 103 insertions(+), 122 deletions(-)

lib/std/compress/flate/Decompress.zig+103-122
...@@ -44,14 +44,13 @@ const State = union(enum) {...@@ -44,14 +44,13 @@ const State = union(enum) {
44pub const Error = Container.Error || error{44pub const Error = Container.Error || error{
45 InvalidCode,45 InvalidCode,
46 InvalidMatch,46 InvalidMatch,
47 InvalidBlockType,
48 WrongStoredBlockNlen,47 WrongStoredBlockNlen,
49 InvalidDynamicBlockHeader,48 InvalidDynamicBlockHeader,
50 EndOfStream,
51 ReadFailed,49 ReadFailed,
52 OversubscribedHuffmanTree,50 OversubscribedHuffmanTree,
53 IncompleteHuffmanTree,51 IncompleteHuffmanTree,
54 MissingEndOfBlockCode,52 MissingEndOfBlockCode,
53 EndOfStream,
55};54};
5655
57pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {56pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {
...@@ -153,7 +152,14 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {...@@ -153,7 +152,14 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
153pub fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {152pub fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
154 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));153 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
155 return readInner(d, w, limit) catch |err| switch (err) {154 return readInner(d, w, limit) catch |err| switch (err) {
156 error.EndOfStream => return error.EndOfStream,155 error.EndOfStream => {
156 if (d.state == .end) {
157 return error.EndOfStream;
158 } else {
159 d.read_err = error.EndOfStream;
160 return error.ReadFailed;
161 }
162 },
157 error.WriteFailed => return error.WriteFailed,163 error.WriteFailed => return error.WriteFailed,
158 else => |e| {164 else => |e| {
159 // In the event of an error, state is unmodified so that it can be165 // In the event of an error, state is unmodified so that it can be
...@@ -922,120 +928,109 @@ test "zlib decompress non compressed block (type 0)" {...@@ -922,120 +928,109 @@ test "zlib decompress non compressed block (type 0)" {
922}928}
923929
924test "failing end-of-stream" {930test "failing end-of-stream" {
925 try testFailure(@embedFile("testdata/fuzz/end-of-stream.input"), error.EndOfStream);931 try testFailure(.raw, @embedFile("testdata/fuzz/end-of-stream.input"), error.EndOfStream);
926}932}
927test "failing invalid-distance" {933test "failing invalid-distance" {
928 try testFailure(@embedFile("testdata/fuzz/invalid-distance.input"), error.InvalidMatch);934 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-distance.input"), error.InvalidMatch);
929}935}
930test "failing invalid-tree01" {936test "failing invalid-tree01" {
931 try testFailure(@embedFile("testdata/fuzz/invalid-tree01.input"), error.IncompleteHuffmanTree);937 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree01.input"), error.IncompleteHuffmanTree);
932}938}
933test "failing invalid-tree02" {939test "failing invalid-tree02" {
934 try testFailure(@embedFile("testdata/fuzz/invalid-tree02.input"), error.IncompleteHuffmanTree);940 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree02.input"), error.IncompleteHuffmanTree);
935}941}
936test "failing invalid-tree03" {942test "failing invalid-tree03" {
937 try testFailure(@embedFile("testdata/fuzz/invalid-tree03.input"), error.IncompleteHuffmanTree);943 try testFailure(.raw, @embedFile("testdata/fuzz/invalid-tree03.input"), error.IncompleteHuffmanTree);
938}944}
939test "failing lengths-overflow" {945test "failing lengths-overflow" {
940 try testFailure(@embedFile("testdata/fuzz/lengths-overflow.input"), error.InvalidDynamicBlockHeader);946 try testFailure(.raw, @embedFile("testdata/fuzz/lengths-overflow.input"), error.InvalidDynamicBlockHeader);
941}947}
942test "failing out-of-codes" {948test "failing out-of-codes" {
943 try testFailure(@embedFile("testdata/fuzz/out-of-codes.input"), error.InvalidCode);949 try testFailure(.raw, @embedFile("testdata/fuzz/out-of-codes.input"), error.InvalidCode);
944}950}
945test "failing puff01" {951test "failing puff01" {
946 try testFailure(@embedFile("testdata/fuzz/puff01.input"), error.WrongStoredBlockNlen);952 try testFailure(.raw, @embedFile("testdata/fuzz/puff01.input"), error.WrongStoredBlockNlen);
947}953}
948test "failing puff02" {954test "failing puff02" {
949 try testFailure(@embedFile("testdata/fuzz/puff02.input"), error.EndOfStream);955 try testFailure(.raw, @embedFile("testdata/fuzz/puff02.input"), error.EndOfStream);
950}956}
951test "failing puff04" {957test "failing puff04" {
952 try testFailure(@embedFile("testdata/fuzz/puff04.input"), error.InvalidCode);958 try testFailure(.raw, @embedFile("testdata/fuzz/puff04.input"), error.InvalidCode);
953}959}
954test "failing puff05" {960test "failing puff05" {
955 try testFailure(@embedFile("testdata/fuzz/puff05.input"), error.EndOfStream);961 try testFailure(.raw, @embedFile("testdata/fuzz/puff05.input"), error.EndOfStream);
956}962}
957test "failing puff06" {963test "failing puff06" {
958 try testFailure(@embedFile("testdata/fuzz/puff06.input"), error.EndOfStream);964 try testFailure(.raw, @embedFile("testdata/fuzz/puff06.input"), error.EndOfStream);
959}965}
960test "failing puff08" {966test "failing puff08" {
961 try testFailure(@embedFile("testdata/fuzz/puff08.input"), error.InvalidCode);967 try testFailure(.raw, @embedFile("testdata/fuzz/puff08.input"), error.InvalidCode);
962}968}
963test "failing puff10" {969test "failing puff10" {
964 try testFailure(@embedFile("testdata/fuzz/puff10.input"), error.InvalidCode);970 try testFailure(.raw, @embedFile("testdata/fuzz/puff10.input"), error.InvalidCode);
965}971}
966test "failing puff11" {972test "failing puff11" {
967 try testFailure(@embedFile("testdata/fuzz/puff11.input"), error.InvalidMatch);973 try testFailure(.raw, @embedFile("testdata/fuzz/puff11.input"), error.InvalidMatch);
968}974}
969test "failing puff12" {975test "failing puff12" {
970 try testFailure(@embedFile("testdata/fuzz/puff12.input"), error.InvalidDynamicBlockHeader);976 try testFailure(.raw, @embedFile("testdata/fuzz/puff12.input"), error.InvalidDynamicBlockHeader);
971}977}
972test "failing puff13" {978test "failing puff13" {
973 try testFailure(@embedFile("testdata/fuzz/puff13.input"), error.IncompleteHuffmanTree);979 try testFailure(.raw, @embedFile("testdata/fuzz/puff13.input"), error.IncompleteHuffmanTree);
974}980}
975test "failing puff14" {981test "failing puff14" {
976 try testFailure(@embedFile("testdata/fuzz/puff14.input"), error.EndOfStream);982 try testFailure(.raw, @embedFile("testdata/fuzz/puff14.input"), error.EndOfStream);
977}983}
978test "failing puff15" {984test "failing puff15" {
979 try testFailure(@embedFile("testdata/fuzz/puff15.input"), error.IncompleteHuffmanTree);985 try testFailure(.raw, @embedFile("testdata/fuzz/puff15.input"), error.IncompleteHuffmanTree);
980}986}
981test "failing puff16" {987test "failing puff16" {
982 try testFailure(@embedFile("testdata/fuzz/puff16.input"), error.InvalidDynamicBlockHeader);988 try testFailure(.raw, @embedFile("testdata/fuzz/puff16.input"), error.InvalidDynamicBlockHeader);
983}989}
984test "failing puff17" {990test "failing puff17" {
985 try testFailure(@embedFile("testdata/fuzz/puff17.input"), error.MissingEndOfBlockCode);991 try testFailure(.raw, @embedFile("testdata/fuzz/puff17.input"), error.MissingEndOfBlockCode);
986}992}
987test "failing fuzz1" {993test "failing fuzz1" {
988 try testFailure(@embedFile("testdata/fuzz/fuzz1.input"), error.InvalidDynamicBlockHeader);994 try testFailure(.raw, @embedFile("testdata/fuzz/fuzz1.input"), error.InvalidDynamicBlockHeader);
989}995}
990test "failing fuzz2" {996test "failing fuzz2" {
991 try testFailure(@embedFile("testdata/fuzz/fuzz2.input"), error.InvalidDynamicBlockHeader);997 try testFailure(.raw, @embedFile("testdata/fuzz/fuzz2.input"), error.InvalidDynamicBlockHeader);
992}998}
993test "failing fuzz3" {999test "failing fuzz3" {
994 try testFailure(@embedFile("testdata/fuzz/fuzz3.input"), error.InvalidMatch);1000 try testFailure(.raw, @embedFile("testdata/fuzz/fuzz3.input"), error.InvalidMatch);
995}1001}
996test "failing fuzz4" {1002test "failing fuzz4" {
997 try testFailure(@embedFile("testdata/fuzz/fuzz4.input"), error.OversubscribedHuffmanTree);1003 try testFailure(.raw, @embedFile("testdata/fuzz/fuzz4.input"), error.OversubscribedHuffmanTree);
998}1004}
999test "failing puff18" {1005test "failing puff18" {
1000 try testFailure(@embedFile("testdata/fuzz/puff18.input"), error.OversubscribedHuffmanTree);1006 try testFailure(.raw, @embedFile("testdata/fuzz/puff18.input"), error.OversubscribedHuffmanTree);
1001}1007}
1002test "failing puff19" {1008test "failing puff19" {
1003 try testFailure(@embedFile("testdata/fuzz/puff19.input"), error.OversubscribedHuffmanTree);1009 try testFailure(.raw, @embedFile("testdata/fuzz/puff19.input"), error.OversubscribedHuffmanTree);
1004}1010}
1005test "failing puff20" {1011test "failing puff20" {
1006 try testFailure(@embedFile("testdata/fuzz/puff20.input"), error.OversubscribedHuffmanTree);1012 try testFailure(.raw, @embedFile("testdata/fuzz/puff20.input"), error.OversubscribedHuffmanTree);
1007}1013}
1008test "failing puff21" {1014test "failing puff21" {
1009 try testFailure(@embedFile("testdata/fuzz/puff21.input"), error.OversubscribedHuffmanTree);1015 try testFailure(.raw, @embedFile("testdata/fuzz/puff21.input"), error.OversubscribedHuffmanTree);
1010}1016}
1011test "failing puff22" {1017test "failing puff22" {
1012 try testFailure(@embedFile("testdata/fuzz/puff22.input"), error.OversubscribedHuffmanTree);1018 try testFailure(.raw, @embedFile("testdata/fuzz/puff22.input"), error.OversubscribedHuffmanTree);
1013}1019}
1014test "failing puff23" {1020test "failing puff23" {
1015 try testFailure(@embedFile("testdata/fuzz/puff23.input"), error.OversubscribedHuffmanTree);1021 try testFailure(.raw, @embedFile("testdata/fuzz/puff23.input"), error.OversubscribedHuffmanTree);
1016}1022}
1017test "failing puff24" {1023test "failing puff24" {
1018 try testFailure(@embedFile("testdata/fuzz/puff24.input"), error.IncompleteHuffmanTree);1024 try testFailure(.raw, @embedFile("testdata/fuzz/puff24.input"), error.IncompleteHuffmanTree);
1019}1025}
1020test "failing puff25" {1026test "failing puff25" {
1021 try testFailure(@embedFile("testdata/fuzz/puff25.input"), error.OversubscribedHuffmanTree);1027 try testFailure(.raw, @embedFile("testdata/fuzz/puff25.input"), error.OversubscribedHuffmanTree);
1022}1028}
1023test "failing puff26" {1029test "failing puff26" {
1024 try testFailure(@embedFile("testdata/fuzz/puff26.input"), error.InvalidDynamicBlockHeader);1030 try testFailure(.raw, @embedFile("testdata/fuzz/puff26.input"), error.InvalidDynamicBlockHeader);
1025}1031}
1026test "failing puff27" {1032test "failing puff27" {
1027 try testFailure(@embedFile("testdata/fuzz/puff27.input"), error.InvalidDynamicBlockHeader);1033 try testFailure(.raw, @embedFile("testdata/fuzz/puff27.input"), error.InvalidDynamicBlockHeader);
1028}
1029
1030fn testFailure(in: []const u8, expected_err: anyerror) !void {
1031 var reader: Reader = .fixed(in);
1032 var aw: Writer.Allocating = .init(testing.allocator);
1033 try aw.ensureUnusedCapacity(flate.history_len);
1034 defer aw.deinit();
1035
1036 var decompress: Decompress = .init(&reader, .raw, &.{});
1037 try testing.expectError(error.ReadFailed, decompress.reader.streamRemaining(&aw.writer));
1038 try testing.expectEqual(expected_err, decompress.read_err orelse return error.TestFailed);
1039}1034}
10401035
1041test "deflate-stream" {1036test "deflate-stream" {
...@@ -1097,82 +1092,57 @@ test "don't read past deflate stream's end" {...@@ -1097,82 +1092,57 @@ test "don't read past deflate stream's end" {
10971092
1098test "zlib header" {1093test "zlib header" {
1099 // Truncated header1094 // Truncated header
1100 try testing.expectError(1095 try testFailure(.zlib, &[_]u8{0x78}, error.EndOfStream);
1101 error.EndOfStream,1096
1102 testDecompress(.zlib, &[_]u8{0x78}, ""),
1103 );
1104 // Wrong CM1097 // Wrong CM
1105 try testing.expectError(1098 try testFailure(.zlib, &[_]u8{ 0x79, 0x94 }, error.BadZlibHeader);
1106 error.BadZlibHeader,1099
1107 testDecompress(.zlib, &[_]u8{ 0x79, 0x94 }, ""),
1108 );
1109 // Wrong CINFO1100 // Wrong CINFO
1110 try testing.expectError(1101 try testFailure(.zlib, &[_]u8{ 0x88, 0x98 }, error.BadZlibHeader);
1111 error.BadZlibHeader,1102
1112 testDecompress(.zlib, &[_]u8{ 0x88, 0x98 }, ""),
1113 );
1114 // Wrong checksum1103 // Wrong checksum
1115 try testing.expectError(1104 try testFailure(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, error.WrongZlibChecksum);
1116 error.WrongZlibChecksum,1105
1117 testDecompress(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, ""),
1118 );
1119 // Truncated checksum1106 // Truncated checksum
1120 try testing.expectError(1107 try testFailure(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, error.EndOfStream);
1121 error.EndOfStream,
1122 testDecompress(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, ""),
1123 );
1124}1108}
11251109
1126test "gzip header" {1110test "gzip header" {
1127 // Truncated header1111 // Truncated header
1128 try testing.expectError(1112 try testFailure(.gzip, &[_]u8{ 0x1f, 0x8B }, error.EndOfStream);
1129 error.EndOfStream,1113
1130 testDecompress(.gzip, &[_]u8{ 0x1f, 0x8B }, undefined),
1131 );
1132 // Wrong CM1114 // Wrong CM
1133 try testing.expectError(1115 try testFailure(.gzip, &[_]u8{
1134 error.BadGzipHeader,1116 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
1135 testDecompress(.gzip, &[_]u8{1117 0x00, 0x03,
1136 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,1118 }, error.BadGzipHeader);
1137 0x00, 0x03,
1138 }, undefined),
1139 );
11401119
1141 // Wrong checksum1120 // Wrong checksum
1142 try testing.expectError(1121 try testFailure(.gzip, &[_]u8{
1143 error.WrongGzipChecksum,1122 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1144 testDecompress(.gzip, &[_]u8{1123 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,
1145 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,1124 0x00, 0x00, 0x00, 0x00,
1146 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,1125 }, error.WrongGzipChecksum);
1147 0x00, 0x00, 0x00, 0x00,1126
1148 }, undefined),
1149 );
1150 // Truncated checksum1127 // Truncated checksum
1151 try testing.expectError(1128 try testFailure(.gzip, &[_]u8{
1152 error.EndOfStream,1129 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1153 testDecompress(.gzip, &[_]u8{1130 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
1154 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,1131 }, error.EndOfStream);
1155 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,1132
1156 }, undefined),
1157 );
1158 // Wrong initial size1133 // Wrong initial size
1159 try testing.expectError(1134 try testFailure(.gzip, &[_]u8{
1160 error.WrongGzipSize,1135 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1161 testDecompress(.gzip, &[_]u8{1136 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
1162 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,1137 0x00, 0x00, 0x00, 0x01,
1163 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,1138 }, error.WrongGzipSize);
1164 0x00, 0x00, 0x00, 0x01,1139
1165 }, undefined),
1166 );
1167 // Truncated initial size field1140 // Truncated initial size field
1168 try testing.expectError(1141 try testFailure(.gzip, &[_]u8{
1169 error.EndOfStream,1142 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1170 testDecompress(.gzip, &[_]u8{1143 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
1171 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,1144 0x00, 0x00, 0x00,
1172 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,1145 }, error.EndOfStream);
1173 0x00, 0x00, 0x00,
1174 }, undefined),
1175 );
11761146
1177 try testDecompress(.gzip, &[_]u8{1147 try testDecompress(.gzip, &[_]u8{
1178 // GZIP header1148 // GZIP header
...@@ -1184,17 +1154,6 @@ test "gzip header" {...@@ -1184,17 +1154,6 @@ test "gzip header" {
1184 }, "");1154 }, "");
1185}1155}
11861156
1187fn testDecompress(container: Container, compressed: []const u8, expected_plain: []const u8) !void {
1188 var in: std.Io.Reader = .fixed(compressed);
1189 var aw: std.Io.Writer.Allocating = .init(testing.allocator);
1190 try aw.ensureUnusedCapacity(flate.history_len);
1191 defer aw.deinit();
1192
1193 var decompress: Decompress = .init(&in, container, &.{});
1194 _ = try decompress.reader.streamRemaining(&aw.writer);
1195 try testing.expectEqualSlices(u8, expected_plain, aw.getWritten());
1196}
1197
1198test "zlib should not overshoot" {1157test "zlib should not overshoot" {
1199 // Compressed zlib data with extra 4 bytes at the end.1158 // Compressed zlib data with extra 4 bytes at the end.
1200 const data = [_]u8{1159 const data = [_]u8{
...@@ -1220,3 +1179,25 @@ test "zlib should not overshoot" {...@@ -1220,3 +1179,25 @@ test "zlib should not overshoot" {
1220 try std.testing.expectEqual(n, 4);1179 try std.testing.expectEqual(n, 4);
1221 try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);1180 try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);
1222}1181}
1182
1183fn testFailure(container: Container, in: []const u8, expected_err: anyerror) !void {
1184 var reader: Reader = .fixed(in);
1185 var aw: Writer.Allocating = .init(testing.allocator);
1186 try aw.ensureUnusedCapacity(flate.history_len);
1187 defer aw.deinit();
1188
1189 var decompress: Decompress = .init(&reader, container, &.{});
1190 try testing.expectError(error.ReadFailed, decompress.reader.streamRemaining(&aw.writer));
1191 try testing.expectEqual(expected_err, decompress.read_err orelse return error.TestFailed);
1192}
1193
1194fn testDecompress(container: Container, compressed: []const u8, expected_plain: []const u8) !void {
1195 var in: std.Io.Reader = .fixed(compressed);
1196 var aw: std.Io.Writer.Allocating = .init(testing.allocator);
1197 try aw.ensureUnusedCapacity(flate.history_len);
1198 defer aw.deinit();
1199
1200 var decompress: Decompress = .init(&in, container, &.{});
1201 _ = try decompress.reader.streamRemaining(&aw.writer);
1202 try testing.expectEqualSlices(u8, expected_plain, aw.getWritten());
1203}