authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-25 20:24:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-26 21:00:58-07:00
loga8ae6c2f4265a66c7a63d788a13549c48a1dd8c0
treed1e1dd03fef4ee98529511926e7617676b2f8505
parent3cb9baaf65abe6c658a5b8b4bd5c0a424ec923c1

std.compress.lzma2: tests passing


2 files changed, 131 insertions(+), 151 deletions(-)

lib/std/compress/lzma.zig+59-67
...@@ -105,7 +105,6 @@ pub const RangeDecoder = struct {...@@ -105,7 +105,6 @@ pub const RangeDecoder = struct {
105105
106pub const Decode = struct {106pub const Decode = struct {
107 properties: Properties,107 properties: Properties,
108 unpacked_size: ?u64,
109 literal_probs: Vec2d,108 literal_probs: Vec2d,
110 pos_slot_decoder: [4]BitTree(6),109 pos_slot_decoder: [4]BitTree(6),
111 align_decoder: BitTree(4),110 align_decoder: BitTree(4),
...@@ -121,15 +120,10 @@ pub const Decode = struct {...@@ -121,15 +120,10 @@ pub const Decode = struct {
121 len_decoder: LenDecoder,120 len_decoder: LenDecoder,
122 rep_len_decoder: LenDecoder,121 rep_len_decoder: LenDecoder,
123122
124 pub fn init(123 pub fn init(gpa: Allocator, properties: Properties) !Decode {
125 gpa: Allocator,
126 properties: Properties,
127 unpacked_size: ?u64,
128 ) !Decode {
129 return .{124 return .{
130 .properties = properties,125 .properties = properties,
131 .unpacked_size = unpacked_size,126 .literal_probs = try Vec2d.init(gpa, 0x400, @as(usize, 1) << (properties.lc + properties.lp), 0x300),
132 .literal_probs = try Vec2d.init(gpa, 0x400, .{ @as(usize, 1) << (properties.lc + properties.lp), 0x300 }),
133 .pos_slot_decoder = @splat(.{}),127 .pos_slot_decoder = @splat(.{}),
134 .align_decoder = .{},128 .align_decoder = .{},
135 .pos_decoders = @splat(0x400),129 .pos_decoders = @splat(0x400),
...@@ -157,7 +151,7 @@ pub const Decode = struct {...@@ -157,7 +151,7 @@ pub const Decode = struct {
157 self.literal_probs.fill(0x400);151 self.literal_probs.fill(0x400);
158 } else {152 } else {
159 self.literal_probs.deinit(gpa);153 self.literal_probs.deinit(gpa);
160 self.literal_probs = try Vec2d.init(gpa, 0x400, .{ @as(usize, 1) << (new_props.lc + new_props.lp), 0x300 });154 self.literal_probs = try Vec2d.init(gpa, 0x400, @as(usize, 1) << (new_props.lc + new_props.lp), 0x300);
161 }155 }
162156
163 self.properties = new_props;157 self.properties = new_props;
...@@ -176,11 +170,12 @@ pub const Decode = struct {...@@ -176,11 +170,12 @@ pub const Decode = struct {
176 self.rep_len_decoder.reset();170 self.rep_len_decoder.reset();
177 }171 }
178172
179 fn processNext(173 pub fn process(
180 self: *Decode,174 self: *Decode,
181 reader: *Reader,175 reader: *Reader,
182 allocating: *Writer.Allocating,176 allocating: *Writer.Allocating,
183 buffer: *CircularBuffer,177 /// `CircularBuffer` or `std.compress.lzma2.AccumBuffer`.
178 buffer: anytype,
184 decoder: *RangeDecoder,179 decoder: *RangeDecoder,
185 ) !ProcessingStatus {180 ) !ProcessingStatus {
186 const gpa = allocating.allocator;181 const gpa = allocating.allocator;
...@@ -256,39 +251,11 @@ pub const Decode = struct {...@@ -256,39 +251,11 @@ pub const Decode = struct {
256 return .more;251 return .more;
257 }252 }
258253
259 pub fn process(
260 self: *Decode,
261 reader: *Reader,
262 allocating: *Writer.Allocating,
263 buffer: *CircularBuffer,
264 decoder: *RangeDecoder,
265 ) !void {
266 process_next: {
267 if (self.unpacked_size) |unpacked_size| {
268 if (buffer.len >= unpacked_size) {
269 break :process_next;
270 }
271 } else if (decoder.isFinished()) {
272 break :process_next;
273 }
274 switch (try self.processNext(reader, allocating, buffer, decoder)) {
275 .more => return,
276 .finished => {},
277 }
278 }
279
280 if (self.unpacked_size) |unpacked_size| {
281 if (buffer.len != unpacked_size) return error.DecompressedSizeMismatch;
282 }
283
284 try buffer.finish(&allocating.writer);
285 self.state = math.maxInt(usize);
286 }
287
288 fn decodeLiteral(254 fn decodeLiteral(
289 self: *Decode,255 self: *Decode,
290 reader: *Reader,256 reader: *Reader,
291 buffer: *CircularBuffer,257 /// `CircularBuffer` or `std.compress.lzma2.AccumBuffer`.
258 buffer: anytype,
292 decoder: *RangeDecoder,259 decoder: *RangeDecoder,
293 ) !u8 {260 ) !u8 {
294 const def_prev_byte = 0;261 const def_prev_byte = 0;
...@@ -377,10 +344,7 @@ pub const Decode = struct {...@@ -377,10 +344,7 @@ pub const Decode = struct {
377 }344 }
378345
379 pub fn get(self: CircularBuffer, index: usize) u8 {346 pub fn get(self: CircularBuffer, index: usize) u8 {
380 return if (0 <= index and index < self.buf.items.len)347 return if (0 <= index and index < self.buf.items.len) self.buf.items[index] else 0;
381 self.buf.items[index]
382 else
383 0;
384 }348 }
385349
386 pub fn set(self: *CircularBuffer, gpa: Allocator, index: usize, value: u8) !void {350 pub fn set(self: *CircularBuffer, gpa: Allocator, index: usize, value: u8) !void {
...@@ -524,29 +488,29 @@ pub const Decode = struct {...@@ -524,29 +488,29 @@ pub const Decode = struct {
524 data: []u16,488 data: []u16,
525 cols: usize,489 cols: usize,
526490
527 pub fn init(gpa: Allocator, value: u16, size: struct { usize, usize }) !Vec2d {491 pub fn init(gpa: Allocator, value: u16, w: usize, h: usize) !Vec2d {
528 const len = try math.mul(usize, size[0], size[1]);492 const len = try math.mul(usize, w, h);
529 const data = try gpa.alloc(u16, len);493 const data = try gpa.alloc(u16, len);
530 @memset(data, value);494 @memset(data, value);
531 return .{495 return .{
532 .data = data,496 .data = data,
533 .cols = size[1],497 .cols = h,
534 };498 };
535 }499 }
536500
537 pub fn deinit(self: *Vec2d, gpa: Allocator) void {501 pub fn deinit(v: *Vec2d, gpa: Allocator) void {
538 gpa.free(self.data);502 gpa.free(v.data);
539 self.* = undefined;503 v.* = undefined;
540 }504 }
541505
542 pub fn fill(self: *Vec2d, value: u16) void {506 pub fn fill(v: *Vec2d, value: u16) void {
543 @memset(self.data, value);507 @memset(v.data, value);
544 }508 }
545509
546 fn get(self: Vec2d, row: usize) ![]u16 {510 fn get(v: Vec2d, row: usize) ![]u16 {
547 const start_row = try math.mul(usize, row, self.cols);511 const start_row = try math.mul(usize, row, v.cols);
548 const end_row = try math.add(usize, start_row, self.cols);512 const end_row = try math.add(usize, start_row, v.cols);
549 return self.data[start_row..end_row];513 return v.data[start_row..end_row];
550 }514 }
551 };515 };
552516
...@@ -627,6 +591,7 @@ pub const Decompress = struct {...@@ -627,6 +591,7 @@ pub const Decompress = struct {
627 range_decoder: RangeDecoder,591 range_decoder: RangeDecoder,
628 decode: Decode,592 decode: Decode,
629 err: ?Error,593 err: ?Error,
594 unpacked_size: ?u64,
630595
631 pub const Error = error{596 pub const Error = error{
632 OutOfMemory,597 OutOfMemory,
...@@ -654,7 +619,7 @@ pub const Decompress = struct {...@@ -654,7 +619,7 @@ pub const Decompress = struct {
654 .input = input,619 .input = input,
655 .buffer = Decode.CircularBuffer.init(params.dict_size, mem_limit),620 .buffer = Decode.CircularBuffer.init(params.dict_size, mem_limit),
656 .range_decoder = try RangeDecoder.init(input),621 .range_decoder = try RangeDecoder.init(input),
657 .decode = try Decode.init(gpa, params.properties, params.unpacked_size),622 .decode = try Decode.init(gpa, params.properties),
658 .reader = .{623 .reader = .{
659 .buffer = buffer,624 .buffer = buffer,
660 .vtable = &.{625 .vtable = &.{
...@@ -666,6 +631,7 @@ pub const Decompress = struct {...@@ -666,6 +631,7 @@ pub const Decompress = struct {
666 .end = 0,631 .end = 0,
667 },632 },
668 .err = null,633 .err = null,
634 .unpacked_size = params.unpacked_size,
669 };635 };
670 }636 }
671637
...@@ -728,20 +694,46 @@ pub const Decompress = struct {...@@ -728,20 +694,46 @@ pub const Decompress = struct {
728 r.end = allocating.writer.end;694 r.end = allocating.writer.end;
729 }695 }
730 if (d.decode.state == math.maxInt(usize)) return error.EndOfStream;696 if (d.decode.state == math.maxInt(usize)) return error.EndOfStream;
731 d.decode.process(d.input, &allocating, &d.buffer, &d.range_decoder) catch |err| switch (err) {697
698 process_next: {
699 if (d.unpacked_size) |unpacked_size| {
700 if (d.buffer.len >= unpacked_size) break :process_next;
701 } else if (d.range_decoder.isFinished()) {
702 break :process_next;
703 }
704 switch (d.decode.process(d.input, &allocating, &d.buffer, &d.range_decoder) catch |err| switch (err) {
705 error.WriteFailed => {
706 d.err = error.OutOfMemory;
707 return error.ReadFailed;
708 },
709 error.EndOfStream => {
710 d.err = error.EndOfStream;
711 return error.ReadFailed;
712 },
713 else => |e| {
714 d.err = e;
715 return error.ReadFailed;
716 },
717 }) {
718 .more => return 0,
719 .finished => break :process_next,
720 }
721 }
722
723 if (d.unpacked_size) |unpacked_size| {
724 if (d.buffer.len != unpacked_size) {
725 d.err = error.DecompressedSizeMismatch;
726 return error.ReadFailed;
727 }
728 }
729
730 d.buffer.finish(&allocating.writer) catch |err| switch (err) {
732 error.WriteFailed => {731 error.WriteFailed => {
733 d.err = error.OutOfMemory;732 d.err = error.OutOfMemory;
734 return error.ReadFailed;733 return error.ReadFailed;
735 },734 },
736 error.EndOfStream => {
737 d.err = error.EndOfStream;
738 return error.ReadFailed;
739 },
740 else => |e| {
741 d.err = e;
742 return error.ReadFailed;
743 },
744 };735 };
736 d.decode.state = math.maxInt(usize);
745 return 0;737 return 0;
746 }738 }
747};739};
lib/std/compress/lzma2.zig+72-84
...@@ -6,17 +6,15 @@ const Writer = std.Io.Writer;...@@ -6,17 +6,15 @@ const Writer = std.Io.Writer;
6const Reader = std.Io.Reader;6const Reader = std.Io.Reader;
77
8/// An accumulating buffer for LZ sequences8/// An accumulating buffer for LZ sequences
9pub const LzAccumBuffer = struct {9pub const AccumBuffer = struct {
10 /// Buffer10 /// Buffer
11 buf: ArrayList(u8),11 buf: ArrayList(u8),
12
13 /// Buffer memory limit12 /// Buffer memory limit
14 memlimit: usize,13 memlimit: usize,
15
16 /// Total number of bytes sent through the buffer14 /// Total number of bytes sent through the buffer
17 len: usize,15 len: usize,
1816
19 pub fn init(memlimit: usize) LzAccumBuffer {17 pub fn init(memlimit: usize) AccumBuffer {
20 return .{18 return .{
21 .buf = .{},19 .buf = .{},
22 .memlimit = memlimit,20 .memlimit = memlimit,
...@@ -24,20 +22,20 @@ pub const LzAccumBuffer = struct {...@@ -24,20 +22,20 @@ pub const LzAccumBuffer = struct {
24 };22 };
25 }23 }
2624
27 pub fn appendByte(self: *LzAccumBuffer, allocator: Allocator, byte: u8) !void {25 pub fn appendByte(self: *AccumBuffer, allocator: Allocator, byte: u8) !void {
28 try self.buf.append(allocator, byte);26 try self.buf.append(allocator, byte);
29 self.len += 1;27 self.len += 1;
30 }28 }
3129
32 /// Reset the internal dictionary30 /// Reset the internal dictionary
33 pub fn reset(self: *LzAccumBuffer, writer: *Writer) !void {31 pub fn reset(self: *AccumBuffer, writer: *Writer) !void {
34 try writer.writeAll(self.buf.items);32 try writer.writeAll(self.buf.items);
35 self.buf.clearRetainingCapacity();33 self.buf.clearRetainingCapacity();
36 self.len = 0;34 self.len = 0;
37 }35 }
3836
39 /// Retrieve the last byte or return a default37 /// Retrieve the last byte or return a default
40 pub fn lastOr(self: LzAccumBuffer, lit: u8) u8 {38 pub fn lastOr(self: AccumBuffer, lit: u8) u8 {
41 const buf_len = self.buf.items.len;39 const buf_len = self.buf.items.len;
42 return if (buf_len == 0)40 return if (buf_len == 0)
43 lit41 lit
...@@ -46,7 +44,7 @@ pub const LzAccumBuffer = struct {...@@ -46,7 +44,7 @@ pub const LzAccumBuffer = struct {
46 }44 }
4745
48 /// Retrieve the n-th last byte46 /// Retrieve the n-th last byte
49 pub fn lastN(self: LzAccumBuffer, dist: usize) !u8 {47 pub fn lastN(self: AccumBuffer, dist: usize) !u8 {
50 const buf_len = self.buf.items.len;48 const buf_len = self.buf.items.len;
51 if (dist > buf_len) {49 if (dist > buf_len) {
52 return error.CorruptInput;50 return error.CorruptInput;
...@@ -57,7 +55,7 @@ pub const LzAccumBuffer = struct {...@@ -57,7 +55,7 @@ pub const LzAccumBuffer = struct {
5755
58 /// Append a literal56 /// Append a literal
59 pub fn appendLiteral(57 pub fn appendLiteral(
60 self: *LzAccumBuffer,58 self: *AccumBuffer,
61 allocator: Allocator,59 allocator: Allocator,
62 lit: u8,60 lit: u8,
63 writer: *Writer,61 writer: *Writer,
...@@ -72,7 +70,7 @@ pub const LzAccumBuffer = struct {...@@ -72,7 +70,7 @@ pub const LzAccumBuffer = struct {
7270
73 /// Fetch an LZ sequence (length, distance) from inside the buffer71 /// Fetch an LZ sequence (length, distance) from inside the buffer
74 pub fn appendLz(72 pub fn appendLz(
75 self: *LzAccumBuffer,73 self: *AccumBuffer,
76 allocator: Allocator,74 allocator: Allocator,
77 len: usize,75 len: usize,
78 dist: usize,76 dist: usize,
...@@ -95,12 +93,12 @@ pub const LzAccumBuffer = struct {...@@ -95,12 +93,12 @@ pub const LzAccumBuffer = struct {
95 self.len += len;93 self.len += len;
96 }94 }
9795
98 pub fn finish(self: *LzAccumBuffer, writer: *Writer) !void {96 pub fn finish(self: *AccumBuffer, writer: *Writer) !void {
99 try writer.writeAll(self.buf.items);97 try writer.writeAll(self.buf.items);
100 self.buf.clearRetainingCapacity();98 self.buf.clearRetainingCapacity();
101 }99 }
102100
103 pub fn deinit(self: *LzAccumBuffer, allocator: Allocator) void {101 pub fn deinit(self: *AccumBuffer, allocator: Allocator) void {
104 self.buf.deinit(allocator);102 self.buf.deinit(allocator);
105 self.* = undefined;103 self.* = undefined;
106 }104 }
...@@ -109,59 +107,43 @@ pub const LzAccumBuffer = struct {...@@ -109,59 +107,43 @@ pub const LzAccumBuffer = struct {
109pub const Decode = struct {107pub const Decode = struct {
110 lzma_decode: lzma.Decode,108 lzma_decode: lzma.Decode,
111109
112 pub fn init(allocator: Allocator) !Decode {110 pub fn init(gpa: Allocator) !Decode {
113 return Decode{111 return .{ .lzma_decode = try lzma.Decode.init(gpa, .{ .lc = 0, .lp = 0, .pb = 0 }) };
114 .lzma_decode = try lzma.Decode.init(
115 allocator,
116 .{
117 .lc = 0,
118 .lp = 0,
119 .pb = 0,
120 },
121 null,
122 ),
123 };
124 }112 }
125113
126 pub fn deinit(self: *Decode, allocator: Allocator) void {114 pub fn deinit(self: *Decode, gpa: Allocator) void {
127 self.lzma_decode.deinit(allocator);115 self.lzma_decode.deinit(gpa);
128 self.* = undefined;116 self.* = undefined;
129 }117 }
130118
131 pub fn decompress(119 pub fn decompress(d: *Decode, reader: *Reader, allocating: *Writer.Allocating) !void {
132 self: *Decode,120 const gpa = allocating.allocator;
133 allocator: Allocator,121
134 reader: *Reader,122 var accum = AccumBuffer.init(std.math.maxInt(usize));
135 writer: *Writer,123 defer accum.deinit(gpa);
136 ) !void {
137 var accum = LzAccumBuffer.init(std.math.maxInt(usize));
138 defer accum.deinit(allocator);
139124
140 while (true) {125 while (true) {
141 const status = try reader.readByte();126 const status = try reader.takeByte();
142127
143 switch (status) {128 switch (status) {
144 0 => break,129 0 => break,
145 1 => try parseUncompressed(allocator, reader, writer, &accum, true),130 1 => try parseUncompressed(reader, allocating, &accum, true),
146 2 => try parseUncompressed(allocator, reader, writer, &accum, false),131 2 => try parseUncompressed(reader, allocating, &accum, false),
147 else => try self.parseLzma(allocator, reader, writer, &accum, status),132 else => try d.parseLzma(reader, allocating, &accum, status),
148 }133 }
149 }134 }
150135
151 try accum.finish(writer);136 try accum.finish(&allocating.writer);
152 }137 }
153138
154 fn parseLzma(139 fn parseLzma(
155 self: *Decode,140 d: *Decode,
156 allocator: Allocator,
157 reader: *Reader,141 reader: *Reader,
158 writer: *Writer,142 allocating: *Writer.Allocating,
159 accum: *LzAccumBuffer,143 accum: *AccumBuffer,
160 status: u8,144 status: u8,
161 ) !void {145 ) !void {
162 if (status & 0x80 == 0) {146 if (status & 0x80 == 0) return error.CorruptInput;
163 return error.CorruptInput;
164 }
165147
166 const Reset = struct {148 const Reset = struct {
167 dict: bool,149 dict: bool,
...@@ -169,23 +151,23 @@ pub const Decode = struct {...@@ -169,23 +151,23 @@ pub const Decode = struct {
169 props: bool,151 props: bool,
170 };152 };
171153
172 const reset = switch ((status >> 5) & 0x3) {154 const reset: Reset = switch ((status >> 5) & 0x3) {
173 0 => Reset{155 0 => .{
174 .dict = false,156 .dict = false,
175 .state = false,157 .state = false,
176 .props = false,158 .props = false,
177 },159 },
178 1 => Reset{160 1 => .{
179 .dict = false,161 .dict = false,
180 .state = true,162 .state = true,
181 .props = false,163 .props = false,
182 },164 },
183 2 => Reset{165 2 => .{
184 .dict = false,166 .dict = false,
185 .state = true,167 .state = true,
186 .props = true,168 .props = true,
187 },169 },
188 3 => Reset{170 3 => .{
189 .dict = true,171 .dict = true,
190 .state = true,172 .state = true,
191 .props = true,173 .props = true,
...@@ -196,24 +178,24 @@ pub const Decode = struct {...@@ -196,24 +178,24 @@ pub const Decode = struct {
196 const unpacked_size = blk: {178 const unpacked_size = blk: {
197 var tmp: u64 = status & 0x1F;179 var tmp: u64 = status & 0x1F;
198 tmp <<= 16;180 tmp <<= 16;
199 tmp |= try reader.readInt(u16, .big);181 tmp |= try reader.takeInt(u16, .big);
200 break :blk tmp + 1;182 break :blk tmp + 1;
201 };183 };
202184
203 const packed_size = blk: {185 const packed_size = blk: {
204 const tmp: u17 = try reader.readInt(u16, .big);186 const tmp: u17 = try reader.takeInt(u16, .big);
205 break :blk tmp + 1;187 break :blk tmp + 1;
206 };188 };
207189
208 if (reset.dict) {190 if (reset.dict) try accum.reset(&allocating.writer);
209 try accum.reset(writer);191
210 }192 const ld = &d.lzma_decode;
211193
212 if (reset.state) {194 if (reset.state) {
213 var new_props = self.lzma_decode.properties;195 var new_props = ld.properties;
214196
215 if (reset.props) {197 if (reset.props) {
216 var props = try reader.readByte();198 var props = try reader.takeByte();
217 if (props >= 225) {199 if (props >= 225) {
218 return error.CorruptInput;200 return error.CorruptInput;
219 }201 }
...@@ -231,38 +213,44 @@ pub const Decode = struct {...@@ -231,38 +213,44 @@ pub const Decode = struct {
231 new_props = .{ .lc = lc, .lp = lp, .pb = pb };213 new_props = .{ .lc = lc, .lp = lp, .pb = pb };
232 }214 }
233215
234 try self.lzma_decode.resetState(allocator, new_props);216 try ld.resetState(allocating.allocator, new_props);
235 }217 }
236218
237 self.lzma_decode.unpacked_size = unpacked_size + accum.len;219 var range_decoder = try lzma.RangeDecoder.init(reader);
238220
239 var counter = std.io.countingReader(reader);221 while (true) {
240 const counter_reader = counter.reader();222 if (accum.len >= unpacked_size) break;
241223 if (range_decoder.isFinished()) break;
242 var rangecoder = try lzma.RangeDecoder.init(counter_reader);224 switch (try ld.process(reader, allocating, accum, &range_decoder)) {
243 while (try self.lzma_decode.process(allocator, counter_reader, writer, accum, &rangecoder) == .continue_) {}225 .more => continue,
244226 .finished => break,
245 if (counter.bytes_read != packed_size) {227 }
246 return error.CorruptInput;
247 }228 }
229 if (accum.len != unpacked_size) return error.DecompressedSizeMismatch;
230
231 // TODO restore this error
232 //if (counter.bytes_read != packed_size) {
233 // return error.CorruptInput;
234 //}
235 _ = packed_size;
248 }236 }
249237
250 fn parseUncompressed(238 fn parseUncompressed(
251 allocator: Allocator,
252 reader: *Reader,239 reader: *Reader,
253 writer: *Writer,240 allocating: *Writer.Allocating,
254 accum: *LzAccumBuffer,241 accum: *AccumBuffer,
255 reset_dict: bool,242 reset_dict: bool,
256 ) !void {243 ) !void {
257 const unpacked_size = @as(u17, try reader.readInt(u16, .big)) + 1;244 const unpacked_size = @as(u17, try reader.takeInt(u16, .big)) + 1;
258245
259 if (reset_dict) {246 if (reset_dict) try accum.reset(&allocating.writer);
260 try accum.reset(writer);247
261 }248 const gpa = allocating.allocator;
262249
263 var i: @TypeOf(unpacked_size) = 0;250 var i = unpacked_size;
264 while (i < unpacked_size) : (i += 1) {251 while (i != 0) {
265 try accum.appendByte(allocator, try reader.readByte());252 try accum.appendByte(gpa, try reader.takeByte());
253 i -= 1;
266 }254 }
267 }255 }
268};256};
...@@ -273,13 +261,13 @@ test "decompress hello world stream" {...@@ -273,13 +261,13 @@ test "decompress hello world stream" {
273261
274 const gpa = std.testing.allocator;262 const gpa = std.testing.allocator;
275263
276 var stream: std.Io.Reader = .fixed(compressed);264 var decode = try Decode.init(gpa);
277
278 var decode = try Decode.init(gpa, &stream);
279 defer decode.deinit(gpa);265 defer decode.deinit(gpa);
280266
281 const result = try decode.reader.allocRemaining(gpa, .unlimited);267 var stream: std.Io.Reader = .fixed(compressed);
282 defer gpa.free(result);268 var result: std.Io.Writer.Allocating = .init(gpa);
269 defer result.deinit();
283270
284 try std.testing.expectEqualStrings(expected, result);271 try decode.decompress(&stream, &result);
272 try std.testing.expectEqualStrings(expected, result.written());
285}273}