143 lines
4.3 KiB
Zig
143 lines
4.3 KiB
Zig
const std = @import("std");
|
|
|
|
const c = @import("../../c_libs.zig").c;
|
|
const Decompressor = @import("../../decomp.zig");
|
|
|
|
const Xz = @This();
|
|
|
|
streams: std.AutoHashMap(std.Thread.Id, c.lzma_stream),
|
|
|
|
interface: Decompressor,
|
|
|
|
err: ?Error = null,
|
|
|
|
pub fn init(alloc: std.mem.Allocator) !Xz {
|
|
return .{
|
|
.streams = try .init(alloc),
|
|
.interface = &.{
|
|
.alloc = alloc,
|
|
.vtable = .{ .decompress = decompress, .stateless = stateless },
|
|
},
|
|
};
|
|
}
|
|
pub fn deinit(self: *Xz) void {
|
|
var values = self.streams.valueIterator();
|
|
while (values.next()) |val| {
|
|
c.lzma_end(val);
|
|
}
|
|
self.streams.deinit();
|
|
}
|
|
|
|
fn getOrCreate(self: *Xz) !*c.lzma_stream {
|
|
const res = try self.streams.getOrPut(std.Thread.getCurrentId());
|
|
if (res.found_existing) return res.value_ptr;
|
|
res.value_ptr.* = .{
|
|
.alloc = .{
|
|
.alloc = lzmaAlloc,
|
|
.free = lzmaFree,
|
|
.@"opaque" = &self.interface.alloc,
|
|
},
|
|
};
|
|
return res.value_ptr;
|
|
}
|
|
|
|
fn decompress(decomp: *const Decompressor, in: []u8, out: []u8) Decompressor.Error!usize {
|
|
var self: *Xz = @fieldParentPtr("interface", decomp);
|
|
|
|
const stream = try self.getOrCreate();
|
|
stream.next_in = in.ptr;
|
|
stream.avail_in = in.len;
|
|
stream.next_out = out.ptr;
|
|
stream.avail_out = out.len;
|
|
var res = c.lzma_stream_decoder(stream, out.len, 0);
|
|
decodeResult(res) catch |err| {
|
|
self.err = err;
|
|
xzErrorToDecompError(err);
|
|
};
|
|
while (true) {
|
|
res = c.lzma_code(&stream, c.LZMA_RUN);
|
|
if (res == c.LZMA_OK) continue;
|
|
if (res == c.LZMA_STREAM_END) break;
|
|
decodeResult(res) catch |err| {
|
|
self.err = err;
|
|
xzErrorToDecompError(err);
|
|
};
|
|
}
|
|
return stream.total_out;
|
|
}
|
|
pub fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize {
|
|
var stream: c.lzma_stream = .{
|
|
.next_in = in.ptr,
|
|
.avail_in = in.len,
|
|
.next_out = out.ptr,
|
|
.avail_out = out.len,
|
|
.allocator = .{
|
|
.alloc = lzmaAlloc,
|
|
.free = lzmaFree,
|
|
.@"opaque" = &alloc,
|
|
},
|
|
};
|
|
var res = c.lzma_stream_decoder(&stream, out.len, 0);
|
|
decodeResult(res) catch |err| xzErrorToDecompError(err);
|
|
while (true) {
|
|
res = c.lzma_code(&stream, c.LZMA_RUN);
|
|
if (res == c.LZMA_OK) continue;
|
|
if (res == c.LZMA_STREAM_END) break;
|
|
decodeResult(res) catch |err| xzErrorToDecompError(err);
|
|
}
|
|
return stream.total_out;
|
|
}
|
|
|
|
inline fn decodeResult(res: c_uint) Error!void {
|
|
return switch (res) {
|
|
c.LZMA_OK => {},
|
|
c.LZMA_STREAM_END => {},
|
|
c.LZMA_NO_CHECK => {},
|
|
c.LZMA_UNSUPPORTED_CHECK => Error.UnsupportedCheck,
|
|
c.LZMA_MEM_ERROR => Error.OutOfMemory,
|
|
c.LZMA_MEMLIMIT_ERROR => Error.OutOfMemory,
|
|
c.LZMA_FORMAT_ERROR => Error.Format,
|
|
c.LZMA_OPTIONS_ERROR => Error.Options,
|
|
c.LZMA_DATA_ERROR => Error.Data,
|
|
c.LZMA_BUF_ERROR => Error.BufferExhausted,
|
|
c.LZMA_PROG_ERROR => Error.Programming,
|
|
c.LZMA_SEEK_NEEDED => Error.SeekNeeded,
|
|
else => Error.Unknown,
|
|
};
|
|
}
|
|
fn xzErrorToDecompError(err: Error) Decompressor.Error {
|
|
switch (err) {
|
|
Error.OutOfMemory => return err,
|
|
Error.UnsupportedCheck => return Decompressor.Error.ReadFailed,
|
|
Error.Format => return Decompressor.Error.ReadFailed,
|
|
Error.Options => return Decompressor.Error.ReadFailed,
|
|
Error.Data => return Decompressor.Error.ReadFailed,
|
|
Error.BufferExhausted => return Decompressor.Error.WriteFailed,
|
|
Error.Programming => return Decompressor.Error.ReadFailed,
|
|
Error.SeekNeeded => return Decompressor.Error.ReadFailed,
|
|
Error.Unknown => return Decompressor.Error.ReadFailed,
|
|
}
|
|
}
|
|
|
|
fn lzmaAlloc(ptr: ?*anyopaque, _: usize, size: usize) callconv(.c) ?*anyopaque {
|
|
var alloc: *std.mem.Allocator = @ptrCast(ptr);
|
|
return alloc.rawAlloc(size, .@"1", 0) catch return null;
|
|
}
|
|
fn lzmaFree(ptr: ?*anyopaque, alloc_ptr: ?*anyopaque) callconv(.c) void {
|
|
if (alloc_ptr == null) return;
|
|
var alloc: *std.mem.Allocator = @ptrCast(ptr);
|
|
alloc.rawFree(@ptrCast(alloc_ptr), .@"1", 0);
|
|
}
|
|
|
|
pub const Error = error{
|
|
OutOfMemory,
|
|
UnsupportedCheck,
|
|
Format,
|
|
Options,
|
|
Data,
|
|
BufferExhausted,
|
|
Programming,
|
|
SeekNeeded,
|
|
Unknown,
|
|
};
|