| author | |
| committer | |
| log | 622a364715522b239eee57aa071399fa268fd372 |
| tree | fdeeb68767ec2039187c213ed5adceda57674304 |
| parent | e03d6c42ea8b65a3b283c4da8c9593b82762874c |
5 files changed, 97 insertions(+), 54 deletions(-)
lib/std/compress/lzma.zig+74-5| ... | @@ -1,4 +1,6 @@ | ... | @@ -1,4 +1,6 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | ||
| 3 | const mem = std.mem; | ||
| 2 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 3 | 5 | ||
| 4 | pub const decode = @import("lzma/decode.zig"); | 6 | pub 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"); |
| 6 | pub fn decompress( | 8 | pub 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 | |||
| 15 | pub 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 | |
| 24 | pub 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 | } |
| 17 | 86 | ||
| 18 | test { | 87 | test { |
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 | } |
| 292 | 292 | ||
| 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 | } |
| 297 | 298 | ||
| 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 | } |
| 304 | 307 | ||
| 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 | |||
| 378 | pub 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 { |
| 98 | 98 | ||
| 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 | } |
| 102 | 103 | ||
| 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 | } |
| 221 | 223 |
lib/std/compress/lzma/test.zig+9-7| ... | @@ -1,22 +1,24 @@ | ... | @@ -1,22 +1,24 @@ |
| 1 | const std = @import("../../std.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const lzma = @import("../lzma.zig"); | 2 | const lzma = @import("../lzma.zig"); |
| 3 | 3 | ||
| 4 | fn testDecompress(compressed: []const u8, writer: anytype) !void { | 4 | fn 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 | } |
| 9 | 12 | ||
| 10 | fn testDecompressEqual(expected: []const u8, compressed: []const u8) !void { | 13 | fn 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 | } |
| 17 | 19 | ||
| 18 | fn testDecompressError(expected: anyerror, compressed: []const u8) !void { | 20 | fn 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 | } |
| 21 | 23 | ||
| 22 | test "LZMA: decompress empty world" { | 24 | test "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(); |
| 142 | 142 | ||
| 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_) {} |
| 145 | 145 | ||
| 146 | if (counter.bytes_read != packed_size) { | 146 | if (counter.bytes_read != packed_size) { |
| 147 | return error.CorruptInput; | 147 | return error.CorruptInput; |