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) {
4444pub const Error = Container.Error || error{
4545 InvalidCode,
4646 InvalidMatch,
47 InvalidBlockType,
4847 WrongStoredBlockNlen,
4948 InvalidDynamicBlockHeader,
50 EndOfStream,
5149 ReadFailed,
5250 OversubscribedHuffmanTree,
5351 IncompleteHuffmanTree,
5452 MissingEndOfBlockCode,
53 EndOfStream,
5554};
5655
5756pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {
......@@ -153,7 +152,14 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
153152pub fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
154153 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
155154 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 },
157163 error.WriteFailed => return error.WriteFailed,
158164 else => |e| {
159165 // 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)" {
922928}
923929
924930test "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);
926932}
927933test "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);
929935}
930936test "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);
932938}
933939test "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);
935941}
936942test "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);
938944}
939945test "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);
941947}
942948test "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);
944950}
945951test "failing puff01" {
946 try testFailure(@embedFile("testdata/fuzz/puff01.input"), error.WrongStoredBlockNlen);
952 try testFailure(.raw, @embedFile("testdata/fuzz/puff01.input"), error.WrongStoredBlockNlen);
947953}
948954test "failing puff02" {
949 try testFailure(@embedFile("testdata/fuzz/puff02.input"), error.EndOfStream);
955 try testFailure(.raw, @embedFile("testdata/fuzz/puff02.input"), error.EndOfStream);
950956}
951957test "failing puff04" {
952 try testFailure(@embedFile("testdata/fuzz/puff04.input"), error.InvalidCode);
958 try testFailure(.raw, @embedFile("testdata/fuzz/puff04.input"), error.InvalidCode);
953959}
954960test "failing puff05" {
955 try testFailure(@embedFile("testdata/fuzz/puff05.input"), error.EndOfStream);
961 try testFailure(.raw, @embedFile("testdata/fuzz/puff05.input"), error.EndOfStream);
956962}
957963test "failing puff06" {
958 try testFailure(@embedFile("testdata/fuzz/puff06.input"), error.EndOfStream);
964 try testFailure(.raw, @embedFile("testdata/fuzz/puff06.input"), error.EndOfStream);
959965}
960966test "failing puff08" {
961 try testFailure(@embedFile("testdata/fuzz/puff08.input"), error.InvalidCode);
967 try testFailure(.raw, @embedFile("testdata/fuzz/puff08.input"), error.InvalidCode);
962968}
963969test "failing puff10" {
964 try testFailure(@embedFile("testdata/fuzz/puff10.input"), error.InvalidCode);
970 try testFailure(.raw, @embedFile("testdata/fuzz/puff10.input"), error.InvalidCode);
965971}
966972test "failing puff11" {
967 try testFailure(@embedFile("testdata/fuzz/puff11.input"), error.InvalidMatch);
973 try testFailure(.raw, @embedFile("testdata/fuzz/puff11.input"), error.InvalidMatch);
968974}
969975test "failing puff12" {
970 try testFailure(@embedFile("testdata/fuzz/puff12.input"), error.InvalidDynamicBlockHeader);
976 try testFailure(.raw, @embedFile("testdata/fuzz/puff12.input"), error.InvalidDynamicBlockHeader);
971977}
972978test "failing puff13" {
973 try testFailure(@embedFile("testdata/fuzz/puff13.input"), error.IncompleteHuffmanTree);
979 try testFailure(.raw, @embedFile("testdata/fuzz/puff13.input"), error.IncompleteHuffmanTree);
974980}
975981test "failing puff14" {
976 try testFailure(@embedFile("testdata/fuzz/puff14.input"), error.EndOfStream);
982 try testFailure(.raw, @embedFile("testdata/fuzz/puff14.input"), error.EndOfStream);
977983}
978984test "failing puff15" {
979 try testFailure(@embedFile("testdata/fuzz/puff15.input"), error.IncompleteHuffmanTree);
985 try testFailure(.raw, @embedFile("testdata/fuzz/puff15.input"), error.IncompleteHuffmanTree);
980986}
981987test "failing puff16" {
982 try testFailure(@embedFile("testdata/fuzz/puff16.input"), error.InvalidDynamicBlockHeader);
988 try testFailure(.raw, @embedFile("testdata/fuzz/puff16.input"), error.InvalidDynamicBlockHeader);
983989}
984990test "failing puff17" {
985 try testFailure(@embedFile("testdata/fuzz/puff17.input"), error.MissingEndOfBlockCode);
991 try testFailure(.raw, @embedFile("testdata/fuzz/puff17.input"), error.MissingEndOfBlockCode);
986992}
987993test "failing fuzz1" {
988 try testFailure(@embedFile("testdata/fuzz/fuzz1.input"), error.InvalidDynamicBlockHeader);
994 try testFailure(.raw, @embedFile("testdata/fuzz/fuzz1.input"), error.InvalidDynamicBlockHeader);
989995}
990996test "failing fuzz2" {
991 try testFailure(@embedFile("testdata/fuzz/fuzz2.input"), error.InvalidDynamicBlockHeader);
997 try testFailure(.raw, @embedFile("testdata/fuzz/fuzz2.input"), error.InvalidDynamicBlockHeader);
992998}
993999test "failing fuzz3" {
994 try testFailure(@embedFile("testdata/fuzz/fuzz3.input"), error.InvalidMatch);
1000 try testFailure(.raw, @embedFile("testdata/fuzz/fuzz3.input"), error.InvalidMatch);
9951001}
9961002test "failing fuzz4" {
997 try testFailure(@embedFile("testdata/fuzz/fuzz4.input"), error.OversubscribedHuffmanTree);
1003 try testFailure(.raw, @embedFile("testdata/fuzz/fuzz4.input"), error.OversubscribedHuffmanTree);
9981004}
9991005test "failing puff18" {
1000 try testFailure(@embedFile("testdata/fuzz/puff18.input"), error.OversubscribedHuffmanTree);
1006 try testFailure(.raw, @embedFile("testdata/fuzz/puff18.input"), error.OversubscribedHuffmanTree);
10011007}
10021008test "failing puff19" {
1003 try testFailure(@embedFile("testdata/fuzz/puff19.input"), error.OversubscribedHuffmanTree);
1009 try testFailure(.raw, @embedFile("testdata/fuzz/puff19.input"), error.OversubscribedHuffmanTree);
10041010}
10051011test "failing puff20" {
1006 try testFailure(@embedFile("testdata/fuzz/puff20.input"), error.OversubscribedHuffmanTree);
1012 try testFailure(.raw, @embedFile("testdata/fuzz/puff20.input"), error.OversubscribedHuffmanTree);
10071013}
10081014test "failing puff21" {
1009 try testFailure(@embedFile("testdata/fuzz/puff21.input"), error.OversubscribedHuffmanTree);
1015 try testFailure(.raw, @embedFile("testdata/fuzz/puff21.input"), error.OversubscribedHuffmanTree);
10101016}
10111017test "failing puff22" {
1012 try testFailure(@embedFile("testdata/fuzz/puff22.input"), error.OversubscribedHuffmanTree);
1018 try testFailure(.raw, @embedFile("testdata/fuzz/puff22.input"), error.OversubscribedHuffmanTree);
10131019}
10141020test "failing puff23" {
1015 try testFailure(@embedFile("testdata/fuzz/puff23.input"), error.OversubscribedHuffmanTree);
1021 try testFailure(.raw, @embedFile("testdata/fuzz/puff23.input"), error.OversubscribedHuffmanTree);
10161022}
10171023test "failing puff24" {
1018 try testFailure(@embedFile("testdata/fuzz/puff24.input"), error.IncompleteHuffmanTree);
1024 try testFailure(.raw, @embedFile("testdata/fuzz/puff24.input"), error.IncompleteHuffmanTree);
10191025}
10201026test "failing puff25" {
1021 try testFailure(@embedFile("testdata/fuzz/puff25.input"), error.OversubscribedHuffmanTree);
1027 try testFailure(.raw, @embedFile("testdata/fuzz/puff25.input"), error.OversubscribedHuffmanTree);
10221028}
10231029test "failing puff26" {
1024 try testFailure(@embedFile("testdata/fuzz/puff26.input"), error.InvalidDynamicBlockHeader);
1030 try testFailure(.raw, @embedFile("testdata/fuzz/puff26.input"), error.InvalidDynamicBlockHeader);
10251031}
10261032test "failing puff27" {
1027 try testFailure(@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);
1033 try testFailure(.raw, @embedFile("testdata/fuzz/puff27.input"), error.InvalidDynamicBlockHeader);
10391034}
10401035
10411036test "deflate-stream" {
......@@ -1097,82 +1092,57 @@ test "don't read past deflate stream's end" {
10971092
10981093test "zlib header" {
10991094 // Truncated header
1100 try testing.expectError(
1101 error.EndOfStream,
1102 testDecompress(.zlib, &[_]u8{0x78}, ""),
1103 );
1095 try testFailure(.zlib, &[_]u8{0x78}, error.EndOfStream);
1096
11041097 // Wrong CM
1105 try testing.expectError(
1106 error.BadZlibHeader,
1107 testDecompress(.zlib, &[_]u8{ 0x79, 0x94 }, ""),
1108 );
1098 try testFailure(.zlib, &[_]u8{ 0x79, 0x94 }, error.BadZlibHeader);
1099
11091100 // Wrong CINFO
1110 try testing.expectError(
1111 error.BadZlibHeader,
1112 testDecompress(.zlib, &[_]u8{ 0x88, 0x98 }, ""),
1113 );
1101 try testFailure(.zlib, &[_]u8{ 0x88, 0x98 }, error.BadZlibHeader);
1102
11141103 // Wrong checksum
1115 try testing.expectError(
1116 error.WrongZlibChecksum,
1117 testDecompress(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, ""),
1118 );
1104 try testFailure(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, error.WrongZlibChecksum);
1105
11191106 // Truncated checksum
1120 try testing.expectError(
1121 error.EndOfStream,
1122 testDecompress(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, ""),
1123 );
1107 try testFailure(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, error.EndOfStream);
11241108}
11251109
11261110test "gzip header" {
11271111 // Truncated header
1128 try testing.expectError(
1129 error.EndOfStream,
1130 testDecompress(.gzip, &[_]u8{ 0x1f, 0x8B }, undefined),
1131 );
1112 try testFailure(.gzip, &[_]u8{ 0x1f, 0x8B }, error.EndOfStream);
1113
11321114 // Wrong CM
1133 try testing.expectError(
1134 error.BadGzipHeader,
1135 testDecompress(.gzip, &[_]u8{
1136 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
1137 0x00, 0x03,
1138 }, undefined),
1139 );
1115 try testFailure(.gzip, &[_]u8{
1116 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
1117 0x00, 0x03,
1118 }, error.BadGzipHeader);
11401119
11411120 // Wrong checksum
1142 try testing.expectError(
1143 error.WrongGzipChecksum,
1144 testDecompress(.gzip, &[_]u8{
1145 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1146 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,
1147 0x00, 0x00, 0x00, 0x00,
1148 }, undefined),
1149 );
1121 try testFailure(.gzip, &[_]u8{
1122 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1123 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,
1124 0x00, 0x00, 0x00, 0x00,
1125 }, error.WrongGzipChecksum);
1126
11501127 // Truncated checksum
1151 try testing.expectError(
1152 error.EndOfStream,
1153 testDecompress(.gzip, &[_]u8{
1154 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1155 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
1156 }, undefined),
1157 );
1128 try testFailure(.gzip, &[_]u8{
1129 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1130 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
1131 }, error.EndOfStream);
1132
11581133 // Wrong initial size
1159 try testing.expectError(
1160 error.WrongGzipSize,
1161 testDecompress(.gzip, &[_]u8{
1162 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1163 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
1164 0x00, 0x00, 0x00, 0x01,
1165 }, undefined),
1166 );
1134 try testFailure(.gzip, &[_]u8{
1135 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1136 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
1137 0x00, 0x00, 0x00, 0x01,
1138 }, error.WrongGzipSize);
1139
11671140 // Truncated initial size field
1168 try testing.expectError(
1169 error.EndOfStream,
1170 testDecompress(.gzip, &[_]u8{
1171 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1172 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
1173 0x00, 0x00, 0x00,
1174 }, undefined),
1175 );
1141 try testFailure(.gzip, &[_]u8{
1142 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1143 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
1144 0x00, 0x00, 0x00,
1145 }, error.EndOfStream);
11761146
11771147 try testDecompress(.gzip, &[_]u8{
11781148 // GZIP header
......@@ -1184,17 +1154,6 @@ test "gzip header" {
11841154 }, "");
11851155}
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
11981157test "zlib should not overshoot" {
11991158 // Compressed zlib data with extra 4 bytes at the end.
12001159 const data = [_]u8{
......@@ -1220,3 +1179,25 @@ test "zlib should not overshoot" {
12201179 try std.testing.expectEqual(n, 4);
12211180 try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);
12221181}
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}