authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-25 18:03:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-26 21:00:58-07:00
log58e60697e2930f4311ae9e744ae1c2877e0b69ed
treefd84142e826d2252f23eaae9b002ae0d3f43e341
parent6464e0d4fc9937e154c34567891bae84c63732b9

std.compress.lzma: update for new I/O API


5 files changed, 556 insertions(+), 653 deletions(-)

lib/std/compress/lzma.zig+214-232
......@@ -4,49 +4,34 @@ const mem = std.mem;
44const Allocator = std.mem.Allocator;
55const assert = std.debug.assert;
66const ArrayList = std.ArrayList;
7const Writer = std.Io.Writer;
8const Reader = std.Io.Reader;
79
810pub const RangeDecoder = struct {
911 range: u32,
1012 code: u32,
1113
12 pub fn init(reader: anytype) !RangeDecoder {
13 const reserved = try reader.readByte();
14 if (reserved != 0) {
15 return error.CorruptInput;
16 }
17 return RangeDecoder{
18 .range = 0xFFFF_FFFF,
19 .code = try reader.readInt(u32, .big),
20 };
21 }
22
23 pub fn fromParts(
24 range: u32,
25 code: u32,
26 ) RangeDecoder {
14 pub fn init(reader: *Reader) !RangeDecoder {
15 const reserved = try reader.takeByte();
16 if (reserved != 0) return error.InvalidRangeCode;
2717 return .{
28 .range = range,
29 .code = code,
18 .range = 0xFFFF_FFFF,
19 .code = try reader.takeInt(u32, .big),
3020 };
3121 }
3222
33 pub fn set(self: *RangeDecoder, range: u32, code: u32) void {
34 self.range = range;
35 self.code = code;
36 }
37
38 pub inline fn isFinished(self: RangeDecoder) bool {
23 pub fn isFinished(self: RangeDecoder) bool {
3924 return self.code == 0;
4025 }
4126
42 inline fn normalize(self: *RangeDecoder, reader: anytype) !void {
27 fn normalize(self: *RangeDecoder, reader: *Reader) !void {
4328 if (self.range < 0x0100_0000) {
4429 self.range <<= 8;
45 self.code = (self.code << 8) ^ @as(u32, try reader.readByte());
30 self.code = (self.code << 8) ^ @as(u32, try reader.takeByte());
4631 }
4732 }
4833
49 inline fn getBit(self: *RangeDecoder, reader: anytype) !bool {
34 fn getBit(self: *RangeDecoder, reader: *Reader) !bool {
5035 self.range >>= 1;
5136
5237 const bit = self.code >= self.range;
......@@ -57,7 +42,7 @@ pub const RangeDecoder = struct {
5742 return bit;
5843 }
5944
60 pub fn get(self: *RangeDecoder, reader: anytype, count: usize) !u32 {
45 pub fn get(self: *RangeDecoder, reader: *Reader, count: usize) !u32 {
6146 var result: u32 = 0;
6247 var i: usize = 0;
6348 while (i < count) : (i += 1)
......@@ -65,7 +50,7 @@ pub const RangeDecoder = struct {
6550 return result;
6651 }
6752
68 pub inline fn decodeBit(self: *RangeDecoder, reader: anytype, prob: *u16, update: bool) !bool {
53 pub fn decodeBit(self: *RangeDecoder, reader: *Reader, prob: *u16, update: bool) !bool {
6954 const bound = (self.range >> 11) * prob.*;
7055
7156 if (self.code < bound) {
......@@ -88,7 +73,7 @@ pub const RangeDecoder = struct {
8873
8974 fn parseBitTree(
9075 self: *RangeDecoder,
91 reader: anytype,
76 reader: *Reader,
9277 num_bits: u5,
9378 probs: []u16,
9479 update: bool,
......@@ -104,7 +89,7 @@ pub const RangeDecoder = struct {
10489
10590 pub fn parseReverseBitTree(
10691 self: *RangeDecoder,
107 reader: anytype,
92 reader: *Reader,
10893 num_bits: u5,
10994 probs: []u16,
11095 offset: usize,
......@@ -123,7 +108,7 @@ pub const RangeDecoder = struct {
123108};
124109
125110pub const Decode = struct {
126 lzma_props: Properties,
111 properties: Properties,
127112 unpacked_size: ?u64,
128113 literal_probs: Vec2d,
129114 pos_slot_decoder: [4]BitTree(6),
......@@ -141,14 +126,14 @@ pub const Decode = struct {
141126 rep_len_decoder: LenDecoder,
142127
143128 pub fn init(
144 allocator: Allocator,
145 lzma_props: Properties,
129 gpa: Allocator,
130 properties: Properties,
146131 unpacked_size: ?u64,
147132 ) !Decode {
148133 return .{
149 .lzma_props = lzma_props,
134 .properties = properties,
150135 .unpacked_size = unpacked_size,
151 .literal_probs = try Vec2d.init(allocator, 0x400, .{ @as(usize, 1) << (lzma_props.lc + lzma_props.lp), 0x300 }),
136 .literal_probs = try Vec2d.init(gpa, 0x400, .{ @as(usize, 1) << (properties.lc + properties.lp), 0x300 }),
152137 .pos_slot_decoder = @splat(.{}),
153138 .align_decoder = .{},
154139 .pos_decoders = @splat(0x400),
......@@ -165,21 +150,21 @@ pub const Decode = struct {
165150 };
166151 }
167152
168 pub fn deinit(self: *Decode, allocator: Allocator) void {
169 self.literal_probs.deinit(allocator);
153 pub fn deinit(self: *Decode, gpa: Allocator) void {
154 self.literal_probs.deinit(gpa);
170155 self.* = undefined;
171156 }
172157
173 pub fn resetState(self: *Decode, allocator: Allocator, new_props: Properties) !void {
158 pub fn resetState(self: *Decode, gpa: Allocator, new_props: Properties) !void {
174159 new_props.validate();
175 if (self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp) {
160 if (self.properties.lc + self.properties.lp == new_props.lc + new_props.lp) {
176161 self.literal_probs.fill(0x400);
177162 } else {
178 self.literal_probs.deinit(allocator);
179 self.literal_probs = try Vec2d.init(allocator, 0x400, .{ @as(usize, 1) << (new_props.lc + new_props.lp), 0x300 });
163 self.literal_probs.deinit(gpa);
164 self.literal_probs = try Vec2d.init(gpa, 0x400, .{ @as(usize, 1) << (new_props.lc + new_props.lp), 0x300 });
180165 }
181166
182 self.lzma_props = new_props;
167 self.properties = new_props;
183168 for (&self.pos_slot_decoder) |*t| t.reset();
184169 self.align_decoder.reset();
185170 self.pos_decoders = @splat(0x400);
......@@ -195,26 +180,23 @@ pub const Decode = struct {
195180 self.rep_len_decoder.reset();
196181 }
197182
198 fn processNextInner(
183 fn processNext(
199184 self: *Decode,
200 allocator: Allocator,
201 reader: anytype,
202 writer: anytype,
203 buffer: anytype,
185 reader: *Reader,
186 allocating: *Writer.Allocating,
187 buffer: *CircularBuffer,
204188 decoder: *RangeDecoder,
205189 update: bool,
206190 ) !ProcessingStatus {
207 const pos_state = buffer.len & ((@as(usize, 1) << self.lzma_props.pb) - 1);
191 const gpa = allocating.allocator;
192 const writer = &allocating.writer;
193 const pos_state = buffer.len & ((@as(usize, 1) << self.properties.pb) - 1);
208194
209 if (!try decoder.decodeBit(
210 reader,
211 &self.is_match[(self.state << 4) + pos_state],
212 update,
213 )) {
195 if (!try decoder.decodeBit(reader, &self.is_match[(self.state << 4) + pos_state], update)) {
214196 const byte: u8 = try self.decodeLiteral(reader, buffer, decoder, update);
215197
216198 if (update) {
217 try buffer.appendLiteral(allocator, byte, writer);
199 try buffer.appendLiteral(gpa, byte, writer);
218200
219201 self.state = if (self.state < 4)
220202 0
......@@ -223,7 +205,7 @@ pub const Decode = struct {
223205 else
224206 self.state - 6;
225207 }
226 return .continue_;
208 return .more;
227209 }
228210
229211 var len: usize = undefined;
......@@ -237,9 +219,9 @@ pub const Decode = struct {
237219 if (update) {
238220 self.state = if (self.state < 7) 9 else 11;
239221 const dist = self.rep[0] + 1;
240 try buffer.appendLz(allocator, 1, dist, writer);
222 try buffer.appendLz(gpa, 1, dist, writer);
241223 }
242 return .continue_;
224 return .more;
243225 }
244226 } else {
245227 const idx: usize = if (!try decoder.decodeBit(reader, &self.is_rep_g1[self.state], update))
......@@ -293,31 +275,19 @@ pub const Decode = struct {
293275 len += 2;
294276
295277 const dist = self.rep[0] + 1;
296 try buffer.appendLz(allocator, len, dist, writer);
278 try buffer.appendLz(gpa, len, dist, writer);
297279 }
298280
299 return .continue_;
300 }
301
302 fn processNext(
303 self: *Decode,
304 allocator: Allocator,
305 reader: anytype,
306 writer: anytype,
307 buffer: anytype,
308 decoder: *RangeDecoder,
309 ) !ProcessingStatus {
310 return self.processNextInner(allocator, reader, writer, buffer, decoder, true);
281 return .more;
311282 }
312283
313284 pub fn process(
314285 self: *Decode,
315 allocator: Allocator,
316 reader: anytype,
317 writer: anytype,
318 buffer: anytype,
286 reader: *Reader,
287 allocating: *Writer.Allocating,
288 buffer: *CircularBuffer,
319289 decoder: *RangeDecoder,
320 ) !ProcessingStatus {
290 ) !void {
321291 process_next: {
322292 if (self.unpacked_size) |unpacked_size| {
323293 if (buffer.len >= unpacked_size) {
......@@ -326,26 +296,24 @@ pub const Decode = struct {
326296 } else if (decoder.isFinished()) {
327297 break :process_next;
328298 }
329
330 switch (try self.processNext(allocator, reader, writer, buffer, decoder)) {
331 .continue_ => return .continue_,
332 .finished => break :process_next,
299 switch (try self.processNext(reader, allocating, buffer, decoder, true)) {
300 .more => return,
301 .finished => {},
333302 }
334303 }
335304
336305 if (self.unpacked_size) |unpacked_size| {
337 if (buffer.len != unpacked_size) {
338 return error.CorruptInput;
339 }
306 if (buffer.len != unpacked_size) return error.DecompressedSizeMismatch;
340307 }
341308
342 return .finished;
309 try buffer.finish(&allocating.writer);
310 self.state = math.maxInt(usize);
343311 }
344312
345313 fn decodeLiteral(
346314 self: *Decode,
347 reader: anytype,
348 buffer: anytype,
315 reader: *Reader,
316 buffer: *CircularBuffer,
349317 decoder: *RangeDecoder,
350318 update: bool,
351319 ) !u8 {
......@@ -353,9 +321,9 @@ pub const Decode = struct {
353321 const prev_byte = @as(usize, buffer.lastOr(def_prev_byte));
354322
355323 var result: usize = 1;
356 const lit_state = ((buffer.len & ((@as(usize, 1) << self.lzma_props.lp) - 1)) << self.lzma_props.lc) +
357 (prev_byte >> (8 - self.lzma_props.lc));
358 const probs = try self.literal_probs.getMut(lit_state);
324 const lit_state = ((buffer.len & ((@as(usize, 1) << self.properties.lp) - 1)) << self.properties.lc) +
325 (prev_byte >> (8 - self.properties.lc));
326 const probs = try self.literal_probs.get(lit_state);
359327
360328 if (self.state >= 7) {
361329 var match_byte = @as(usize, try buffer.lastN(self.rep[0] + 1));
......@@ -384,7 +352,7 @@ pub const Decode = struct {
384352
385353 fn decodeDistance(
386354 self: *Decode,
387 reader: anytype,
355 reader: *Reader,
388356 decoder: *RangeDecoder,
389357 length: usize,
390358 update: bool,
......@@ -415,46 +383,40 @@ pub const Decode = struct {
415383 }
416384
417385 /// A circular buffer for LZ sequences
418 pub const LzCircularBuffer = struct {
386 pub const CircularBuffer = struct {
419387 /// Circular buffer
420388 buf: ArrayList(u8),
421
422389 /// Length of the buffer
423390 dict_size: usize,
424
425391 /// Buffer memory limit
426 memlimit: usize,
427
392 mem_limit: usize,
428393 /// Current position
429394 cursor: usize,
430
431395 /// Total number of bytes sent through the buffer
432396 len: usize,
433397
434 const Self = @This();
435
436 pub fn init(dict_size: usize, memlimit: usize) Self {
437 return Self{
398 pub fn init(dict_size: usize, mem_limit: usize) CircularBuffer {
399 return .{
438400 .buf = .{},
439401 .dict_size = dict_size,
440 .memlimit = memlimit,
402 .mem_limit = mem_limit,
441403 .cursor = 0,
442404 .len = 0,
443405 };
444406 }
445407
446 pub fn get(self: Self, index: usize) u8 {
408 pub fn get(self: CircularBuffer, index: usize) u8 {
447409 return if (0 <= index and index < self.buf.items.len)
448410 self.buf.items[index]
449411 else
450412 0;
451413 }
452414
453 pub fn set(self: *Self, allocator: Allocator, index: usize, value: u8) !void {
454 if (index >= self.memlimit) {
415 pub fn set(self: *CircularBuffer, gpa: Allocator, index: usize, value: u8) !void {
416 if (index >= self.mem_limit) {
455417 return error.CorruptInput;
456418 }
457 try self.buf.ensureTotalCapacity(allocator, index + 1);
419 try self.buf.ensureTotalCapacity(gpa, index + 1);
458420 while (self.buf.items.len < index) {
459421 self.buf.appendAssumeCapacity(0);
460422 }
......@@ -462,7 +424,7 @@ pub const Decode = struct {
462424 }
463425
464426 /// Retrieve the last byte or return a default
465 pub fn lastOr(self: Self, lit: u8) u8 {
427 pub fn lastOr(self: CircularBuffer, lit: u8) u8 {
466428 return if (self.len == 0)
467429 lit
468430 else
......@@ -470,7 +432,7 @@ pub const Decode = struct {
470432 }
471433
472434 /// Retrieve the n-th last byte
473 pub fn lastN(self: Self, dist: usize) !u8 {
435 pub fn lastN(self: CircularBuffer, dist: usize) !u8 {
474436 if (dist > self.dict_size or dist > self.len) {
475437 return error.CorruptInput;
476438 }
......@@ -481,12 +443,12 @@ pub const Decode = struct {
481443
482444 /// Append a literal
483445 pub fn appendLiteral(
484 self: *Self,
485 allocator: Allocator,
446 self: *CircularBuffer,
447 gpa: Allocator,
486448 lit: u8,
487 writer: anytype,
449 writer: *Writer,
488450 ) !void {
489 try self.set(allocator, self.cursor, lit);
451 try self.set(gpa, self.cursor, lit);
490452 self.cursor += 1;
491453 self.len += 1;
492454
......@@ -499,11 +461,11 @@ pub const Decode = struct {
499461
500462 /// Fetch an LZ sequence (length, distance) from inside the buffer
501463 pub fn appendLz(
502 self: *Self,
503 allocator: Allocator,
464 self: *CircularBuffer,
465 gpa: Allocator,
504466 len: usize,
505467 dist: usize,
506 writer: anytype,
468 writer: *Writer,
507469 ) !void {
508470 if (dist > self.dict_size or dist > self.len) {
509471 return error.CorruptInput;
......@@ -513,7 +475,7 @@ pub const Decode = struct {
513475 var i: usize = 0;
514476 while (i < len) : (i += 1) {
515477 const x = self.get(offset);
516 try self.appendLiteral(allocator, x, writer);
478 try self.appendLiteral(gpa, x, writer);
517479 offset += 1;
518480 if (offset == self.dict_size) {
519481 offset = 0;
......@@ -521,15 +483,15 @@ pub const Decode = struct {
521483 }
522484 }
523485
524 pub fn finish(self: *Self, writer: anytype) !void {
486 pub fn finish(self: *CircularBuffer, writer: *Writer) !void {
525487 if (self.cursor > 0) {
526488 try writer.writeAll(self.buf.items[0..self.cursor]);
527489 self.cursor = 0;
528490 }
529491 }
530492
531 pub fn deinit(self: *Self, allocator: Allocator) void {
532 self.buf.deinit(allocator);
493 pub fn deinit(self: *CircularBuffer, gpa: Allocator) void {
494 self.buf.deinit(gpa);
533495 self.* = undefined;
534496 }
535497 };
......@@ -538,11 +500,9 @@ pub const Decode = struct {
538500 return struct {
539501 probs: [1 << num_bits]u16 = @splat(0x400),
540502
541 const Self = @This();
542
543503 pub fn parse(
544 self: *Self,
545 reader: anytype,
504 self: *@This(),
505 reader: *Reader,
546506 decoder: *RangeDecoder,
547507 update: bool,
548508 ) !u32 {
......@@ -550,15 +510,15 @@ pub const Decode = struct {
550510 }
551511
552512 pub fn parseReverse(
553 self: *Self,
554 reader: anytype,
513 self: *@This(),
514 reader: *Reader,
555515 decoder: *RangeDecoder,
556516 update: bool,
557517 ) !u32 {
558518 return decoder.parseReverseBitTree(reader, num_bits, &self.probs, 0, update);
559519 }
560520
561 pub fn reset(self: *Self) void {
521 pub fn reset(self: *@This()) void {
562522 @memset(&self.probs, 0x400);
563523 }
564524 };
......@@ -573,7 +533,7 @@ pub const Decode = struct {
573533
574534 pub fn decode(
575535 self: *LenDecoder,
576 reader: anytype,
536 reader: *Reader,
577537 decoder: *RangeDecoder,
578538 pos_state: usize,
579539 update: bool,
......@@ -600,45 +560,35 @@ pub const Decode = struct {
600560 data: []u16,
601561 cols: usize,
602562
603 const Self = @This();
604
605 pub fn init(allocator: Allocator, value: u16, size: struct { usize, usize }) !Self {
563 pub fn init(gpa: Allocator, value: u16, size: struct { usize, usize }) !Vec2d {
606564 const len = try math.mul(usize, size[0], size[1]);
607 const data = try allocator.alloc(u16, len);
565 const data = try gpa.alloc(u16, len);
608566 @memset(data, value);
609 return Self{
567 return .{
610568 .data = data,
611569 .cols = size[1],
612570 };
613571 }
614572
615 pub fn deinit(self: *Self, allocator: Allocator) void {
616 allocator.free(self.data);
573 pub fn deinit(self: *Vec2d, gpa: Allocator) void {
574 gpa.free(self.data);
617575 self.* = undefined;
618576 }
619577
620 pub fn fill(self: *Self, value: u16) void {
578 pub fn fill(self: *Vec2d, value: u16) void {
621579 @memset(self.data, value);
622580 }
623581
624 inline fn _get(self: Self, row: usize) ![]u16 {
582 fn get(self: Vec2d, row: usize) ![]u16 {
625583 const start_row = try math.mul(usize, row, self.cols);
626584 const end_row = try math.add(usize, start_row, self.cols);
627585 return self.data[start_row..end_row];
628586 }
629
630 pub fn get(self: Self, row: usize) ![]const u16 {
631 return self._get(row);
632 }
633
634 pub fn getMut(self: *Self, row: usize) ![]u16 {
635 return self._get(row);
636 }
637587 };
638588
639589 pub const Options = struct {
640590 unpacked_size: UnpackedSize = .read_from_header,
641 memlimit: ?usize = null,
591 mem_limit: ?usize = null,
642592 allow_incomplete: bool = false,
643593 };
644594
......@@ -649,7 +599,7 @@ pub const Decode = struct {
649599 };
650600
651601 const ProcessingStatus = enum {
652 continue_,
602 more,
653603 finished,
654604 };
655605
......@@ -670,39 +620,34 @@ pub const Decode = struct {
670620 dict_size: u32,
671621 unpacked_size: ?u64,
672622
673 pub fn readHeader(reader: anytype, options: Options) !Params {
674 var props = try reader.readByte();
675 if (props >= 225) {
676 return error.CorruptInput;
677 }
623 pub fn readHeader(reader: *Reader, options: Options) !Params {
624 var props = try reader.takeByte();
625 if (props >= 225) return error.CorruptInput;
678626
679 const lc = @as(u4, @intCast(props % 9));
627 const lc: u4 = @intCast(props % 9);
680628 props /= 9;
681 const lp = @as(u3, @intCast(props % 5));
629 const lp: u3 = @intCast(props % 5);
682630 props /= 5;
683 const pb = @as(u3, @intCast(props));
631 const pb: u3 = @intCast(props);
684632
685 const dict_size_provided = try reader.readInt(u32, .little);
633 const dict_size_provided = try reader.takeInt(u32, .little);
686634 const dict_size = @max(0x1000, dict_size_provided);
687635
688636 const unpacked_size = switch (options.unpacked_size) {
689637 .read_from_header => blk: {
690 const unpacked_size_provided = try reader.readInt(u64, .little);
638 const unpacked_size_provided = try reader.takeInt(u64, .little);
691639 const marker_mandatory = unpacked_size_provided == 0xFFFF_FFFF_FFFF_FFFF;
692 break :blk if (marker_mandatory)
693 null
694 else
695 unpacked_size_provided;
640 break :blk if (marker_mandatory) null else unpacked_size_provided;
696641 },
697642 .read_header_but_use_provided => |x| blk: {
698 _ = try reader.readInt(u64, .little);
643 _ = try reader.takeInt(u64, .little);
699644 break :blk x;
700645 },
701646 .use_provided => |x| x,
702647 };
703648
704 return Params{
705 .properties = Properties{ .lc = lc, .lp = lp, .pb = pb },
649 return .{
650 .properties = .{ .lc = lc, .lp = lp, .pb = pb },
706651 .dict_size = dict_size,
707652 .unpacked_size = unpacked_size,
708653 };
......@@ -710,84 +655,121 @@ pub const Decode = struct {
710655 };
711656};
712657
713pub fn decompress(
714 allocator: Allocator,
715 reader: anytype,
716) !Decompress(@TypeOf(reader)) {
717 return decompressWithOptions(allocator, reader, .{});
718}
719
720pub fn decompressWithOptions(
721 allocator: Allocator,
722 reader: anytype,
723 options: Decode.Options,
724) !Decompress(@TypeOf(reader)) {
725 const params = try Decode.Params.readHeader(reader, options);
726 return Decompress(@TypeOf(reader)).init(allocator, reader, params, options.memlimit);
727}
728
729pub fn Decompress(comptime ReaderType: type) type {
730 return struct {
731 const Self = @This();
732
733 pub const Error =
734 ReaderType.Error ||
735 Allocator.Error ||
736 error{ CorruptInput, EndOfStream, Overflow };
737
738 pub const Reader = std.io.GenericReader(*Self, Error, read);
658pub const Decompress = struct {
659 gpa: Allocator,
660 input: *Reader,
661 reader: Reader,
662 buffer: Decode.CircularBuffer,
663 range_decoder: RangeDecoder,
664 decode: Decode,
665 err: ?Error,
666
667 pub const Error = error{
668 OutOfMemory,
669 ReadFailed,
670 CorruptInput,
671 DecompressedSizeMismatch,
672 EndOfStream,
673 Overflow,
674 };
739675
740 allocator: Allocator,
741 in_reader: ReaderType,
742 to_read: std.ArrayListUnmanaged(u8),
676 /// Takes ownership of `buffer` which may be resized with `gpa`.
677 ///
678 /// LZMA was explicitly designed to take advantage of large heap memory
679 /// being available, with a dictionary size anywhere from 4K to 4G. Thus,
680 /// this API dynamically allocates the dictionary as-needed.
681 pub fn initParams(
682 input: *Reader,
683 gpa: Allocator,
684 buffer: []u8,
685 params: Decode.Params,
686 mem_limit: usize,
687 ) !Decompress {
688 return .{
689 .gpa = gpa,
690 .input = input,
691 .buffer = Decode.CircularBuffer.init(params.dict_size, mem_limit),
692 .range_decoder = try RangeDecoder.init(input),
693 .decode = try Decode.init(gpa, params.properties, params.unpacked_size),
694 .reader = .{
695 .buffer = buffer,
696 .vtable = &.{
697 .readVec = readVec,
698 .stream = stream,
699 },
700 .seek = 0,
701 .end = 0,
702 },
703 .err = null,
704 };
705 }
743706
744 buffer: Decode.LzCircularBuffer,
745 decoder: RangeDecoder,
746 state: Decode,
707 /// Takes ownership of `buffer` which may be resized with `gpa`.
708 ///
709 /// LZMA was explicitly designed to take advantage of large heap memory
710 /// being available, with a dictionary size anywhere from 4K to 4G. Thus,
711 /// this API dynamically allocates the dictionary as-needed.
712 pub fn initOptions(
713 input: *Reader,
714 gpa: Allocator,
715 buffer: []u8,
716 options: Decode.Options,
717 mem_limit: usize,
718 ) !Decompress {
719 const params = try Decode.Params.readHeader(input, options);
720 return initParams(input, gpa, buffer, params, mem_limit);
721 }
747722
748 pub fn init(allocator: Allocator, source: ReaderType, params: Decode.Params, memlimit: ?usize) !Self {
749 return Self{
750 .allocator = allocator,
751 .in_reader = source,
752 .to_read = .{},
723 /// Reclaim ownership of the buffer passed to `init`.
724 pub fn takeBuffer(d: *Decompress) []u8 {
725 const buffer = d.reader.buffer;
726 d.reader.buffer = &.{};
727 return buffer;
728 }
753729
754 .buffer = Decode.LzCircularBuffer.init(params.dict_size, memlimit orelse math.maxInt(usize)),
755 .decoder = try RangeDecoder.init(source),
756 .state = try Decode.init(allocator, params.properties, params.unpacked_size),
757 };
758 }
730 pub fn deinit(d: *Decompress) void {
731 const gpa = d.gpa;
732 gpa.free(d.reader.buffer);
733 d.buffer.deinit(gpa);
734 d.decode.deinit(gpa);
735 d.* = undefined;
736 }
759737
760 pub fn reader(self: *Self) Reader {
761 return .{ .context = self };
762 }
738 fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize {
739 _ = data;
740 return readIndirect(r);
741 }
763742
764 pub fn deinit(self: *Self) void {
765 self.to_read.deinit(self.allocator);
766 self.buffer.deinit(self.allocator);
767 self.state.deinit(self.allocator);
768 self.* = undefined;
769 }
743 fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
744 _ = w;
745 _ = limit;
746 return readIndirect(r);
747 }
770748
771 pub fn read(self: *Self, output: []u8) Error!usize {
772 const writer = self.to_read.writer(self.allocator);
773 while (self.to_read.items.len < output.len) {
774 switch (try self.state.process(self.allocator, self.in_reader, writer, &self.buffer, &self.decoder)) {
775 .continue_ => {},
776 .finished => {
777 try self.buffer.finish(writer);
778 break;
779 },
780 }
781 }
782 const input = self.to_read.items;
783 const n = @min(input.len, output.len);
784 @memcpy(output[0..n], input[0..n]);
785 std.mem.copyForwards(u8, input[0 .. input.len - n], input[n..]);
786 self.to_read.shrinkRetainingCapacity(input.len - n);
787 return n;
788 }
789 };
790}
749 fn readIndirect(r: *Reader) Reader.Error!usize {
750 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
751 const gpa = d.gpa;
752 var allocating = Writer.Allocating.initOwnedSlice(gpa, r.buffer);
753 allocating.writer.end = r.end;
754 defer r.end = allocating.writer.end;
755 if (d.decode.state == math.maxInt(usize)) return error.EndOfStream;
756 d.decode.process(d.input, &allocating, &d.buffer, &d.range_decoder) catch |err| switch (err) {
757 error.WriteFailed => {
758 d.err = error.OutOfMemory;
759 return error.ReadFailed;
760 },
761 error.EndOfStream => {
762 d.err = error.EndOfStream;
763 return error.ReadFailed;
764 },
765 else => |e| {
766 d.err = e;
767 return error.ReadFailed;
768 },
769 };
770 return 0;
771 }
772};
791773
792774test {
793775 _ = @import("lzma/test.zig");
lib/std/compress/lzma/test.zig+14-12
......@@ -1,19 +1,19 @@
11const std = @import("../../std.zig");
2const lzma = @import("../lzma.zig");
2const lzma = std.compress.lzma;
33
44fn testDecompress(compressed: []const u8) ![]u8 {
5 const allocator = std.testing.allocator;
6 var stream = std.io.fixedBufferStream(compressed);
7 var decompressor = try lzma.decompress(allocator, stream.reader());
5 const gpa = std.testing.allocator;
6 var stream: std.Io.Reader = .fixed(compressed);
7
8 var decompressor = try lzma.Decompress.initOptions(&stream, gpa, &.{}, .{}, std.math.maxInt(u32));
89 defer decompressor.deinit();
9 const reader = decompressor.reader();
10 return reader.readAllAlloc(allocator, std.math.maxInt(usize));
10 return decompressor.reader.allocRemaining(gpa, .unlimited);
1111}
1212
1313fn testDecompressEqual(expected: []const u8, compressed: []const u8) !void {
14 const allocator = std.testing.allocator;
14 const gpa = std.testing.allocator;
1515 const decomp = try testDecompress(compressed);
16 defer allocator.free(decomp);
16 defer gpa.free(decomp);
1717 try std.testing.expectEqualSlices(u8, expected, decomp);
1818}
1919
......@@ -89,11 +89,13 @@ test "too small uncompressed size in header" {
8989}
9090
9191test "reading one byte" {
92 const gpa = std.testing.allocator;
9293 const compressed = @embedFile("testdata/good-known_size-with_eopm.lzma");
93 var stream = std.io.fixedBufferStream(compressed);
94 var decompressor = try lzma.decompress(std.testing.allocator, stream.reader());
94 var stream: std.Io.Reader = .fixed(compressed);
95 var decompressor = try lzma.Decompress.initOptions(&stream, gpa, &.{}, .{}, std.math.maxInt(u32));
9596 defer decompressor.deinit();
9697
97 var buffer = [1]u8{0};
98 _ = try decompressor.read(buffer[0..]);
98 var buffer: [1]u8 = undefined;
99 try decompressor.reader.readSliceAll(&buffer);
100 try std.testing.expectEqual(72, buffer[0]);
99101}
lib/std/compress/lzma2.zig+39-44
......@@ -2,6 +2,8 @@ const std = @import("../std.zig");
22const Allocator = std.mem.Allocator;
33const ArrayList = std.ArrayList;
44const lzma = std.compress.lzma;
5const Writer = std.Io.Writer;
6const Reader = std.Io.Reader;
57
68/// An accumulating buffer for LZ sequences
79pub const LzAccumBuffer = struct {
......@@ -14,30 +16,28 @@ pub const LzAccumBuffer = struct {
1416 /// Total number of bytes sent through the buffer
1517 len: usize,
1618
17 const Self = @This();
18
19 pub fn init(memlimit: usize) Self {
20 return Self{
19 pub fn init(memlimit: usize) LzAccumBuffer {
20 return .{
2121 .buf = .{},
2222 .memlimit = memlimit,
2323 .len = 0,
2424 };
2525 }
2626
27 pub fn appendByte(self: *Self, allocator: Allocator, byte: u8) !void {
27 pub fn appendByte(self: *LzAccumBuffer, allocator: Allocator, byte: u8) !void {
2828 try self.buf.append(allocator, byte);
2929 self.len += 1;
3030 }
3131
3232 /// Reset the internal dictionary
33 pub fn reset(self: *Self, writer: anytype) !void {
33 pub fn reset(self: *LzAccumBuffer, writer: *Writer) !void {
3434 try writer.writeAll(self.buf.items);
3535 self.buf.clearRetainingCapacity();
3636 self.len = 0;
3737 }
3838
3939 /// Retrieve the last byte or return a default
40 pub fn lastOr(self: Self, lit: u8) u8 {
40 pub fn lastOr(self: LzAccumBuffer, lit: u8) u8 {
4141 const buf_len = self.buf.items.len;
4242 return if (buf_len == 0)
4343 lit
......@@ -46,7 +46,7 @@ pub const LzAccumBuffer = struct {
4646 }
4747
4848 /// Retrieve the n-th last byte
49 pub fn lastN(self: Self, dist: usize) !u8 {
49 pub fn lastN(self: LzAccumBuffer, dist: usize) !u8 {
5050 const buf_len = self.buf.items.len;
5151 if (dist > buf_len) {
5252 return error.CorruptInput;
......@@ -57,10 +57,10 @@ pub const LzAccumBuffer = struct {
5757
5858 /// Append a literal
5959 pub fn appendLiteral(
60 self: *Self,
60 self: *LzAccumBuffer,
6161 allocator: Allocator,
6262 lit: u8,
63 writer: anytype,
63 writer: *Writer,
6464 ) !void {
6565 _ = writer;
6666 if (self.len >= self.memlimit) {
......@@ -72,11 +72,11 @@ pub const LzAccumBuffer = struct {
7272
7373 /// Fetch an LZ sequence (length, distance) from inside the buffer
7474 pub fn appendLz(
75 self: *Self,
75 self: *LzAccumBuffer,
7676 allocator: Allocator,
7777 len: usize,
7878 dist: usize,
79 writer: anytype,
79 writer: *Writer,
8080 ) !void {
8181 _ = writer;
8282
......@@ -95,23 +95,23 @@ pub const LzAccumBuffer = struct {
9595 self.len += len;
9696 }
9797
98 pub fn finish(self: *Self, writer: anytype) !void {
98 pub fn finish(self: *LzAccumBuffer, writer: *Writer) !void {
9999 try writer.writeAll(self.buf.items);
100100 self.buf.clearRetainingCapacity();
101101 }
102102
103 pub fn deinit(self: *Self, allocator: Allocator) void {
103 pub fn deinit(self: *LzAccumBuffer, allocator: Allocator) void {
104104 self.buf.deinit(allocator);
105105 self.* = undefined;
106106 }
107107};
108108
109109pub const Decode = struct {
110 lzma_state: lzma.Decode,
110 lzma_decode: lzma.Decode,
111111
112112 pub fn init(allocator: Allocator) !Decode {
113113 return Decode{
114 .lzma_state = try lzma.Decode.init(
114 .lzma_decode = try lzma.Decode.init(
115115 allocator,
116116 .{
117117 .lc = 0,
......@@ -124,15 +124,15 @@ pub const Decode = struct {
124124 }
125125
126126 pub fn deinit(self: *Decode, allocator: Allocator) void {
127 self.lzma_state.deinit(allocator);
127 self.lzma_decode.deinit(allocator);
128128 self.* = undefined;
129129 }
130130
131131 pub fn decompress(
132132 self: *Decode,
133133 allocator: Allocator,
134 reader: anytype,
135 writer: anytype,
134 reader: *Reader,
135 writer: *Writer,
136136 ) !void {
137137 var accum = LzAccumBuffer.init(std.math.maxInt(usize));
138138 defer accum.deinit(allocator);
......@@ -154,8 +154,8 @@ pub const Decode = struct {
154154 fn parseLzma(
155155 self: *Decode,
156156 allocator: Allocator,
157 reader: anytype,
158 writer: anytype,
157 reader: *Reader,
158 writer: *Writer,
159159 accum: *LzAccumBuffer,
160160 status: u8,
161161 ) !void {
......@@ -210,7 +210,7 @@ pub const Decode = struct {
210210 }
211211
212212 if (reset.state) {
213 var new_props = self.lzma_state.lzma_props;
213 var new_props = self.lzma_decode.properties;
214214
215215 if (reset.props) {
216216 var props = try reader.readByte();
......@@ -231,16 +231,16 @@ pub const Decode = struct {
231231 new_props = .{ .lc = lc, .lp = lp, .pb = pb };
232232 }
233233
234 try self.lzma_state.resetState(allocator, new_props);
234 try self.lzma_decode.resetState(allocator, new_props);
235235 }
236236
237 self.lzma_state.unpacked_size = unpacked_size + accum.len;
237 self.lzma_decode.unpacked_size = unpacked_size + accum.len;
238238
239239 var counter = std.io.countingReader(reader);
240240 const counter_reader = counter.reader();
241241
242242 var rangecoder = try lzma.RangeDecoder.init(counter_reader);
243 while (try self.lzma_state.process(allocator, counter_reader, writer, accum, &rangecoder) == .continue_) {}
243 while (try self.lzma_decode.process(allocator, counter_reader, writer, accum, &rangecoder) == .continue_) {}
244244
245245 if (counter.bytes_read != packed_size) {
246246 return error.CorruptInput;
......@@ -249,8 +249,8 @@ pub const Decode = struct {
249249
250250 fn parseUncompressed(
251251 allocator: Allocator,
252 reader: anytype,
253 writer: anytype,
252 reader: *Reader,
253 writer: *Writer,
254254 accum: *LzAccumBuffer,
255255 reset_dict: bool,
256256 ) !void {
......@@ -267,24 +267,19 @@ pub const Decode = struct {
267267 }
268268};
269269
270pub fn decompress(
271 allocator: Allocator,
272 reader: anytype,
273 writer: anytype,
274) !void {
275 var decoder = try Decode.init(allocator);
276 defer decoder.deinit(allocator);
277 return decoder.decompress(allocator, reader, writer);
278}
279
280test {
270test "decompress hello world stream" {
281271 const expected = "Hello\nWorld!\n";
282272 const compressed = &[_]u8{ 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x02, 0x00, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00 };
283273
284 const allocator = std.testing.allocator;
285 var decomp = std.array_list.Managed(u8).init(allocator);
286 defer decomp.deinit();
287 var stream = std.io.fixedBufferStream(compressed);
288 try decompress(allocator, stream.reader(), decomp.writer());
289 try std.testing.expectEqualSlices(u8, expected, decomp.items);
274 const gpa = std.testing.allocator;
275
276 var stream: std.Io.Reader = .fixed(compressed);
277
278 var decode = try Decode.init(gpa, &stream);
279 defer decode.deinit(gpa);
280
281 const result = try decode.reader.allocRemaining(gpa, .unlimited);
282 defer gpa.free(result);
283
284 try std.testing.expectEqualStrings(expected, result);
290285}
lib/std/compress/xz.zig+1-365
......@@ -1,368 +1,4 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const ArrayList = std.ArrayList;
4const Crc32 = std.hash.Crc32;
5const Crc64 = std.hash.crc.Crc64Xz;
6const Sha256 = std.crypto.hash.sha2.Sha256;
7const lzma2 = std.compress.lzma2;
8
9pub const Check = enum(u4) {
10 none = 0x00,
11 crc32 = 0x01,
12 crc64 = 0x04,
13 sha256 = 0x0A,
14 _,
15};
16
17fn readStreamFlags(reader: anytype, check: *Check) !void {
18 const reserved1 = try reader.readByte();
19 if (reserved1 != 0) return error.CorruptInput;
20 const byte = try reader.readByte();
21 if ((byte >> 4) != 0) return error.CorruptInput;
22 check.* = @enumFromInt(@as(u4, @truncate(byte)));
23}
24
25pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) {
26 return Decompress(@TypeOf(reader)).init(allocator, reader);
27}
28
29pub fn Decompress(comptime ReaderType: type) type {
30 return struct {
31 const Self = @This();
32
33 pub const Error = ReaderType.Error || Decoder(ReaderType).Error;
34 pub const Reader = std.io.GenericReader(*Self, Error, read);
35
36 allocator: Allocator,
37 block_decoder: Decoder(ReaderType),
38 in_reader: ReaderType,
39
40 fn init(allocator: Allocator, source: ReaderType) !Self {
41 const magic = try source.readBytesNoEof(6);
42 if (!std.mem.eql(u8, &magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 }))
43 return error.BadHeader;
44
45 var check: Check = undefined;
46 const hash_a = blk: {
47 var hasher = hashedReader(source, Crc32.init());
48 try readStreamFlags(hasher.reader(), &check);
49 break :blk hasher.hasher.final();
50 };
51
52 const hash_b = try source.readInt(u32, .little);
53 if (hash_a != hash_b)
54 return error.WrongChecksum;
55
56 return Self{
57 .allocator = allocator,
58 .block_decoder = try decoder(allocator, source, check),
59 .in_reader = source,
60 };
61 }
62
63 pub fn deinit(self: *Self) void {
64 self.block_decoder.deinit();
65 }
66
67 pub fn reader(self: *Self) Reader {
68 return .{ .context = self };
69 }
70
71 pub fn read(self: *Self, buffer: []u8) Error!usize {
72 if (buffer.len == 0)
73 return 0;
74
75 const r = try self.block_decoder.read(buffer);
76 if (r != 0)
77 return r;
78
79 const index_size = blk: {
80 var hasher = hashedReader(self.in_reader, Crc32.init());
81 hasher.hasher.update(&[1]u8{0x00});
82
83 var counter = std.io.countingReader(hasher.reader());
84 counter.bytes_read += 1;
85
86 const counting_reader = counter.reader();
87
88 const record_count = try std.leb.readUleb128(u64, counting_reader);
89 if (record_count != self.block_decoder.block_count)
90 return error.CorruptInput;
91
92 var i: usize = 0;
93 while (i < record_count) : (i += 1) {
94 // TODO: validate records
95 _ = try std.leb.readUleb128(u64, counting_reader);
96 _ = try std.leb.readUleb128(u64, counting_reader);
97 }
98
99 while (counter.bytes_read % 4 != 0) {
100 if (try counting_reader.readByte() != 0)
101 return error.CorruptInput;
102 }
103
104 const hash_a = hasher.hasher.final();
105 const hash_b = try counting_reader.readInt(u32, .little);
106 if (hash_a != hash_b)
107 return error.WrongChecksum;
108
109 break :blk counter.bytes_read;
110 };
111
112 const hash_a = try self.in_reader.readInt(u32, .little);
113
114 const hash_b = blk: {
115 var hasher = hashedReader(self.in_reader, Crc32.init());
116 const hashed_reader = hasher.reader();
117
118 const backward_size = (@as(u64, try hashed_reader.readInt(u32, .little)) + 1) * 4;
119 if (backward_size != index_size)
120 return error.CorruptInput;
121
122 var check: Check = undefined;
123 try readStreamFlags(hashed_reader, &check);
124
125 break :blk hasher.hasher.final();
126 };
127
128 if (hash_a != hash_b)
129 return error.WrongChecksum;
130
131 const magic = try self.in_reader.readBytesNoEof(2);
132 if (!std.mem.eql(u8, &magic, &.{ 'Y', 'Z' }))
133 return error.CorruptInput;
134
135 return 0;
136 }
137 };
138}
139
140pub fn HashedReader(ReaderType: type, HasherType: type) type {
141 return struct {
142 child_reader: ReaderType,
143 hasher: HasherType,
144
145 pub const Error = ReaderType.Error;
146 pub const Reader = std.io.GenericReader(*@This(), Error, read);
147
148 pub fn read(self: *@This(), buf: []u8) Error!usize {
149 const amt = try self.child_reader.read(buf);
150 self.hasher.update(buf[0..amt]);
151 return amt;
152 }
153
154 pub fn reader(self: *@This()) Reader {
155 return .{ .context = self };
156 }
157 };
158}
159
160pub fn hashedReader(
161 reader: anytype,
162 hasher: anytype,
163) HashedReader(@TypeOf(reader), @TypeOf(hasher)) {
164 return .{ .child_reader = reader, .hasher = hasher };
165}
166
167const DecodeError = error{
168 CorruptInput,
169 EndOfStream,
170 EndOfStreamWithNoError,
171 WrongChecksum,
172 Unsupported,
173 Overflow,
174};
175
176pub fn decoder(allocator: Allocator, reader: anytype, check: Check) !Decoder(@TypeOf(reader)) {
177 return Decoder(@TypeOf(reader)).init(allocator, reader, check);
178}
179
180pub fn Decoder(comptime ReaderType: type) type {
181 return struct {
182 const Self = @This();
183 pub const Error =
184 ReaderType.Error ||
185 DecodeError ||
186 Allocator.Error;
187 pub const Reader = std.io.GenericReader(*Self, Error, read);
188
189 allocator: Allocator,
190 inner_reader: ReaderType,
191 check: Check,
192 err: ?Error,
193 to_read: ArrayList(u8),
194 read_pos: usize,
195 block_count: usize,
196
197 fn init(allocator: Allocator, in_reader: ReaderType, check: Check) !Self {
198 return Self{
199 .allocator = allocator,
200 .inner_reader = in_reader,
201 .check = check,
202 .err = null,
203 .to_read = .{},
204 .read_pos = 0,
205 .block_count = 0,
206 };
207 }
208
209 pub fn deinit(self: *Self) void {
210 self.to_read.deinit(self.allocator);
211 }
212
213 pub fn reader(self: *Self) Reader {
214 return .{ .context = self };
215 }
216
217 pub fn read(self: *Self, output: []u8) Error!usize {
218 while (true) {
219 const unread_len = self.to_read.items.len - self.read_pos;
220 if (unread_len > 0) {
221 const n = @min(unread_len, output.len);
222 @memcpy(output[0..n], self.to_read.items[self.read_pos..][0..n]);
223 self.read_pos += n;
224 return n;
225 }
226 if (self.err) |e| {
227 if (e == DecodeError.EndOfStreamWithNoError) {
228 return 0;
229 }
230 return e;
231 }
232 if (self.read_pos > 0) {
233 self.to_read.shrinkRetainingCapacity(0);
234 self.read_pos = 0;
235 }
236 self.readBlock() catch |e| {
237 self.err = e;
238 };
239 }
240 }
241
242 fn readBlock(self: *Self) Error!void {
243 var block_counter = std.io.countingReader(self.inner_reader);
244 const block_reader = block_counter.reader();
245
246 var packed_size: ?u64 = null;
247 var unpacked_size: ?u64 = null;
248
249 // Block Header
250 {
251 var header_hasher = hashedReader(block_reader, Crc32.init());
252 const header_reader = header_hasher.reader();
253
254 const header_size = @as(u64, try header_reader.readByte()) * 4;
255 if (header_size == 0)
256 return error.EndOfStreamWithNoError;
257
258 const Flags = packed struct(u8) {
259 last_filter_index: u2,
260 reserved: u4,
261 has_packed_size: bool,
262 has_unpacked_size: bool,
263 };
264
265 const flags = @as(Flags, @bitCast(try header_reader.readByte()));
266 const filter_count = @as(u3, flags.last_filter_index) + 1;
267 if (filter_count > 1)
268 return error.Unsupported;
269
270 if (flags.has_packed_size)
271 packed_size = try std.leb.readUleb128(u64, header_reader);
272
273 if (flags.has_unpacked_size)
274 unpacked_size = try std.leb.readUleb128(u64, header_reader);
275
276 const FilterId = enum(u64) {
277 lzma2 = 0x21,
278 _,
279 };
280
281 const filter_id = @as(
282 FilterId,
283 @enumFromInt(try std.leb.readUleb128(u64, header_reader)),
284 );
285
286 if (@intFromEnum(filter_id) >= 0x4000_0000_0000_0000)
287 return error.CorruptInput;
288
289 if (filter_id != .lzma2)
290 return error.Unsupported;
291
292 const properties_size = try std.leb.readUleb128(u64, header_reader);
293 if (properties_size != 1)
294 return error.CorruptInput;
295
296 // TODO: use filter properties
297 _ = try header_reader.readByte();
298
299 while (block_counter.bytes_read != header_size) {
300 if (try header_reader.readByte() != 0)
301 return error.CorruptInput;
302 }
303
304 const hash_a = header_hasher.hasher.final();
305 const hash_b = try header_reader.readInt(u32, .little);
306 if (hash_a != hash_b)
307 return error.WrongChecksum;
308 }
309
310 // Compressed Data
311 var packed_counter = std.io.countingReader(block_reader);
312 try lzma2.decompress(
313 self.allocator,
314 packed_counter.reader(),
315 self.to_read.writer(self.allocator),
316 );
317
318 if (packed_size) |s| {
319 if (s != packed_counter.bytes_read)
320 return error.CorruptInput;
321 }
322
323 const unpacked_bytes = self.to_read.items;
324 if (unpacked_size) |s| {
325 if (s != unpacked_bytes.len)
326 return error.CorruptInput;
327 }
328
329 // Block Padding
330 while (block_counter.bytes_read % 4 != 0) {
331 if (try block_reader.readByte() != 0)
332 return error.CorruptInput;
333 }
334
335 switch (self.check) {
336 .none => {},
337 .crc32 => {
338 const hash_a = Crc32.hash(unpacked_bytes);
339 const hash_b = try self.inner_reader.readInt(u32, .little);
340 if (hash_a != hash_b)
341 return error.WrongChecksum;
342 },
343 .crc64 => {
344 const hash_a = Crc64.hash(unpacked_bytes);
345 const hash_b = try self.inner_reader.readInt(u64, .little);
346 if (hash_a != hash_b)
347 return error.WrongChecksum;
348 },
349 .sha256 => {
350 var hash_a: [Sha256.digest_length]u8 = undefined;
351 Sha256.hash(unpacked_bytes, &hash_a, .{});
352
353 var hash_b: [Sha256.digest_length]u8 = undefined;
354 try self.inner_reader.readNoEof(&hash_b);
355
356 if (!std.mem.eql(u8, &hash_a, &hash_b))
357 return error.WrongChecksum;
358 },
359 else => return error.Unsupported,
360 }
361
362 self.block_count += 1;
363 }
364 };
365}
1pub const Decompress = @import("xz/Decompress.zig");
3662
3673test {
3684 _ = @import("xz/test.zig");
lib/std/compress/xz/Decompress.zig created+288
......@@ -0,0 +1,288 @@
1const Decompress = @This();
2const std = @import("../../std.zig");
3const Allocator = std.mem.Allocator;
4const ArrayList = std.ArrayList;
5const Crc32 = std.hash.Crc32;
6const Crc64 = std.hash.crc.Crc64Xz;
7const Sha256 = std.crypto.hash.sha2.Sha256;
8const lzma2 = std.compress.lzma2;
9const Writer = std.Io.Writer;
10const Reader = std.Io.Reader;
11
12/// Underlying compressed data stream to pull bytes from.
13input: *Reader,
14/// Uncompressed bytes output by this stream implementation.
15reader: Reader,
16gpa: Allocator,
17check: Check,
18block_count: usize,
19err: ?Error,
20
21pub const Error = error{
22 ReadFailed,
23 OutOfMemory,
24 CorruptInput,
25 EndOfStream,
26 WrongChecksum,
27 Unsupported,
28 Overflow,
29};
30
31pub const Check = enum(u4) {
32 none = 0x00,
33 crc32 = 0x01,
34 crc64 = 0x04,
35 sha256 = 0x0A,
36 _,
37};
38
39pub const StreamFlags = packed struct(u16) {
40 null: u8 = 0,
41 check: Check,
42 reserved: u4 = 0,
43};
44
45pub const InitError = error{
46 NotXzStream,
47 WrongChecksum,
48};
49
50/// XZ uses a series of LZMA2 blocks which each specify a dictionary size
51/// anywhere from 4K to 4G. Thus, this API dynamically allocates the dictionary
52/// as-needed.
53pub fn init(
54 input: *Reader,
55 gpa: Allocator,
56 /// Decompress takes ownership of this buffer and resizes it with `gpa`.
57 buffer: []u8,
58) Decompress {
59 const magic = try input.takeBytes(6);
60 if (!std.mem.eql(u8, &magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 }))
61 return error.NotXzStream;
62
63 const actual_hash = Crc32.hash(try input.peek(@sizeOf(StreamFlags)));
64 const stream_flags = input.takeStruct(StreamFlags, .little) catch unreachable;
65 const stored_hash = try input.readInt(u32, .little);
66 if (actual_hash != stored_hash) return error.WrongChecksum;
67
68 return .{
69 .input = input,
70 .reader = .{
71 .vtable = &.{
72 .stream = stream,
73 .readVec = readVec,
74 },
75 .buffer = buffer,
76 .seek = 0,
77 .end = 0,
78 },
79 .gpa = gpa,
80 .check = stream_flags.check,
81 .block_count = 0,
82 .err = null,
83 };
84}
85
86fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
87 _ = w;
88 _ = limit;
89 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
90 _ = d;
91 @panic("TODO");
92}
93
94fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize {
95 _ = data;
96 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
97 _ = d;
98 @panic("TODO");
99}
100
101// if (buffer.len == 0)
102// return 0;
103//
104// const r = try self.block_decode.read(buffer);
105// if (r != 0)
106// return r;
107//
108// const index_size = blk: {
109// var hasher = hashedReader(self.in_reader, Crc32.init());
110// hasher.hasher.update(&[1]u8{0x00});
111//
112// var counter = std.io.countingReader(hasher.reader());
113// counter.bytes_read += 1;
114//
115// const counting_reader = counter.reader();
116//
117// const record_count = try std.leb.readUleb128(u64, counting_reader);
118// if (record_count != self.block_decode.block_count)
119// return error.CorruptInput;
120//
121// var i: usize = 0;
122// while (i < record_count) : (i += 1) {
123// // TODO: validate records
124// _ = try std.leb.readUleb128(u64, counting_reader);
125// _ = try std.leb.readUleb128(u64, counting_reader);
126// }
127//
128// while (counter.bytes_read % 4 != 0) {
129// if (try counting_reader.readByte() != 0)
130// return error.CorruptInput;
131// }
132//
133// const hash_a = hasher.hasher.final();
134// const hash_b = try counting_reader.readInt(u32, .little);
135// if (hash_a != hash_b)
136// return error.WrongChecksum;
137//
138// break :blk counter.bytes_read;
139// };
140//
141// const hash_a = try self.in_reader.readInt(u32, .little);
142//
143// const hash_b = blk: {
144// var hasher = hashedReader(self.in_reader, Crc32.init());
145// const hashed_reader = hasher.reader();
146//
147// const backward_size = (@as(u64, try hashed_reader.readInt(u32, .little)) + 1) * 4;
148// if (backward_size != index_size)
149// return error.CorruptInput;
150//
151// var check: Check = undefined;
152// try readStreamFlags(hashed_reader, &check);
153//
154// break :blk hasher.hasher.final();
155// };
156//
157// if (hash_a != hash_b)
158// return error.WrongChecksum;
159//
160// const magic = try self.in_reader.readBytesNoEof(2);
161// if (!std.mem.eql(u8, &magic, &.{ 'Y', 'Z' }))
162// return error.CorruptInput;
163//
164// return 0;
165//}
166
167//fn readBlock(self: *BlockDecode) Error!void {
168// var block_counter = std.io.countingReader(self.inner_reader);
169// const block_reader = block_counter.reader();
170//
171// var packed_size: ?u64 = null;
172// var unpacked_size: ?u64 = null;
173//
174// // Block Header
175// {
176// var header_hasher = hashedReader(block_reader, Crc32.init());
177// const header_reader = header_hasher.reader();
178//
179// const header_size = @as(u64, try header_reader.readByte()) * 4;
180// if (header_size == 0)
181// return error.EndOfStreamWithNoError;
182//
183// const Flags = packed struct(u8) {
184// last_filter_index: u2,
185// reserved: u4,
186// has_packed_size: bool,
187// has_unpacked_size: bool,
188// };
189//
190// const flags = @as(Flags, @bitCast(try header_reader.readByte()));
191// const filter_count = @as(u3, flags.last_filter_index) + 1;
192// if (filter_count > 1)
193// return error.Unsupported;
194//
195// if (flags.has_packed_size)
196// packed_size = try std.leb.readUleb128(u64, header_reader);
197//
198// if (flags.has_unpacked_size)
199// unpacked_size = try std.leb.readUleb128(u64, header_reader);
200//
201// const FilterId = enum(u64) {
202// lzma2 = 0x21,
203// _,
204// };
205//
206// const filter_id = @as(
207// FilterId,
208// @enumFromInt(try std.leb.readUleb128(u64, header_reader)),
209// );
210//
211// if (@intFromEnum(filter_id) >= 0x4000_0000_0000_0000)
212// return error.CorruptInput;
213//
214// if (filter_id != .lzma2)
215// return error.Unsupported;
216//
217// const properties_size = try std.leb.readUleb128(u64, header_reader);
218// if (properties_size != 1)
219// return error.CorruptInput;
220//
221// // TODO: use filter properties
222// _ = try header_reader.readByte();
223//
224// while (block_counter.bytes_read != header_size) {
225// if (try header_reader.readByte() != 0)
226// return error.CorruptInput;
227// }
228//
229// const hash_a = header_hasher.hasher.final();
230// const hash_b = try header_reader.readInt(u32, .little);
231// if (hash_a != hash_b)
232// return error.WrongChecksum;
233// }
234//
235// // Compressed Data
236// var packed_counter = std.io.countingReader(block_reader);
237// try lzma2.decompress(
238// self.allocator,
239// packed_counter.reader(),
240// self.to_read.writer(self.allocator),
241// );
242//
243// if (packed_size) |s| {
244// if (s != packed_counter.bytes_read)
245// return error.CorruptInput;
246// }
247//
248// const unpacked_bytes = self.to_read.items;
249// if (unpacked_size) |s| {
250// if (s != unpacked_bytes.len)
251// return error.CorruptInput;
252// }
253//
254// // Block Padding
255// while (block_counter.bytes_read % 4 != 0) {
256// if (try block_reader.readByte() != 0)
257// return error.CorruptInput;
258// }
259//
260// switch (self.check) {
261// .none => {},
262// .crc32 => {
263// const hash_a = Crc32.hash(unpacked_bytes);
264// const hash_b = try self.inner_reader.readInt(u32, .little);
265// if (hash_a != hash_b)
266// return error.WrongChecksum;
267// },
268// .crc64 => {
269// const hash_a = Crc64.hash(unpacked_bytes);
270// const hash_b = try self.inner_reader.readInt(u64, .little);
271// if (hash_a != hash_b)
272// return error.WrongChecksum;
273// },
274// .sha256 => {
275// var hash_a: [Sha256.digest_length]u8 = undefined;
276// Sha256.hash(unpacked_bytes, &hash_a, .{});
277//
278// var hash_b: [Sha256.digest_length]u8 = undefined;
279// try self.inner_reader.readNoEof(&hash_b);
280//
281// if (!std.mem.eql(u8, &hash_a, &hash_b))
282// return error.WrongChecksum;
283// },
284// else => return error.Unsupported,
285// }
286//
287// self.block_count += 1;
288//}