| ... | ... | @@ -1,5 +1,4 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | | const Allocator = std.mem.Allocator; |
| 3 | 2 | const RingBuffer = std.RingBuffer; |
| 4 | 3 | |
| 5 | 4 | const types = @import("zstandard/types.zig"); |
| ... | ... | @@ -10,7 +9,9 @@ pub const decompress = @import("zstandard/decompress.zig"); |
| 10 | 9 | |
| 11 | 10 | pub const DecompressStreamOptions = struct { |
| 12 | 11 | verify_checksum: bool = true, |
| 13 | | window_size_max: usize = 1 << 23, // 8MiB default maximum window size |
| 12 | window_size_max: usize = default_window_size_max, |
| 13 | |
| 14 | pub const default_window_size_max = 1 << 23; // 8MiB default maximum window size |
| 14 | 15 | }; |
| 15 | 16 | |
| 16 | 17 | pub fn DecompressStream( |
| ... | ... | @@ -20,20 +21,29 @@ pub fn DecompressStream( |
| 20 | 21 | return struct { |
| 21 | 22 | const Self = @This(); |
| 22 | 23 | |
| 23 | | allocator: Allocator, |
| 24 | pub const window_size_max = options.window_size_max; |
| 25 | |
| 26 | const table_size_max = types.compressed_block.table_size_max; |
| 27 | |
| 24 | 28 | source: std.io.CountingReader(ReaderType), |
| 25 | 29 | state: enum { NewFrame, InFrame, LastBlock }, |
| 26 | 30 | decode_state: decompress.block.DecodeState, |
| 27 | 31 | frame_context: decompress.FrameContext, |
| 28 | | buffer: RingBuffer, |
| 29 | | literal_fse_buffer: []types.compressed_block.Table.Fse, |
| 30 | | match_fse_buffer: []types.compressed_block.Table.Fse, |
| 31 | | offset_fse_buffer: []types.compressed_block.Table.Fse, |
| 32 | | literals_buffer: []u8, |
| 33 | | sequence_buffer: []u8, |
| 32 | buffer: WindowBuffer, |
| 33 | literal_fse_buffer: [table_size_max.literal]types.compressed_block.Table.Fse, |
| 34 | match_fse_buffer: [table_size_max.match]types.compressed_block.Table.Fse, |
| 35 | offset_fse_buffer: [table_size_max.offset]types.compressed_block.Table.Fse, |
| 36 | literals_buffer: [types.block_size_max]u8, |
| 37 | sequence_buffer: [types.block_size_max]u8, |
| 34 | 38 | checksum: if (options.verify_checksum) ?u32 else void, |
| 35 | 39 | current_frame_decompressed_size: usize, |
| 36 | 40 | |
| 41 | const WindowBuffer = struct { |
| 42 | data: *[options.window_size_max]u8 = undefined, |
| 43 | read_index: usize = 0, |
| 44 | write_index: usize = 0, |
| 45 | }; |
| 46 | |
| 37 | 47 | pub const Error = ReaderType.Error || error{ |
| 38 | 48 | ChecksumFailure, |
| 39 | 49 | DictionaryIdFlagUnsupported, |
| ... | ... | @@ -44,14 +54,13 @@ pub fn DecompressStream( |
| 44 | 54 | |
| 45 | 55 | pub const Reader = std.io.Reader(*Self, Error, read); |
| 46 | 56 | |
| 47 | | pub fn init(allocator: Allocator, source: ReaderType) Self { |
| 57 | pub fn init(source: ReaderType, window_buffer: *[options.window_size_max]u8) Self { |
| 48 | 58 | return Self{ |
| 49 | | .allocator = allocator, |
| 50 | 59 | .source = std.io.countingReader(source), |
| 51 | 60 | .state = .NewFrame, |
| 52 | 61 | .decode_state = undefined, |
| 53 | 62 | .frame_context = undefined, |
| 54 | | .buffer = undefined, |
| 63 | .buffer = .{ .data = window_buffer }, |
| 55 | 64 | .literal_fse_buffer = undefined, |
| 56 | 65 | .match_fse_buffer = undefined, |
| 57 | 66 | .offset_fse_buffer = undefined, |
| ... | ... | @@ -76,44 +85,11 @@ pub fn DecompressStream( |
| 76 | 85 | options.verify_checksum, |
| 77 | 86 | ); |
| 78 | 87 | |
| 79 | | const literal_fse_buffer = try self.allocator.alloc( |
| 80 | | types.compressed_block.Table.Fse, |
| 81 | | types.compressed_block.table_size_max.literal, |
| 82 | | ); |
| 83 | | errdefer self.allocator.free(literal_fse_buffer); |
| 84 | | |
| 85 | | const match_fse_buffer = try self.allocator.alloc( |
| 86 | | types.compressed_block.Table.Fse, |
| 87 | | types.compressed_block.table_size_max.match, |
| 88 | | ); |
| 89 | | errdefer self.allocator.free(match_fse_buffer); |
| 90 | | |
| 91 | | const offset_fse_buffer = try self.allocator.alloc( |
| 92 | | types.compressed_block.Table.Fse, |
| 93 | | types.compressed_block.table_size_max.offset, |
| 94 | | ); |
| 95 | | errdefer self.allocator.free(offset_fse_buffer); |
| 96 | | |
| 97 | 88 | const decode_state = decompress.block.DecodeState.init( |
| 98 | | literal_fse_buffer, |
| 99 | | match_fse_buffer, |
| 100 | | offset_fse_buffer, |
| 89 | &self.literal_fse_buffer, |
| 90 | &self.match_fse_buffer, |
| 91 | &self.offset_fse_buffer, |
| 101 | 92 | ); |
| 102 | | const buffer = try RingBuffer.init(self.allocator, frame_context.window_size); |
| 103 | | |
| 104 | | const literals_data = try self.allocator.alloc(u8, frame_context.block_size_max); |
| 105 | | errdefer self.allocator.free(literals_data); |
| 106 | | |
| 107 | | const sequence_data = try self.allocator.alloc(u8, frame_context.block_size_max); |
| 108 | | errdefer self.allocator.free(sequence_data); |
| 109 | | |
| 110 | | self.literal_fse_buffer = literal_fse_buffer; |
| 111 | | self.match_fse_buffer = match_fse_buffer; |
| 112 | | self.offset_fse_buffer = offset_fse_buffer; |
| 113 | | self.literals_buffer = literals_data; |
| 114 | | self.sequence_buffer = sequence_data; |
| 115 | | |
| 116 | | self.buffer = buffer; |
| 117 | 93 | |
| 118 | 94 | self.decode_state = decode_state; |
| 119 | 95 | self.frame_context = frame_context; |
| ... | ... | @@ -126,16 +102,6 @@ pub fn DecompressStream( |
| 126 | 102 | } |
| 127 | 103 | } |
| 128 | 104 | |
| 129 | | pub fn deinit(self: *Self) void { |
| 130 | | if (self.state == .NewFrame) return; |
| 131 | | self.allocator.free(self.decode_state.literal_fse_buffer); |
| 132 | | self.allocator.free(self.decode_state.match_fse_buffer); |
| 133 | | self.allocator.free(self.decode_state.offset_fse_buffer); |
| 134 | | self.allocator.free(self.literals_buffer); |
| 135 | | self.allocator.free(self.sequence_buffer); |
| 136 | | self.buffer.deinit(self.allocator); |
| 137 | | } |
| 138 | | |
| 139 | 105 | pub fn reader(self: *Self) Reader { |
| 140 | 106 | return .{ .context = self }; |
| 141 | 107 | } |
| ... | ... | @@ -153,7 +119,6 @@ pub fn DecompressStream( |
| 153 | 119 | 0 |
| 154 | 120 | else |
| 155 | 121 | error.MalformedFrame, |
| 156 | | error.OutOfMemory => return error.OutOfMemory, |
| 157 | 122 | else => return error.MalformedFrame, |
| 158 | 123 | }; |
| 159 | 124 | } |
| ... | ... | @@ -165,20 +130,30 @@ pub fn DecompressStream( |
| 165 | 130 | fn readInner(self: *Self, buffer: []u8) Error!usize { |
| 166 | 131 | std.debug.assert(self.state != .NewFrame); |
| 167 | 132 | |
| 133 | var ring_buffer = RingBuffer{ |
| 134 | .data = self.buffer.data, |
| 135 | .read_index = self.buffer.read_index, |
| 136 | .write_index = self.buffer.write_index, |
| 137 | }; |
| 138 | defer { |
| 139 | self.buffer.read_index = ring_buffer.read_index; |
| 140 | self.buffer.write_index = ring_buffer.write_index; |
| 141 | } |
| 142 | |
| 168 | 143 | const source_reader = self.source.reader(); |
| 169 | | while (self.buffer.isEmpty() and self.state != .LastBlock) { |
| 144 | while (ring_buffer.isEmpty() and self.state != .LastBlock) { |
| 170 | 145 | const header_bytes = source_reader.readBytesNoEof(3) catch |
| 171 | 146 | return error.MalformedFrame; |
| 172 | 147 | const block_header = decompress.block.decodeBlockHeader(&header_bytes); |
| 173 | 148 | |
| 174 | 149 | decompress.block.decodeBlockReader( |
| 175 | | &self.buffer, |
| 150 | &ring_buffer, |
| 176 | 151 | source_reader, |
| 177 | 152 | block_header, |
| 178 | 153 | &self.decode_state, |
| 179 | 154 | self.frame_context.block_size_max, |
| 180 | | self.literals_buffer, |
| 181 | | self.sequence_buffer, |
| 155 | &self.literals_buffer, |
| 156 | &self.sequence_buffer, |
| 182 | 157 | ) catch |
| 183 | 158 | return error.MalformedBlock; |
| 184 | 159 | |
| ... | ... | @@ -186,12 +161,12 @@ pub fn DecompressStream( |
| 186 | 161 | if (self.current_frame_decompressed_size > size) return error.MalformedFrame; |
| 187 | 162 | } |
| 188 | 163 | |
| 189 | | const size = self.buffer.len(); |
| 164 | const size = ring_buffer.len(); |
| 190 | 165 | self.current_frame_decompressed_size += size; |
| 191 | 166 | |
| 192 | 167 | if (self.frame_context.hasher_opt) |*hasher| { |
| 193 | 168 | if (size > 0) { |
| 194 | | const written_slice = self.buffer.sliceLast(size); |
| 169 | const written_slice = ring_buffer.sliceLast(size); |
| 195 | 170 | hasher.update(written_slice.first); |
| 196 | 171 | hasher.update(written_slice.second); |
| 197 | 172 | } |
| ... | ... | @@ -216,18 +191,12 @@ pub fn DecompressStream( |
| 216 | 191 | } |
| 217 | 192 | } |
| 218 | 193 | |
| 219 | | const size = @min(self.buffer.len(), buffer.len); |
| 194 | const size = @min(ring_buffer.len(), buffer.len); |
| 220 | 195 | if (size > 0) { |
| 221 | | self.buffer.readFirstAssumeLength(buffer, size); |
| 196 | ring_buffer.readFirstAssumeLength(buffer, size); |
| 222 | 197 | } |
| 223 | | if (self.state == .LastBlock and self.buffer.len() == 0) { |
| 198 | if (self.state == .LastBlock and ring_buffer.len() == 0) { |
| 224 | 199 | self.state = .NewFrame; |
| 225 | | self.allocator.free(self.literal_fse_buffer); |
| 226 | | self.allocator.free(self.match_fse_buffer); |
| 227 | | self.allocator.free(self.offset_fse_buffer); |
| 228 | | self.allocator.free(self.literals_buffer); |
| 229 | | self.allocator.free(self.sequence_buffer); |
| 230 | | self.buffer.deinit(self.allocator); |
| 231 | 200 | } |
| 232 | 201 | return size; |
| 233 | 202 | } |
| ... | ... | @@ -235,24 +204,24 @@ pub fn DecompressStream( |
| 235 | 204 | } |
| 236 | 205 | |
| 237 | 206 | pub fn decompressStreamOptions( |
| 238 | | allocator: Allocator, |
| 239 | 207 | reader: anytype, |
| 240 | 208 | comptime options: DecompressStreamOptions, |
| 209 | window_buffer: *[options.window_size_max]u8, |
| 241 | 210 | ) DecompressStream(@TypeOf(reader), options) { |
| 242 | | return DecompressStream(@TypeOf(reader), options).init(allocator, reader); |
| 211 | return DecompressStream(@TypeOf(reader), options).init(reader, window_buffer); |
| 243 | 212 | } |
| 244 | 213 | |
| 245 | 214 | pub fn decompressStream( |
| 246 | | allocator: Allocator, |
| 247 | 215 | reader: anytype, |
| 216 | window_buffer: *[DecompressStreamOptions.default_window_size_max]u8, |
| 248 | 217 | ) DecompressStream(@TypeOf(reader), .{}) { |
| 249 | | return DecompressStream(@TypeOf(reader), .{}).init(allocator, reader); |
| 218 | return DecompressStream(@TypeOf(reader), .{}).init(reader, window_buffer); |
| 250 | 219 | } |
| 251 | 220 | |
| 252 | 221 | fn testDecompress(data: []const u8) ![]u8 { |
| 222 | var window_buffer: [DecompressStreamOptions.default_window_size_max]u8 = undefined; |
| 253 | 223 | var in_stream = std.io.fixedBufferStream(data); |
| 254 | | var zstd_stream = decompressStream(std.testing.allocator, in_stream.reader()); |
| 255 | | defer zstd_stream.deinit(); |
| 224 | var zstd_stream = decompressStream(in_stream.reader(), &window_buffer); |
| 256 | 225 | const result = zstd_stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); |
| 257 | 226 | return result; |
| 258 | 227 | } |
| ... | ... | @@ -301,9 +270,9 @@ fn expectEqualDecoded(expected: []const u8, input: []const u8) !void { |
| 301 | 270 | } |
| 302 | 271 | |
| 303 | 272 | { |
| 273 | var window_buffer: [DecompressStreamOptions.default_window_size_max]u8 = undefined; |
| 304 | 274 | var in_stream = std.io.fixedBufferStream(input); |
| 305 | | var stream = decompressStream(allocator, in_stream.reader()); |
| 306 | | defer stream.deinit(); |
| 275 | var stream = decompressStream(in_stream.reader(), &window_buffer); |
| 307 | 276 | |
| 308 | 277 | const result = try stream.reader().readAllAlloc(allocator, std.math.maxInt(usize)); |
| 309 | 278 | defer allocator.free(result); |