| 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 | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | |
| 3 | const mem = std.mem; | |
| 2 | 4 | const Allocator = std.mem.Allocator; |
| 3 | 5 | |
| 4 | 6 | pub const decode = @import("lzma/decode.zig"); |
| ... | ... | @@ -6,13 +8,80 @@ pub const decode = @import("lzma/decode.zig"); |
| 6 | 8 | pub fn decompress( |
| 7 | 9 | allocator: Allocator, |
| 8 | 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 | 18 | options: decode.Options, |
| 11 | ) !void { | |
| 19 | ) !Decompress(@TypeOf(reader)) { | |
| 12 | 20 | const params = try decode.Params.readHeader(reader, options); |
| 13 | var decoder = try decode.Decoder.init(allocator, params, options.memlimit); | |
| 14 | defer decoder.deinit(allocator); | |
| 15 | return decoder.decompress(allocator, reader, writer); | |
| 21 | return Decompress(@TypeOf(reader)).init(allocator, reader, params, options.memlimit); | |
| 22 | } | |
| 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 | 87 | test { |
lib/std/compress/lzma/decode.zig+11-41| ... | ... | @@ -280,26 +280,29 @@ pub const DecoderState = struct { |
| 280 | 280 | writer: anytype, |
| 281 | 281 | buffer: anytype, |
| 282 | 282 | decoder: *RangeDecoder, |
| 283 | ) !void { | |
| 284 | while (true) { | |
| 283 | ) !ProcessingStatus { | |
| 284 | process_next: { | |
| 285 | 285 | if (self.unpacked_size) |unpacked_size| { |
| 286 | 286 | if (buffer.len >= unpacked_size) { |
| 287 | break; | |
| 287 | break :process_next; | |
| 288 | 288 | } |
| 289 | 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) { | |
| 294 | break; | |
| 293 | switch (try self.processNext(allocator, reader, writer, buffer, decoder)) { | |
| 294 | .continue_ => return .continue_, | |
| 295 | .finished => break :process_next, | |
| 295 | 296 | } |
| 296 | 297 | } |
| 297 | 298 | |
| 298 | if (self.unpacked_size) |len| { | |
| 299 | if (len != buffer.len) { | |
| 299 | if (self.unpacked_size) |unpacked_size| { | |
| 300 | if (buffer.len != unpacked_size) { | |
| 300 | 301 | return error.CorruptInput; |
| 301 | 302 | } |
| 302 | 303 | } |
| 304 | ||
| 305 | return .finished; | |
| 303 | 306 | } |
| 304 | 307 | |
| 305 | 308 | fn decodeLiteral( |
| ... | ... | @@ -374,36 +377,3 @@ pub const DecoderState = struct { |
| 374 | 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 | 98 | |
| 99 | 99 | pub fn finish(self: *Self, writer: anytype) !void { |
| 100 | 100 | try writer.writeAll(self.buf.items); |
| 101 | self.buf.clearRetainingCapacity(); | |
| 101 | 102 | } |
| 102 | 103 | |
| 103 | 104 | pub fn deinit(self: *Self, allocator: Allocator) void { |
| ... | ... | @@ -216,6 +217,7 @@ pub const LzCircularBuffer = struct { |
| 216 | 217 | pub fn finish(self: *Self, writer: anytype) !void { |
| 217 | 218 | if (self.cursor > 0) { |
| 218 | 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 | 1 | const std = @import("../../std.zig"); |
| 2 | 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 | 5 | const allocator = std.testing.allocator; |
| 6 | 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 | 13 | fn testDecompressEqual(expected: []const u8, compressed: []const u8) !void { |
| 11 | 14 | const allocator = std.testing.allocator; |
| 12 | var decomp = std.ArrayList(u8).init(allocator); | |
| 13 | defer decomp.deinit(); | |
| 14 | try testDecompress(compressed, decomp.writer()); | |
| 15 | try std.testing.expectEqualSlices(u8, expected, decomp.items); | |
| 15 | const decomp = try testDecompress(compressed); | |
| 16 | defer allocator.free(decomp); | |
| 17 | try std.testing.expectEqualSlices(u8, expected, decomp); | |
| 16 | 18 | } |
| 17 | 19 | |
| 18 | 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 | 24 | test "LZMA: decompress empty world" { |
lib/std/compress/lzma2/decode.zig+1-1| ... | ... | @@ -141,7 +141,7 @@ pub const Decoder = struct { |
| 141 | 141 | const counter_reader = counter.reader(); |
| 142 | 142 | |
| 143 | 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 | 146 | if (counter.bytes_read != packed_size) { |
| 147 | 147 | return error.CorruptInput; |