authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-01-29 14:12:19+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-29 14:30:23-08:00
log4dfca01de4fda9a195048011b3339686dce4e936
treecad8ca1b8575524f097fcdd71620131661f454b8
parent27d2d8e81f03264e951ef3a7e4bb230d223f2ac2

gzip: implement compression


8 files changed, 222 insertions(+), 57 deletions(-)

lib/std/compress.zig+31-1
...@@ -21,7 +21,7 @@ pub fn HashedReader(...@@ -21,7 +21,7 @@ pub fn HashedReader(
2121
22 pub fn read(self: *@This(), buf: []u8) Error!usize {22 pub fn read(self: *@This(), buf: []u8) Error!usize {
23 const amt = try self.child_reader.read(buf);23 const amt = try self.child_reader.read(buf);
24 self.hasher.update(buf);24 self.hasher.update(buf[0..amt]);
25 return amt;25 return amt;
26 }26 }
2727
...@@ -38,6 +38,36 @@ pub fn hashedReader(...@@ -38,6 +38,36 @@ pub fn hashedReader(
38 return .{ .child_reader = reader, .hasher = hasher };38 return .{ .child_reader = reader, .hasher = hasher };
39}39}
4040
41pub fn HashedWriter(
42 comptime WriterType: anytype,
43 comptime HasherType: anytype,
44) type {
45 return struct {
46 child_writer: WriterType,
47 hasher: HasherType,
48
49 pub const Error = WriterType.Error;
50 pub const Writer = std.io.Writer(*@This(), Error, write);
51
52 pub fn write(self: *@This(), buf: []const u8) Error!usize {
53 const amt = try self.child_writer.write(buf);
54 self.hasher.update(buf[0..amt]);
55 return amt;
56 }
57
58 pub fn writer(self: *@This()) Writer {
59 return .{ .context = self };
60 }
61 };
62}
63
64pub fn hashedWriter(
65 writer: anytype,
66 hasher: anytype,
67) HashedWriter(@TypeOf(writer), @TypeOf(hasher)) {
68 return .{ .child_writer = writer, .hasher = hasher };
69}
70
41test {71test {
42 _ = deflate;72 _ = deflate;
43 _ = gzip;73 _ = gzip;
lib/std/compress/deflate/compressor.zig+3-3
...@@ -733,7 +733,7 @@ pub fn Compressor(comptime WriterType: anytype) type {...@@ -733,7 +733,7 @@ pub fn Compressor(comptime WriterType: anytype) type {
733 }733 }
734734
735 /// Writes the compressed form of `input` to the underlying writer.735 /// Writes the compressed form of `input` to the underlying writer.
736 pub fn write(self: *Self, input: []const u8) !usize {736 pub fn write(self: *Self, input: []const u8) Error!usize {
737 var buf = input;737 var buf = input;
738738
739 // writes data to hm_bw, which will eventually write the739 // writes data to hm_bw, which will eventually write the
...@@ -756,7 +756,7 @@ pub fn Compressor(comptime WriterType: anytype) type {...@@ -756,7 +756,7 @@ pub fn Compressor(comptime WriterType: anytype) type {
756 /// If the underlying writer returns an error, `flush()` returns that error.756 /// If the underlying writer returns an error, `flush()` returns that error.
757 ///757 ///
758 /// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.758 /// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
759 pub fn flush(self: *Self) !void {759 pub fn flush(self: *Self) Error!void {
760 self.sync = true;760 self.sync = true;
761 try self.step();761 try self.step();
762 try self.hm_bw.writeStoredHeader(0, false);762 try self.hm_bw.writeStoredHeader(0, false);
...@@ -956,7 +956,7 @@ pub fn Compressor(comptime WriterType: anytype) type {...@@ -956,7 +956,7 @@ pub fn Compressor(comptime WriterType: anytype) type {
956 }956 }
957957
958 /// Writes any pending data to the underlying writer.958 /// Writes any pending data to the underlying writer.
959 pub fn close(self: *Self) !void {959 pub fn close(self: *Self) Error!void {
960 self.sync = true;960 self.sync = true;
961 try self.step();961 try self.step();
962 try self.hm_bw.writeStoredHeader(0, true);962 try self.hm_bw.writeStoredHeader(0, true);
lib/std/compress/deflate/compressor_test.zig+4-4
...@@ -86,7 +86,7 @@ fn testSync(level: deflate.Compression, input: []const u8) !void {...@@ -86,7 +86,7 @@ fn testSync(level: deflate.Compression, input: []const u8) !void {
86 read = try decomp.reader().readAll(&final);86 read = try decomp.reader().readAll(&final);
87 try testing.expectEqual(@as(usize, 0), read); // expect ended stream to return 0 bytes87 try testing.expectEqual(@as(usize, 0), read); // expect ended stream to return 0 bytes
8888
89 _ = decomp.close();89 try decomp.close();
90 }90 }
91 }91 }
9292
...@@ -102,7 +102,7 @@ fn testSync(level: deflate.Compression, input: []const u8) !void {...@@ -102,7 +102,7 @@ fn testSync(level: deflate.Compression, input: []const u8) !void {
102 defer testing.allocator.free(decompressed);102 defer testing.allocator.free(decompressed);
103103
104 _ = try decomp.reader().readAll(decompressed);104 _ = try decomp.reader().readAll(decompressed);
105 _ = decomp.close();105 try decomp.close();
106106
107 try testing.expectEqualSlices(u8, input, decompressed);107 try testing.expectEqualSlices(u8, input, decompressed);
108}108}
...@@ -477,7 +477,7 @@ test "inflate reset" {...@@ -477,7 +477,7 @@ test "inflate reset" {
477 .readAllAlloc(testing.allocator, math.maxInt(usize));477 .readAllAlloc(testing.allocator, math.maxInt(usize));
478 defer testing.allocator.free(decompressed_1);478 defer testing.allocator.free(decompressed_1);
479479
480 _ = decomp.close();480 try decomp.close();
481481
482 try testing.expectEqualSlices(u8, strings[0], decompressed_0);482 try testing.expectEqualSlices(u8, strings[0], decompressed_0);
483 try testing.expectEqualSlices(u8, strings[1], decompressed_1);483 try testing.expectEqualSlices(u8, strings[1], decompressed_1);
...@@ -524,7 +524,7 @@ test "inflate reset dictionary" {...@@ -524,7 +524,7 @@ test "inflate reset dictionary" {
524 .readAllAlloc(testing.allocator, math.maxInt(usize));524 .readAllAlloc(testing.allocator, math.maxInt(usize));
525 defer testing.allocator.free(decompressed_1);525 defer testing.allocator.free(decompressed_1);
526526
527 _ = decomp.close();527 try decomp.close();
528528
529 try testing.expectEqualSlices(u8, strings[0], decompressed_0);529 try testing.expectEqualSlices(u8, strings[0], decompressed_0);
530 try testing.expectEqualSlices(u8, strings[1], decompressed_1);530 try testing.expectEqualSlices(u8, strings[1], decompressed_1);
lib/std/compress/deflate/decompressor.zig+5-8
...@@ -477,11 +477,10 @@ pub fn Decompressor(comptime ReaderType: type) type {...@@ -477,11 +477,10 @@ pub fn Decompressor(comptime ReaderType: type) type {
477 }477 }
478 }478 }
479479
480 pub fn close(self: *Self) ?Error {480 pub fn close(self: *Self) Error!void {
481 if (self.err == @as(?Error, error.EndOfStreamWithNoError)) {481 if (self.err) |err| {
482 return null;482 if (err != error.EndOfStreamWithNoError) return err;
483 }483 }
484 return self.err;
485 }484 }
486485
487 // RFC 1951 section 3.2.7.486 // RFC 1951 section 3.2.7.
...@@ -880,7 +879,7 @@ pub fn Decompressor(comptime ReaderType: type) type {...@@ -880,7 +879,7 @@ pub fn Decompressor(comptime ReaderType: type) type {
880879
881 /// Replaces the inner reader and dictionary with new_reader and new_dict.880 /// Replaces the inner reader and dictionary with new_reader and new_dict.
882 /// new_reader must be of the same type as the reader being replaced.881 /// new_reader must be of the same type as the reader being replaced.
883 pub fn reset(s: *Self, new_reader: ReaderType, new_dict: ?[]const u8) !void {882 pub fn reset(s: *Self, new_reader: ReaderType, new_dict: ?[]const u8) Error!void {
884 s.inner_reader = new_reader;883 s.inner_reader = new_reader;
885 s.step = nextBlock;884 s.step = nextBlock;
886 s.err = null;885 s.err = null;
...@@ -920,9 +919,7 @@ test "confirm decompressor resets" {...@@ -920,9 +919,7 @@ test "confirm decompressor resets" {
920 const buf = try decomp.reader().readAllAlloc(std.testing.allocator, 1024 * 100);919 const buf = try decomp.reader().readAllAlloc(std.testing.allocator, 1024 * 100);
921 defer std.testing.allocator.free(buf);920 defer std.testing.allocator.free(buf);
922921
923 if (decomp.close()) |err| {922 try decomp.close();
924 return err;
925 }
926923
927 try decomp.reset(stream.reader(), null);924 try decomp.reset(stream.reader(), null);
928 }925 }
lib/std/compress/deflate/deflate_fast_test.zig+2-2
...@@ -83,7 +83,7 @@ test "best speed" {...@@ -83,7 +83,7 @@ test "best speed" {
83 defer decomp.deinit();83 defer decomp.deinit();
8484
85 const read = try decomp.reader().readAll(decompressed);85 const read = try decomp.reader().readAll(decompressed);
86 _ = decomp.close();86 try decomp.close();
8787
88 try testing.expectEqual(want.items.len, read);88 try testing.expectEqual(want.items.len, read);
89 try testing.expectEqualSlices(u8, want.items, decompressed);89 try testing.expectEqualSlices(u8, want.items, decompressed);
...@@ -150,7 +150,7 @@ test "best speed max match offset" {...@@ -150,7 +150,7 @@ test "best speed max match offset" {
150 var decomp = try inflate.decompressor(testing.allocator, fib.reader(), null);150 var decomp = try inflate.decompressor(testing.allocator, fib.reader(), null);
151 defer decomp.deinit();151 defer decomp.deinit();
152 const read = try decomp.reader().readAll(decompressed);152 const read = try decomp.reader().readAll(decompressed);
153 _ = decomp.close();153 try decomp.close();
154154
155 try testing.expectEqual(src.len, read);155 try testing.expectEqual(src.len, read);
156 try testing.expectEqualSlices(u8, src, decompressed);156 try testing.expectEqualSlices(u8, src, decompressed);
lib/std/compress/deflate/huffman_bit_writer.zig+2-1
...@@ -124,7 +124,8 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {...@@ -124,7 +124,8 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
124 if (self.err) {124 if (self.err) {
125 return;125 return;
126 }126 }
127 self.bytes_written += try self.inner_writer.write(b);127 try self.inner_writer.writeAll(b);
128 self.bytes_written += b.len;
128 }129 }
129130
130 fn writeBits(self: *Self, b: u32, nb: u32) Error!void {131 fn writeBits(self: *Self, b: u32, nb: u32) Error!void {
lib/std/compress/gzip.zig+175-38
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1//1//
2// Decompressor for GZIP data streams (RFC1952)2// Compressor/Decompressor for GZIP data streams (RFC1952)
33
4const std = @import("../std.zig");4const std = @import("../std.zig");
5const io = std.io;5const io = std.io;
...@@ -8,6 +8,8 @@ const testing = std.testing;...@@ -8,6 +8,8 @@ const testing = std.testing;
8const mem = std.mem;8const mem = std.mem;
9const deflate = std.compress.deflate;9const deflate = std.compress.deflate;
1010
11const magic = &[2]u8{ 0x1f, 0x8b };
12
11// Flags for the FLG field in the header13// Flags for the FLG field in the header
12const FTEXT = 1 << 0;14const FTEXT = 1 << 0;
13const FHCRC = 1 << 1;15const FHCRC = 1 << 1;
...@@ -17,6 +19,14 @@ const FCOMMENT = 1 << 4;...@@ -17,6 +19,14 @@ const FCOMMENT = 1 << 4;
1719
18const max_string_len = 1024;20const max_string_len = 1024;
1921
22pub const Header = struct {
23 extra: ?[]const u8 = null,
24 filename: ?[]const u8 = null,
25 comment: ?[]const u8 = null,
26 modification_time: u32 = 0,
27 operating_system: u8 = 255,
28};
29
20pub fn Decompress(comptime ReaderType: type) type {30pub fn Decompress(comptime ReaderType: type) type {
21 return struct {31 return struct {
22 const Self = @This();32 const Self = @This();
...@@ -30,25 +40,19 @@ pub fn Decompress(comptime ReaderType: type) type {...@@ -30,25 +40,19 @@ pub fn Decompress(comptime ReaderType: type) type {
30 inflater: deflate.Decompressor(ReaderType),40 inflater: deflate.Decompressor(ReaderType),
31 in_reader: ReaderType,41 in_reader: ReaderType,
32 hasher: std.hash.Crc32,42 hasher: std.hash.Crc32,
33 read_amt: usize,43 read_amt: u32,
3444
35 info: struct {45 info: Header,
36 extra: ?[]const u8,
37 filename: ?[]const u8,
38 comment: ?[]const u8,
39 modification_time: u32,
40 operating_system: u8,
41 },
4246
43 fn init(allocator: mem.Allocator, source: ReaderType) !Self {47 fn init(allocator: mem.Allocator, in_reader: ReaderType) !Self {
44 var hasher = std.compress.hashedReader(source, std.hash.Crc32.init());48 var hasher = std.compress.hashedReader(in_reader, std.hash.Crc32.init());
45 const hashed_reader = hasher.reader();49 const hashed_reader = hasher.reader();
4650
47 // gzip header format is specified in RFC195251 // gzip header format is specified in RFC1952
48 const header = try hashed_reader.readBytesNoEof(10);52 const header = try hashed_reader.readBytesNoEof(10);
4953
50 // Check the ID1/ID2 fields54 // Check the ID1/ID2 fields
51 if (header[0] != 0x1f or header[1] != 0x8b)55 if (!std.mem.eql(u8, header[0..2], magic))
52 return error.BadHeader;56 return error.BadHeader;
5357
54 const CM = header[2];58 const CM = header[2];
...@@ -88,15 +92,15 @@ pub fn Decompress(comptime ReaderType: type) type {...@@ -88,15 +92,15 @@ pub fn Decompress(comptime ReaderType: type) type {
88 errdefer if (comment) |p| allocator.free(p);92 errdefer if (comment) |p| allocator.free(p);
8993
90 if (FLG & FHCRC != 0) {94 if (FLG & FHCRC != 0) {
91 const hash = try source.readInt(u16, .little);95 const hash = try in_reader.readInt(u16, .little);
92 if (hash != @as(u16, @truncate(hasher.hasher.final())))96 if (hash != @as(u16, @truncate(hasher.hasher.final())))
93 return error.WrongChecksum;97 return error.WrongChecksum;
94 }98 }
9599
96 return Self{100 return .{
97 .allocator = allocator,101 .allocator = allocator,
98 .inflater = try deflate.decompressor(allocator, source, null),102 .inflater = try deflate.decompressor(allocator, in_reader, null),
99 .in_reader = source,103 .in_reader = in_reader,
100 .hasher = std.hash.Crc32.init(),104 .hasher = std.hash.Crc32.init(),
101 .info = .{105 .info = .{
102 .filename = filename,106 .filename = filename,
...@@ -119,7 +123,7 @@ pub fn Decompress(comptime ReaderType: type) type {...@@ -119,7 +123,7 @@ pub fn Decompress(comptime ReaderType: type) type {
119 self.allocator.free(comment);123 self.allocator.free(comment);
120 }124 }
121125
122 // Implements the io.Reader interface126 /// Implements the io.Reader interface
123 pub fn read(self: *Self, buffer: []u8) Error!usize {127 pub fn read(self: *Self, buffer: []u8) Error!usize {
124 if (buffer.len == 0)128 if (buffer.len == 0)
125 return 0;129 return 0;
...@@ -128,10 +132,12 @@ pub fn Decompress(comptime ReaderType: type) type {...@@ -128,10 +132,12 @@ pub fn Decompress(comptime ReaderType: type) type {
128 const r = try self.inflater.read(buffer);132 const r = try self.inflater.read(buffer);
129 if (r != 0) {133 if (r != 0) {
130 self.hasher.update(buffer[0..r]);134 self.hasher.update(buffer[0..r]);
131 self.read_amt += r;135 self.read_amt +%= @truncate(r);
132 return r;136 return r;
133 }137 }
134138
139 try self.inflater.close();
140
135 // We've reached the end of stream, check if the checksum matches141 // We've reached the end of stream, check if the checksum matches
136 const hash = try self.in_reader.readInt(u32, .little);142 const hash = try self.in_reader.readInt(u32, .little);
137 if (hash != self.hasher.final())143 if (hash != self.hasher.final())
...@@ -139,7 +145,7 @@ pub fn Decompress(comptime ReaderType: type) type {...@@ -139,7 +145,7 @@ pub fn Decompress(comptime ReaderType: type) type {
139145
140 // The ISIZE field is the size of the uncompressed input modulo 2^32146 // The ISIZE field is the size of the uncompressed input modulo 2^32
141 const input_size = try self.in_reader.readInt(u32, .little);147 const input_size = try self.in_reader.readInt(u32, .little);
142 if (self.read_amt & 0xffffffff != input_size)148 if (self.read_amt != input_size)
143 return error.CorruptedData;149 return error.CorruptedData;
144150
145 return 0;151 return 0;
...@@ -155,7 +161,117 @@ pub fn decompress(allocator: mem.Allocator, reader: anytype) !Decompress(@TypeOf...@@ -155,7 +161,117 @@ pub fn decompress(allocator: mem.Allocator, reader: anytype) !Decompress(@TypeOf
155 return Decompress(@TypeOf(reader)).init(allocator, reader);161 return Decompress(@TypeOf(reader)).init(allocator, reader);
156}162}
157163
158fn testReader(data: []const u8, comptime expected: []const u8) !void {164pub const CompressOptions = struct {
165 header: Header = .{},
166 hash_header: bool = true,
167 level: deflate.Compression = .default_compression,
168};
169
170pub fn Compress(comptime WriterType: type) type {
171 return struct {
172 const Self = @This();
173
174 pub const Error = WriterType.Error ||
175 deflate.Compressor(WriterType).Error;
176 pub const Writer = io.Writer(*Self, Error, write);
177
178 allocator: mem.Allocator,
179 deflater: deflate.Compressor(WriterType),
180 out_writer: WriterType,
181 hasher: std.hash.Crc32,
182 write_amt: u32,
183
184 fn init(allocator: mem.Allocator, out_writer: WriterType, options: CompressOptions) !Self {
185 var hasher = std.compress.hashedWriter(out_writer, std.hash.Crc32.init());
186 const hashed_writer = hasher.writer();
187
188 // ID1/ID2
189 try hashed_writer.writeAll(magic);
190 // CM
191 try hashed_writer.writeByte(8);
192 // Flags
193 try hashed_writer.writeByte(
194 @as(u8, if (options.hash_header) FHCRC else 0) |
195 @as(u8, if (options.header.extra) |_| FEXTRA else 0) |
196 @as(u8, if (options.header.filename) |_| FNAME else 0) |
197 @as(u8, if (options.header.comment) |_| FCOMMENT else 0),
198 );
199 // Modification time
200 try hashed_writer.writeInt(u32, options.header.modification_time, .little);
201 // Extra flags
202 try hashed_writer.writeByte(0);
203 // Operating system
204 try hashed_writer.writeByte(options.header.operating_system);
205
206 if (options.header.extra) |extra| {
207 try hashed_writer.writeInt(u16, @intCast(extra.len), .little);
208 try hashed_writer.writeAll(extra);
209 }
210
211 if (options.header.filename) |filename| {
212 try hashed_writer.writeAll(filename);
213 try hashed_writer.writeByte(0);
214 }
215
216 if (options.header.comment) |comment| {
217 try hashed_writer.writeAll(comment);
218 try hashed_writer.writeByte(0);
219 }
220
221 if (options.hash_header) {
222 try out_writer.writeInt(
223 u16,
224 @truncate(hasher.hasher.final()),
225 .little,
226 );
227 }
228
229 return .{
230 .allocator = allocator,
231 .deflater = try deflate.compressor(allocator, out_writer, .{ .level = options.level }),
232 .out_writer = out_writer,
233 .hasher = std.hash.Crc32.init(),
234 .write_amt = 0,
235 };
236 }
237
238 pub fn deinit(self: *Self) void {
239 self.deflater.deinit();
240 }
241
242 /// Implements the io.Writer interface
243 pub fn write(self: *Self, buffer: []const u8) Error!usize {
244 if (buffer.len == 0)
245 return 0;
246
247 // Write to the compressed stream and update the computed checksum
248 const r = try self.deflater.write(buffer);
249 self.hasher.update(buffer[0..r]);
250 self.write_amt +%= @truncate(r);
251 return r;
252 }
253
254 pub fn writer(self: *Self) Writer {
255 return .{ .context = self };
256 }
257
258 pub fn flush(self: *Self) Error!void {
259 try self.deflater.flush();
260 }
261
262 pub fn close(self: *Self) Error!void {
263 try self.deflater.close();
264 try self.out_writer.writeInt(u32, self.hasher.final(), .little);
265 try self.out_writer.writeInt(u32, self.write_amt, .little);
266 }
267 };
268}
269
270pub fn compress(allocator: mem.Allocator, writer: anytype, options: CompressOptions) !Compress(@TypeOf(writer)) {
271 return Compress(@TypeOf(writer)).init(allocator, writer, options);
272}
273
274fn testReader(expected: []const u8, data: []const u8) !void {
159 var in_stream = io.fixedBufferStream(data);275 var in_stream = io.fixedBufferStream(data);
160276
161 var gzip_stream = try decompress(testing.allocator, in_stream.reader());277 var gzip_stream = try decompress(testing.allocator, in_stream.reader());
...@@ -169,70 +285,91 @@ fn testReader(data: []const u8, comptime expected: []const u8) !void {...@@ -169,70 +285,91 @@ fn testReader(data: []const u8, comptime expected: []const u8) !void {
169 try testing.expectEqualSlices(u8, expected, buf);285 try testing.expectEqualSlices(u8, expected, buf);
170}286}
171287
288fn testWriter(expected: []const u8, data: []const u8, options: CompressOptions) !void {
289 var actual = std.ArrayList(u8).init(testing.allocator);
290 defer actual.deinit();
291
292 var gzip_stream = try compress(testing.allocator, actual.writer(), options);
293 defer gzip_stream.deinit();
294
295 // Write and compress the whole file
296 try gzip_stream.writer().writeAll(data);
297 try gzip_stream.close();
298
299 // Check against the reference
300 try testing.expectEqualSlices(u8, expected, actual.items);
301}
302
172// All the test cases are obtained by compressing the RFC1952 text303// All the test cases are obtained by compressing the RFC1952 text
173//304//
174// https://tools.ietf.org/rfc/rfc1952.txt length=25037 bytes305// https://tools.ietf.org/rfc/rfc1952.txt length=25037 bytes
175// SHA256=164ef0897b4cbec63abf1b57f069f3599bd0fb7c72c2a4dee21bd7e03ec9af67306// SHA256=164ef0897b4cbec63abf1b57f069f3599bd0fb7c72c2a4dee21bd7e03ec9af67
176test "compressed data" {307test "compressed data" {
177 try testReader(308 const plain = @embedFile("testdata/rfc1952.txt");
178 @embedFile("testdata/rfc1952.txt.gz"),309 const compressed = @embedFile("testdata/rfc1952.txt.gz");
179 @embedFile("testdata/rfc1952.txt"),310 try testReader(plain, compressed);
180 );311 try testWriter(compressed, plain, .{
312 .header = .{
313 .filename = "rfc1952.txt",
314 .modification_time = 1706533053,
315 .operating_system = 3,
316 },
317 });
181}318}
182319
183test "sanity checks" {320test "sanity checks" {
184 // Truncated header321 // Truncated header
185 try testing.expectError(322 try testing.expectError(
186 error.EndOfStream,323 error.EndOfStream,
187 testReader(&[_]u8{ 0x1f, 0x8B }, ""),324 testReader(undefined, &[_]u8{ 0x1f, 0x8B }),
188 );325 );
189 // Wrong CM326 // Wrong CM
190 try testing.expectError(327 try testing.expectError(
191 error.InvalidCompression,328 error.InvalidCompression,
192 testReader(&[_]u8{329 testReader(undefined, &[_]u8{
193 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,330 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x00, 0x03,331 0x00, 0x03,
195 }, ""),332 }),
196 );333 );
197 // Wrong checksum334 // Wrong checksum
198 try testing.expectError(335 try testing.expectError(
199 error.WrongChecksum,336 error.WrongChecksum,
200 testReader(&[_]u8{337 testReader(undefined, &[_]u8{
201 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,338 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
202 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,339 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,
203 0x00, 0x00, 0x00, 0x00,340 0x00, 0x00, 0x00, 0x00,
204 }, ""),341 }),
205 );342 );
206 // Truncated checksum343 // Truncated checksum
207 try testing.expectError(344 try testing.expectError(
208 error.EndOfStream,345 error.EndOfStream,
209 testReader(&[_]u8{346 testReader(undefined, &[_]u8{
210 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,347 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,348 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
212 }, ""),349 }),
213 );350 );
214 // Wrong initial size351 // Wrong initial size
215 try testing.expectError(352 try testing.expectError(
216 error.CorruptedData,353 error.CorruptedData,
217 testReader(&[_]u8{354 testReader(undefined, &[_]u8{
218 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,355 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,356 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
220 0x00, 0x00, 0x00, 0x01,357 0x00, 0x00, 0x00, 0x01,
221 }, ""),358 }),
222 );359 );
223 // Truncated initial size field360 // Truncated initial size field
224 try testing.expectError(361 try testing.expectError(
225 error.EndOfStream,362 error.EndOfStream,
226 testReader(&[_]u8{363 testReader(undefined, &[_]u8{
227 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,364 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,365 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00,366 0x00, 0x00, 0x00,
230 }, ""),367 }),
231 );368 );
232}369}
233370
234test "header checksum" {371test "header checksum" {
235 try testReader(&[_]u8{372 try testReader("", &[_]u8{
236 // GZIP header373 // GZIP header
237 0x1f, 0x8b, 0x08, 0x12, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00,374 0x1f, 0x8b, 0x08, 0x12, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00,
238375
...@@ -241,5 +378,5 @@ test "header checksum" {...@@ -241,5 +378,5 @@ test "header checksum" {
241378
242 // GZIP data379 // GZIP data
243 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,380 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 }, "");381 });
245}382}
lib/std/compress/testdata/rfc1952.txt.gz
Binary files a/lib/std/compress/testdata/rfc1952.txt.gz and b/lib/std/compress/testdata/rfc1952.txt.gz differ