authorgravatar for fncontroloption@noreply.codeberg.orgFnControlOption <fncontroloption@noreply.codeberg.org> 2023-02-05 08:23:51-08:00
committergravatar for fncontroloption@noreply.codeberg.orgFnControlOption <fncontroloption@noreply.codeberg.org> 2023-02-05 08:23:51-08:00
log622a364715522b239eee57aa071399fa268fd372
treefdeeb68767ec2039187c213ed5adceda57674304
parente03d6c42ea8b65a3b283c4da8c9593b82762874c

Implement std.io.Reader for LZMA1


5 files changed, 97 insertions(+), 54 deletions(-)

lib/std/compress/lzma.zig+74-5
...@@ -1,4 +1,6 @@...@@ -1,4 +1,6 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const math = std.math;
3const mem = std.mem;
2const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
35
4pub const decode = @import("lzma/decode.zig");6pub const decode = @import("lzma/decode.zig");
...@@ -6,13 +8,80 @@ pub const decode = @import("lzma/decode.zig");...@@ -6,13 +8,80 @@ pub const decode = @import("lzma/decode.zig");
6pub fn decompress(8pub fn decompress(
7 allocator: Allocator,9 allocator: Allocator,
8 reader: anytype,10 reader: anytype,
9 writer: anytype,11) !Decompress(@TypeOf(reader)) {
12 return decompressWithOptions(allocator, reader, .{});
13}
14
15pub fn decompressWithOptions(
16 allocator: Allocator,
17 reader: anytype,
10 options: decode.Options,18 options: decode.Options,
11) !void {19) !Decompress(@TypeOf(reader)) {
12 const params = try decode.Params.readHeader(reader, options);20 const params = try decode.Params.readHeader(reader, options);
13 var decoder = try decode.Decoder.init(allocator, params, options.memlimit);21 return Decompress(@TypeOf(reader)).init(allocator, reader, params, options.memlimit);
14 defer decoder.deinit(allocator);22}
15 return decoder.decompress(allocator, reader, writer);23
24pub fn Decompress(comptime ReaderType: type) type {
25 return struct {
26 const Self = @This();
27
28 pub const Error =
29 ReaderType.Error ||
30 Allocator.Error ||
31 error{ CorruptInput, EndOfStream, Overflow };
32
33 pub const Reader = std.io.Reader(*Self, Error, read);
34
35 allocator: Allocator,
36 in_reader: ReaderType,
37 to_read: std.ArrayListUnmanaged(u8),
38
39 buffer: decode.lzbuffer.LzCircularBuffer,
40 decoder: decode.rangecoder.RangeDecoder,
41 state: decode.DecoderState,
42
43 pub fn init(allocator: Allocator, source: ReaderType, params: decode.Params, memlimit: ?usize) !Self {
44 return Self{
45 .allocator = allocator,
46 .in_reader = source,
47 .to_read = .{},
48
49 .buffer = decode.lzbuffer.LzCircularBuffer.init(params.dict_size, memlimit orelse math.maxInt(usize)),
50 .decoder = try decode.rangecoder.RangeDecoder.init(source),
51 .state = try decode.DecoderState.init(allocator, params.properties, params.unpacked_size),
52 };
53 }
54
55 pub fn reader(self: *Self) Reader {
56 return .{ .context = self };
57 }
58
59 pub fn deinit(self: *Self) void {
60 self.to_read.deinit(self.allocator);
61 self.buffer.deinit(self.allocator);
62 self.state.deinit(self.allocator);
63 self.* = undefined;
64 }
65
66 pub fn read(self: *Self, output: []u8) Error!usize {
67 const writer = self.to_read.writer(self.allocator);
68 while (self.to_read.items.len < output.len) {
69 switch (try self.state.process(self.allocator, self.in_reader, writer, &self.buffer, &self.decoder)) {
70 .continue_ => {},
71 .finished => {
72 try self.buffer.finish(writer);
73 break;
74 },
75 }
76 }
77 const input = self.to_read.items;
78 const n = math.min(input.len, output.len);
79 mem.copy(u8, output[0..n], input[0..n]);
80 mem.copy(u8, input, input[n..]);
81 self.to_read.shrinkRetainingCapacity(input.len - n);
82 return n;
83 }
84 };
16}85}
1786
18test {87test {
lib/std/compress/lzma/decode.zig+11-41
...@@ -280,26 +280,29 @@ pub const DecoderState = struct {...@@ -280,26 +280,29 @@ pub const DecoderState = struct {
280 writer: anytype,280 writer: anytype,
281 buffer: anytype,281 buffer: anytype,
282 decoder: *RangeDecoder,282 decoder: *RangeDecoder,
283 ) !void {283 ) !ProcessingStatus {
284 while (true) {284 process_next: {
285 if (self.unpacked_size) |unpacked_size| {285 if (self.unpacked_size) |unpacked_size| {
286 if (buffer.len >= unpacked_size) {286 if (buffer.len >= unpacked_size) {
287 break;287 break :process_next;
288 }288 }
289 } else if (decoder.isFinished()) {289 } else if (decoder.isFinished()) {
290 break;290 break :process_next;
291 }291 }
292292
293 if (try self.processNext(allocator, reader, writer, buffer, decoder) == .finished) {293 switch (try self.processNext(allocator, reader, writer, buffer, decoder)) {
294 break;294 .continue_ => return .continue_,
295 .finished => break :process_next,
295 }296 }
296 }297 }
297298
298 if (self.unpacked_size) |len| {299 if (self.unpacked_size) |unpacked_size| {
299 if (len != buffer.len) {300 if (buffer.len != unpacked_size) {
300 return error.CorruptInput;301 return error.CorruptInput;
301 }302 }
302 }303 }
304
305 return .finished;
303 }306 }
304307
305 fn decodeLiteral(308 fn decodeLiteral(
...@@ -374,36 +377,3 @@ pub const DecoderState = struct {...@@ -374,36 +377,3 @@ pub const DecoderState = struct {
374 return result;377 return result;
375 }378 }
376};379};
377
378pub const Decoder = struct {
379 params: Params,
380 memlimit: usize,
381 state: DecoderState,
382
383 pub fn init(allocator: Allocator, params: Params, memlimit: ?usize) !Decoder {
384 return Decoder{
385 .params = params,
386 .memlimit = memlimit orelse math.maxInt(usize),
387 .state = try DecoderState.init(allocator, params.properties, params.unpacked_size),
388 };
389 }
390
391 pub fn deinit(self: *Decoder, allocator: Allocator) void {
392 self.state.deinit(allocator);
393 self.* = undefined;
394 }
395
396 pub fn decompress(
397 self: *Decoder,
398 allocator: Allocator,
399 reader: anytype,
400 writer: anytype,
401 ) !void {
402 var buffer = LzCircularBuffer.init(self.params.dict_size, self.memlimit);
403 defer buffer.deinit(allocator);
404
405 var decoder = try RangeDecoder.init(reader);
406 try self.state.process(allocator, reader, writer, &buffer, &decoder);
407 try buffer.finish(writer);
408 }
409};
lib/std/compress/lzma/decode/lzbuffer.zig+2
...@@ -98,6 +98,7 @@ pub const LzAccumBuffer = struct {...@@ -98,6 +98,7 @@ pub const LzAccumBuffer = struct {
9898
99 pub fn finish(self: *Self, writer: anytype) !void {99 pub fn finish(self: *Self, writer: anytype) !void {
100 try writer.writeAll(self.buf.items);100 try writer.writeAll(self.buf.items);
101 self.buf.clearRetainingCapacity();
101 }102 }
102103
103 pub fn deinit(self: *Self, allocator: Allocator) void {104 pub fn deinit(self: *Self, allocator: Allocator) void {
...@@ -216,6 +217,7 @@ pub const LzCircularBuffer = struct {...@@ -216,6 +217,7 @@ pub const LzCircularBuffer = struct {
216 pub fn finish(self: *Self, writer: anytype) !void {217 pub fn finish(self: *Self, writer: anytype) !void {
217 if (self.cursor > 0) {218 if (self.cursor > 0) {
218 try writer.writeAll(self.buf.items[0..self.cursor]);219 try writer.writeAll(self.buf.items[0..self.cursor]);
220 self.cursor = 0;
219 }221 }
220 }222 }
221223
lib/std/compress/lzma/test.zig+9-7
...@@ -1,22 +1,24 @@...@@ -1,22 +1,24 @@
1const std = @import("../../std.zig");1const std = @import("../../std.zig");
2const lzma = @import("../lzma.zig");2const lzma = @import("../lzma.zig");
33
4fn testDecompress(compressed: []const u8, writer: anytype) !void {4fn testDecompress(compressed: []const u8) ![]u8 {
5 const allocator = std.testing.allocator;5 const allocator = std.testing.allocator;
6 var stream = std.io.fixedBufferStream(compressed);6 var stream = std.io.fixedBufferStream(compressed);
7 try lzma.decompress(allocator, stream.reader(), writer, .{});7 var decompressor = try lzma.decompress(allocator, stream.reader());
8 defer decompressor.deinit();
9 const reader = decompressor.reader();
10 return reader.readAllAlloc(allocator, std.math.maxInt(usize));
8}11}
912
10fn testDecompressEqual(expected: []const u8, compressed: []const u8) !void {13fn testDecompressEqual(expected: []const u8, compressed: []const u8) !void {
11 const allocator = std.testing.allocator;14 const allocator = std.testing.allocator;
12 var decomp = std.ArrayList(u8).init(allocator);15 const decomp = try testDecompress(compressed);
13 defer decomp.deinit();16 defer allocator.free(decomp);
14 try testDecompress(compressed, decomp.writer());17 try std.testing.expectEqualSlices(u8, expected, decomp);
15 try std.testing.expectEqualSlices(u8, expected, decomp.items);
16}18}
1719
18fn testDecompressError(expected: anyerror, compressed: []const u8) !void {20fn testDecompressError(expected: anyerror, compressed: []const u8) !void {
19 return std.testing.expectError(expected, testDecompress(compressed, std.io.null_writer));21 return std.testing.expectError(expected, testDecompress(compressed));
20}22}
2123
22test "LZMA: decompress empty world" {24test "LZMA: decompress empty world" {
lib/std/compress/lzma2/decode.zig+1-1
...@@ -141,7 +141,7 @@ pub const Decoder = struct {...@@ -141,7 +141,7 @@ pub const Decoder = struct {
141 const counter_reader = counter.reader();141 const counter_reader = counter.reader();
142142
143 var rangecoder = try RangeDecoder.init(counter_reader);143 var rangecoder = try RangeDecoder.init(counter_reader);
144 try self.lzma_state.process(allocator, counter_reader, writer, accum, &rangecoder);144 while (try self.lzma_state.process(allocator, counter_reader, writer, accum, &rangecoder) == .continue_) {}
145145
146 if (counter.bytes_read != packed_size) {146 if (counter.bytes_read != packed_size) {
147 return error.CorruptInput;147 return error.CorruptInput;