| ... | ... | @@ -9,20 +9,14 @@ pub const decompress = @import("zstandard/decompress.zig"); |
| 9 | 9 | |
| 10 | 10 | pub const DecompressStreamOptions = struct { |
| 11 | 11 | verify_checksum: bool = true, |
| 12 | | window_size_max: usize = default_window_size_max, |
| 13 | | |
| 14 | | pub const default_window_size_max = 1 << 23; // 8MiB default maximum window size |
| 15 | 12 | }; |
| 16 | 13 | |
| 17 | 14 | pub fn DecompressStream( |
| 18 | 15 | comptime ReaderType: type, |
| 19 | | comptime options: DecompressStreamOptions, |
| 20 | 16 | ) type { |
| 21 | 17 | return struct { |
| 22 | 18 | const Self = @This(); |
| 23 | 19 | |
| 24 | | pub const window_size_max = options.window_size_max; |
| 25 | | |
| 26 | 20 | const table_size_max = types.compressed_block.table_size_max; |
| 27 | 21 | |
| 28 | 22 | source: std.io.CountingReader(ReaderType), |
| ... | ... | @@ -35,11 +29,12 @@ pub fn DecompressStream( |
| 35 | 29 | offset_fse_buffer: [table_size_max.offset]types.compressed_block.Table.Fse, |
| 36 | 30 | literals_buffer: [types.block_size_max]u8, |
| 37 | 31 | sequence_buffer: [types.block_size_max]u8, |
| 38 | | checksum: if (options.verify_checksum) ?u32 else void, |
| 32 | verify_checksum: bool, |
| 33 | checksum: ?u32, |
| 39 | 34 | current_frame_decompressed_size: usize, |
| 40 | 35 | |
| 41 | 36 | const WindowBuffer = struct { |
| 42 | | data: *[options.window_size_max]u8 = undefined, |
| 37 | data: []u8 = undefined, |
| 43 | 38 | read_index: usize = 0, |
| 44 | 39 | write_index: usize = 0, |
| 45 | 40 | }; |
| ... | ... | @@ -54,7 +49,7 @@ pub fn DecompressStream( |
| 54 | 49 | |
| 55 | 50 | pub const Reader = std.io.Reader(*Self, Error, read); |
| 56 | 51 | |
| 57 | | pub fn init(source: ReaderType, window_buffer: *[options.window_size_max]u8) Self { |
| 52 | pub fn init(source: ReaderType, window_buffer: []u8, options: DecompressStreamOptions) Self { |
| 58 | 53 | return Self{ |
| 59 | 54 | .source = std.io.countingReader(source), |
| 60 | 55 | .state = .NewFrame, |
| ... | ... | @@ -66,6 +61,7 @@ pub fn DecompressStream( |
| 66 | 61 | .offset_fse_buffer = undefined, |
| 67 | 62 | .literals_buffer = undefined, |
| 68 | 63 | .sequence_buffer = undefined, |
| 64 | .verify_checksum = options.verify_checksum, |
| 69 | 65 | .checksum = undefined, |
| 70 | 66 | .current_frame_decompressed_size = undefined, |
| 71 | 67 | }; |
| ... | ... | @@ -81,8 +77,8 @@ pub fn DecompressStream( |
| 81 | 77 | .zstandard => |header| { |
| 82 | 78 | const frame_context = try decompress.FrameContext.init( |
| 83 | 79 | header, |
| 84 | | options.window_size_max, |
| 85 | | options.verify_checksum, |
| 80 | self.buffer.data.len, |
| 81 | self.verify_checksum, |
| 86 | 82 | ); |
| 87 | 83 | |
| 88 | 84 | const decode_state = decompress.block.DecodeState.init( |
| ... | ... | @@ -94,7 +90,7 @@ pub fn DecompressStream( |
| 94 | 90 | self.decode_state = decode_state; |
| 95 | 91 | self.frame_context = frame_context; |
| 96 | 92 | |
| 97 | | self.checksum = if (options.verify_checksum) null else {}; |
| 93 | self.checksum = null; |
| 98 | 94 | self.current_frame_decompressed_size = 0; |
| 99 | 95 | |
| 100 | 96 | self.state = .InFrame; |
| ... | ... | @@ -176,7 +172,7 @@ pub fn DecompressStream( |
| 176 | 172 | if (self.frame_context.has_checksum) { |
| 177 | 173 | const checksum = source_reader.readInt(u32, .little) catch |
| 178 | 174 | return error.MalformedFrame; |
| 179 | | if (comptime options.verify_checksum) { |
| 175 | if (self.verify_checksum) { |
| 180 | 176 | if (self.frame_context.hasher_opt) |*hasher| { |
| 181 | 177 | if (checksum != decompress.computeChecksum(hasher)) |
| 182 | 178 | return error.ChecksumFailure; |
| ... | ... | @@ -213,15 +209,18 @@ pub fn decompressStreamOptions( |
| 213 | 209 | |
| 214 | 210 | pub fn decompressStream( |
| 215 | 211 | reader: anytype, |
| 216 | | window_buffer: *[DecompressStreamOptions.default_window_size_max]u8, |
| 217 | | ) DecompressStream(@TypeOf(reader), .{}) { |
| 218 | | return DecompressStream(@TypeOf(reader), .{}).init(reader, window_buffer); |
| 212 | window_buffer: []u8, |
| 213 | options: DecompressStreamOptions, |
| 214 | ) DecompressStream(@TypeOf(reader)) { |
| 215 | return DecompressStream(@TypeOf(reader)).init(reader, window_buffer, options); |
| 219 | 216 | } |
| 220 | 217 | |
| 221 | 218 | fn testDecompress(data: []const u8) ![]u8 { |
| 222 | | var window_buffer: [DecompressStreamOptions.default_window_size_max]u8 = undefined; |
| 219 | const window_buffer = try std.testing.allocator.alloc(u8, 1 << 23); |
| 220 | defer std.testing.allocator.free(window_buffer); |
| 221 | |
| 223 | 222 | var in_stream = std.io.fixedBufferStream(data); |
| 224 | | var zstd_stream = decompressStream(in_stream.reader(), &window_buffer); |
| 223 | var zstd_stream = decompressStream(in_stream.reader(), window_buffer, .{}); |
| 225 | 224 | const result = zstd_stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); |
| 226 | 225 | return result; |
| 227 | 226 | } |
| ... | ... | @@ -251,7 +250,7 @@ test "zstandard decompression" { |
| 251 | 250 | |
| 252 | 251 | test "zstandard streaming decompression" { |
| 253 | 252 | // default stack size for wasm32 is too low for DecompressStream - slightly |
| 254 | | // over 9MiB stack space is needed via the --stack CLI flag |
| 253 | // over 1MiB stack space is needed via the --stack CLI flag |
| 255 | 254 | if (@import("builtin").target.cpu.arch == .wasm32) return error.SkipZigTest; |
| 256 | 255 | |
| 257 | 256 | const uncompressed = @embedFile("testdata/rfc8478.txt"); |
| ... | ... | @@ -279,9 +278,11 @@ fn expectEqualDecoded(expected: []const u8, input: []const u8) !void { |
| 279 | 278 | } |
| 280 | 279 | |
| 281 | 280 | fn expectEqualDecodedStreaming(expected: []const u8, input: []const u8) !void { |
| 282 | | var window_buffer: [DecompressStreamOptions.default_window_size_max]u8 = undefined; |
| 281 | const window_buffer = try std.testing.allocator.alloc(u8, 1 << 23); |
| 282 | defer std.testing.allocator.free(window_buffer); |
| 283 | |
| 283 | 284 | var in_stream = std.io.fixedBufferStream(input); |
| 284 | | var stream = decompressStream(in_stream.reader(), &window_buffer); |
| 285 | var stream = decompressStream(in_stream.reader(), window_buffer, .{}); |
| 285 | 286 | |
| 286 | 287 | const result = try stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); |
| 287 | 288 | defer std.testing.allocator.free(result); |
| ... | ... | @@ -307,7 +308,7 @@ test "zero sized block" { |
| 307 | 308 | |
| 308 | 309 | test "zero sized block streaming" { |
| 309 | 310 | // default stack size for wasm32 is too low for DecompressStream - slightly |
| 310 | | // over 9MiB stack space is needed via the --stack CLI flag |
| 311 | // over 1MiB stack space is needed via the --stack CLI flag |
| 311 | 312 | if (@import("builtin").target.cpu.arch == .wasm32) return error.SkipZigTest; |
| 312 | 313 | |
| 313 | 314 | const input_raw = |