authorgravatar for dante.catalfamo@gmail.comdantecatalfamo <dante.catalfamo@gmail.com> 2022-12-16 13:14:12-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-17 14:07:45-07:00
log8b923543969c8eceb90f493875b4bb8540f54a71
tree4e67c29f54f55bc845a65daaa7b195c785a84607
parent9370fb8b81f69fb28311c00f0833883acfa93521

std: add zlib stream writer


1 files changed, 106 insertions(+), 5 deletions(-)

lib/std/compress/zlib.zig+106-5
......@@ -1,5 +1,5 @@
11//
2// Decompressor for ZLIB data streams (RFC1950)
2// Compressor/Decompressor for ZLIB data streams (RFC1950)
33
44const std = @import("std");
55const io = std.io;
......@@ -8,7 +8,7 @@ const testing = std.testing;
88const mem = std.mem;
99const deflate = std.compress.deflate;
1010
11pub fn ZlibStream(comptime ReaderType: type) type {
11pub fn ZlibStreamReader(comptime ReaderType: type) type {
1212 return struct {
1313 const Self = @This();
1414
......@@ -84,14 +84,99 @@ pub fn ZlibStream(comptime ReaderType: type) type {
8484 };
8585}
8686
87pub fn zlibStream(allocator: mem.Allocator, reader: anytype) !ZlibStream(@TypeOf(reader)) {
88 return ZlibStream(@TypeOf(reader)).init(allocator, reader);
87pub fn zlibStreamReader(allocator: mem.Allocator, reader: anytype) !ZlibStreamReader(@TypeOf(reader)) {
88 return ZlibStreamReader(@TypeOf(reader)).init(allocator, reader);
8989}
9090
91pub const CompressionLevel = enum(u2) {
92 no_compression = 0,
93 fastest = 1,
94 default = 2,
95 maximum = 3,
96};
97
98pub const CompressionOptions = struct {
99 level: CompressionLevel = .default,
100};
101
102pub fn ZlibStreamWriter(comptime WriterType: type) type {
103 return struct {
104 const Self = @This();
105
106 const Error = WriterType.Error ||
107 deflate.Compressor(WriterType).Error;
108 pub const Writer = io.Writer(*Self, Error, write);
109
110 allocator: mem.Allocator,
111 deflator: deflate.Compressor(WriterType),
112 in_writer: WriterType,
113 hasher: std.hash.Adler32,
114
115 fn init(allocator: mem.Allocator, dest: WriterType, options: CompressionOptions) !Self {
116 // Zlib header format is specified in RFC1950
117 const CM: u4 = 8; // DEFLATE
118 const CINFO: u4 = 7; // 32K window
119 const CMF: u8 = (@as(u8, CINFO) << 4) | CM;
120
121 const FLEVEL: u2 = @enumToInt(options.level);
122 const FDICT: u1 = 0; // No preset dictionary support
123 const FLG_temp = (@as(u8, FLEVEL) << 6) | (@as(u8, FDICT) << 5);
124 const FCHECK: u5 = 31 - ((@as(u16, CMF) * 256 + FLG_temp) % 31);
125 const FLG = FLG_temp | FCHECK;
126
127 const compression_level: deflate.Compression = switch (options.level) {
128 .no_compression => .no_compression,
129 .fastest => .best_speed,
130 .default => .default_compression,
131 .maximum => .best_compression,
132 };
133
134 try dest.writeAll(&.{ CMF, FLG });
135
136 return Self{
137 .allocator = allocator,
138 .deflator = try deflate.compressor(allocator, dest, .{ .level = compression_level }),
139 .in_writer = dest,
140 .hasher = std.hash.Adler32.init(),
141 };
142 }
143
144 pub fn write(self: *Self, bytes: []const u8) Error!usize {
145 if (bytes.len == 0) {
146 return 0;
147 }
148
149 const w = try self.deflator.write(bytes);
150
151 self.hasher.update(bytes[0..w]);
152 return w;
153 }
154
155 pub fn writer(self: *Self) Writer {
156 return .{ .context = self };
157 }
158
159 pub fn deinit(self: *Self) void {
160 self.deflator.deinit();
161 }
162
163 pub fn close(self: *Self) !void {
164 const hash = self.hasher.final();
165 try self.deflator.close();
166 try self.in_writer.writeIntBig(u32, hash);
167 }
168 };
169}
170
171pub fn zlibStreamWriter(allocator: mem.Allocator, writer: anytype, options: CompressionOptions) !ZlibStreamWriter(@TypeOf(writer)) {
172 return ZlibStreamWriter(@TypeOf(writer)).init(allocator, writer, options);
173}
174
175
91176fn testReader(data: []const u8, expected: []const u8) !void {
92177 var in_stream = io.fixedBufferStream(data);
93178
94 var zlib_stream = try zlibStream(testing.allocator, in_stream.reader());
179 var zlib_stream = try zlibStreamReader(testing.allocator, in_stream.reader());
95180 defer zlib_stream.deinit();
96181
97182 // Read and decompress the whole file
......@@ -170,3 +255,19 @@ test "sanity checks" {
170255 testReader(&[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, ""),
171256 );
172257}
258
259test "compress data" {
260 const allocator = testing.allocator;
261 const rfc1951_txt = @embedFile("testdata/rfc1951.txt");
262
263 var compressed_data = std.ArrayList(u8).init(allocator);
264 defer compressed_data.deinit();
265
266 var compressor = try zlibStreamWriter(allocator, compressed_data.writer(), .{});
267 defer compressor.deinit();
268
269 try compressor.writer().writeAll(rfc1951_txt);
270 try compressor.close();
271
272 try testReader(compressed_data.items, rfc1951_txt);
273}