mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 22:04:21 +00:00
169 lines
4.6 KiB
Zig
169 lines
4.6 KiB
Zig
const std = @import("../../std.zig");
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const lzma = @import("../lzma.zig");
|
|
const DecoderState = lzma.decode.DecoderState;
|
|
const LzAccumBuffer = lzma.decode.lzbuffer.LzAccumBuffer;
|
|
const Properties = lzma.decode.Properties;
|
|
const RangeDecoder = lzma.decode.rangecoder.RangeDecoder;
|
|
|
|
pub const Decoder = struct {
|
|
lzma_state: DecoderState,
|
|
|
|
pub fn init(allocator: Allocator) !Decoder {
|
|
return Decoder{
|
|
.lzma_state = try DecoderState.init(
|
|
allocator,
|
|
Properties{
|
|
.lc = 0,
|
|
.lp = 0,
|
|
.pb = 0,
|
|
},
|
|
null,
|
|
),
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *Decoder, allocator: Allocator) void {
|
|
self.lzma_state.deinit(allocator);
|
|
self.* = undefined;
|
|
}
|
|
|
|
pub fn decompress(
|
|
self: *Decoder,
|
|
allocator: Allocator,
|
|
reader: anytype,
|
|
writer: anytype,
|
|
) !void {
|
|
var accum = LzAccumBuffer.init(std.math.maxInt(usize));
|
|
defer accum.deinit(allocator);
|
|
|
|
while (true) {
|
|
const status = try reader.readByte();
|
|
|
|
switch (status) {
|
|
0 => break,
|
|
1 => try parseUncompressed(allocator, reader, writer, &accum, true),
|
|
2 => try parseUncompressed(allocator, reader, writer, &accum, false),
|
|
else => try self.parseLzma(allocator, reader, writer, &accum, status),
|
|
}
|
|
}
|
|
|
|
try accum.finish(writer);
|
|
}
|
|
|
|
fn parseLzma(
|
|
self: *Decoder,
|
|
allocator: Allocator,
|
|
reader: anytype,
|
|
writer: anytype,
|
|
accum: *LzAccumBuffer,
|
|
status: u8,
|
|
) !void {
|
|
if (status & 0x80 == 0) {
|
|
return error.CorruptInput;
|
|
}
|
|
|
|
const Reset = struct {
|
|
dict: bool,
|
|
state: bool,
|
|
props: bool,
|
|
};
|
|
|
|
const reset = switch ((status >> 5) & 0x3) {
|
|
0 => Reset{
|
|
.dict = false,
|
|
.state = false,
|
|
.props = false,
|
|
},
|
|
1 => Reset{
|
|
.dict = false,
|
|
.state = true,
|
|
.props = false,
|
|
},
|
|
2 => Reset{
|
|
.dict = false,
|
|
.state = true,
|
|
.props = true,
|
|
},
|
|
3 => Reset{
|
|
.dict = true,
|
|
.state = true,
|
|
.props = true,
|
|
},
|
|
else => unreachable,
|
|
};
|
|
|
|
const unpacked_size = blk: {
|
|
var tmp: u64 = status & 0x1F;
|
|
tmp <<= 16;
|
|
tmp |= try reader.readIntBig(u16);
|
|
break :blk tmp + 1;
|
|
};
|
|
|
|
const packed_size = blk: {
|
|
const tmp: u17 = try reader.readIntBig(u16);
|
|
break :blk tmp + 1;
|
|
};
|
|
|
|
if (reset.dict) {
|
|
try accum.reset(writer);
|
|
}
|
|
|
|
if (reset.state) {
|
|
var new_props = self.lzma_state.lzma_props;
|
|
|
|
if (reset.props) {
|
|
var props = try reader.readByte();
|
|
if (props >= 225) {
|
|
return error.CorruptInput;
|
|
}
|
|
|
|
const lc = @intCast(u4, props % 9);
|
|
props /= 9;
|
|
const lp = @intCast(u3, props % 5);
|
|
props /= 5;
|
|
const pb = @intCast(u3, props);
|
|
|
|
if (lc + lp > 4) {
|
|
return error.CorruptInput;
|
|
}
|
|
|
|
new_props = Properties{ .lc = lc, .lp = lp, .pb = pb };
|
|
}
|
|
|
|
try self.lzma_state.resetState(allocator, new_props);
|
|
}
|
|
|
|
self.lzma_state.unpacked_size = unpacked_size + accum.len;
|
|
|
|
var counter = std.io.countingReader(reader);
|
|
const counter_reader = counter.reader();
|
|
|
|
var rangecoder = try RangeDecoder.init(counter_reader);
|
|
while (try self.lzma_state.process(allocator, counter_reader, writer, accum, &rangecoder) == .continue_) {}
|
|
|
|
if (counter.bytes_read != packed_size) {
|
|
return error.CorruptInput;
|
|
}
|
|
}
|
|
|
|
fn parseUncompressed(
|
|
allocator: Allocator,
|
|
reader: anytype,
|
|
writer: anytype,
|
|
accum: *LzAccumBuffer,
|
|
reset_dict: bool,
|
|
) !void {
|
|
const unpacked_size = @as(u17, try reader.readIntBig(u16)) + 1;
|
|
|
|
if (reset_dict) {
|
|
try accum.reset(writer);
|
|
}
|
|
|
|
var i: @TypeOf(unpacked_size) = 0;
|
|
while (i < unpacked_size) : (i += 1) {
|
|
try accum.appendByte(allocator, try reader.readByte());
|
|
}
|
|
}
|
|
};
|