From 4b2b7021c749f5e589cca856ce653c05581cde96 Mon Sep 17 00:00:00 2001 From: "Caleb J. Gardner" Date: Thu, 2 Apr 2026 06:27:34 -0500 Subject: [PATCH] Moved & organized decompression Fully implemented Decompressor vtable --- src/archive.zig | 32 ++---- src/{decomp => }/c.zig | 6 -- src/decomp/c/lz4.zig | 14 +++ src/decomp/c/lzma.zig | 127 ++++++++++++++++++++++ src/decomp/c/lzo.zig | 67 ++++++++++++ src/decomp/c/xz.zig | 127 ++++++++++++++++++++++ src/decomp/c/zlib.zig | 112 ++++++++++++++++++++ src/decomp/c/zstd.zig | 165 +++++++++++++++++++++++++++++ src/decomp/gzip.zig | 114 -------------------- src/decomp/lzma.zig | 146 -------------------------- src/decomp/misc_c.zig | 43 -------- src/decomp/types.zig | 43 ++++++++ src/decomp/zig/lzma.zig | 19 ++++ src/decomp/zig/xz.zig | 25 +++++ src/decomp/zig/zlib.zig | 18 ++++ src/decomp/zig/zstd.zig | 18 ++++ src/decomp/zig_decomp.zig | 51 --------- src/decomp/zstd.zig | 216 -------------------------------------- src/tables.zig | 5 +- src/xattr.zig | 6 +- 20 files changed, 749 insertions(+), 605 deletions(-) rename src/{decomp => }/c.zig (60%) create mode 100644 src/decomp/c/lz4.zig create mode 100644 src/decomp/c/lzma.zig create mode 100644 src/decomp/c/lzo.zig create mode 100644 src/decomp/c/xz.zig create mode 100644 src/decomp/c/zlib.zig create mode 100644 src/decomp/c/zstd.zig delete mode 100644 src/decomp/gzip.zig delete mode 100644 src/decomp/lzma.zig delete mode 100644 src/decomp/misc_c.zig create mode 100644 src/decomp/types.zig create mode 100644 src/decomp/zig/lzma.zig create mode 100644 src/decomp/zig/xz.zig create mode 100644 src/decomp/zig/zlib.zig create mode 100644 src/decomp/zig/zstd.zig delete mode 100644 src/decomp/zig_decomp.zig delete mode 100644 src/decomp/zstd.zig diff --git a/src/archive.zig b/src/archive.zig index 010f3dc..bce92e8 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -30,24 +30,7 @@ super: Superblock, tables: ?Tables = null, -decomp: if (config.use_zig_decomp) Decomp.DecompFn else union(enum) { - gzip: @import("decomp/gzip.zig"), - lzma: @import("decomp/lzma.zig"), - lzo: void, - xz: @import("decomp/lzma.zig"), - lz4: void, - zstd: @import("decomp/zstd.zig"), - - pub fn decomp(self: @This(), in: []u8, out: []u8) !usize { - return switch (self) { - .gzip => |*g| g.decompress(in, out), - .zstd => |*z| z.decompress(in, out), - .lzma, .xz => |*d| d.decompress(in, out), - .lz4 => cDecomp.lz4(in, out), - .lzo => cDecomp.lzo(in, out), - }; - } -}, +decomp: @import("decomp/types.zig").Decomp, /// Default settings using std.Thread.getCpuCount() threads and the minimum of 4gb or half of system memory for memory usage. pub fn init(alloc: std.mem.Allocator, fil: File, offset: u64) !Archive { @@ -63,16 +46,16 @@ pub fn init(alloc: std.mem.Allocator, fil: File, offset: u64) !Archive { switch (super.compression) { .lz4 => return error.Lz4Unsupported, .lzo => return error.LzoUnsupported, - .gzip => Decomp.gzip, - .lzma => Decomp.lzma, - .xz => Decomp.xz, - .zstd => Decomp.zstd, + .gzip => .{ .gzip = .{} }, + .lzma => .{ .lzma = .{} }, + .xz => .{ .xz = .{} }, + .zstd => .{ .zstd = .{} }, } else switch (super.compression) { .gzip => .{ .gzip = .init(alloc) }, .zstd => .{ .zstd = .init(alloc) }, - .xz => .{ .xz = .init(alloc, true) }, - .lzma => .{ .lzma = .init(alloc, false) }, + .xz => .{ .xz = .init(alloc) }, + .lzma => .{ .lzma = .init(alloc) }, .lzo => .{ .lzo = .{} }, .lz4 => .{ .lz4 = .{} }, }, @@ -82,6 +65,7 @@ pub fn init(alloc: std.mem.Allocator, fil: File, offset: u64) !Archive { pub fn deinit(self: *Archive) void { if (self.tables != null) self.tables.?.deinit(); + self.decomp.deinit(); } pub fn inode(self: *Archive, alloc: std.mem.Allocator, num: u32) !Inode { diff --git a/src/decomp/c.zig b/src/c.zig similarity index 60% rename from src/decomp/c.zig rename to src/c.zig index d6acdbd..42f23c9 100644 --- a/src/decomp/c.zig +++ b/src/c.zig @@ -1,9 +1,3 @@ -const std = @import("std"); -const Reader = std.Io.Reader; -const builtin = @import("builtin"); - -const config = @import("config"); - pub const c = @cImport({ @cInclude("zlib-ng.h"); @cInclude("lzma.h"); diff --git a/src/decomp/c/lz4.zig b/src/decomp/c/lz4.zig new file mode 100644 index 0000000..d230e62 --- /dev/null +++ b/src/decomp/c/lz4.zig @@ -0,0 +1,14 @@ +const std = @import("std"); + +const c = @import("../../c.zig").c; +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, + +fn stateless(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + const res = c.LZ4_decompress_fast(in.ptr, out.ptr, @intCast(out.len)); + if (res < 0) return Decompressor.Error.ReadFailed; + return @abs(res); +} diff --git a/src/decomp/c/lzma.zig b/src/decomp/c/lzma.zig new file mode 100644 index 0000000..e09e0bf --- /dev/null +++ b/src/decomp/c/lzma.zig @@ -0,0 +1,127 @@ +const std = @import("std"); + +const c = @import("../../c.zig").c; +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +alloc: std.mem.Allocator, +streams: std.AutoHashMap(std.Thread.Id, c.lzma_stream), + +interface: Decompressor, + +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .alloc = alloc, + .streams = .init(alloc), + .interface = .{ + .vtable = &.{ + .decompress = decompress, + .stateless = stateless, + }, + }, + }; +} +pub fn deinit(self: *Self) void { + self.streams.deinit(); +} + +fn decompress(decomp: *Decompressor, in: []u8, out: []u8) Decompressor.Error!usize { + var self: *Self = @fieldParentPtr("interface", decomp); + + var strm = try self.getOrCreate(); + strm.next_in = in.ptr; + strm.avail_in = in.len; + strm.next_out = out.ptr; + strm.avail_out = out.len; + var res = c.lzma_alone_decoder(strm, out.len * 2); + decodeResult(res) catch |err| return lzmaErrToDecompErr(err); + while (res == c.LZMA_OK) + res = c.lzma_code(strm, c.LZMA_RUN); + decodeResult(res) catch |err| return lzmaErrToDecompErr(err); + return strm.total_out; +} +fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + var strm: c.lzma_stream = .{ + .allocator = &.{ + .alloc = lzmaAlloc, + .free = lzmaFree, + .@"opaque" = @ptrCast(@constCast(&alloc)), + }, + .next_in = in.ptr, + .avail_in = in.len, + .next_out = out.ptr, + .avail_out = out.len, + }; + var res = c.lzma_alone_decoder(&strm, out.len * 2); + decodeResult(res) catch |err| return lzmaErrToDecompErr(err); + while (res == c.LZMA_OK) + res = c.lzma_code(&strm, c.LZMA_RUN); + decodeResult(res) catch |err| return lzmaErrToDecompErr(err); + return strm.total_out; +} + +inline fn getOrCreate(self: *Self) !*c.lzma_stream { + const res = try self.streams.getOrPut(std.Thread.getCurrentId()); + if (res.found_existing) return res.value_ptr; + res.value_ptr.* = .{ .allocator = &.{ + .alloc = lzmaAlloc, + .free = lzmaFree, + .@"opaque" = @ptrCast(&self.alloc), + } }; + return res.value_ptr; +} +inline fn decodeResult(res: usize) Error!void { + return switch (res) { + c.LZMA_OK, c.LZMA_STREAM_END => {}, + c.LZMA_NO_CHECK => Error.NoCheck, + c.LZMA_UNSUPPORTED_CHECK => Error.UnsupportedCheck, + c.LZMA_GET_CHECK => Error.GetCheck, + c.LZMA_MEM_ERROR, 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.Buffer, + c.LZMA_PROG_ERROR => Error.Program, + c.LZMA_SEEK_NEEDED => Error.SeekNeeded, + else => Error.Unknown, + }; +} +inline fn lzmaErrToDecompErr(err: Error) Decompressor.Error { + return switch (err) { + Error.OutOfMemory => Decompressor.Error.OutOfMemory, + Error.NoCheck => Decompressor.Error.ReadFailed, + Error.UnsupportedCheck => Decompressor.Error.ReadFailed, + Error.GetCheck => Decompressor.Error.ReadFailed, + Error.Format => Decompressor.Error.ReadFailed, + Error.Options => Decompressor.Error.ReadFailed, + Error.Data => Decompressor.Error.ReadFailed, + Error.Buffer => Decompressor.Error.WriteFailed, + Error.Program => Decompressor.Error.ReadFailed, + Error.SeekNeeded => Decompressor.Error.ReadFailed, + else => Decompressor.Error.ReadFailed, + }; +} + +fn lzmaAlloc(ptr: ?*anyopaque, size: usize, _: usize) callconv(.c) ?*anyopaque { + var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr))); + return alloc.rawAlloc(size, .@"1", 0); +} +fn lzmaFree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void { + var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr))); + alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0); +} + +const Error = error{ + OutOfMemory, + NoCheck, + UnsupportedCheck, + GetCheck, + Format, + Options, + Data, + Buffer, + Program, + SeekNeeded, + Unknown, +}; diff --git a/src/decomp/c/lzo.zig b/src/decomp/c/lzo.zig new file mode 100644 index 0000000..4e4977f --- /dev/null +++ b/src/decomp/c/lzo.zig @@ -0,0 +1,67 @@ +const std = @import("std"); + +const c = @import("../../c.zig").c; +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, + +fn stateless(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + var out_len: usize = out.len; + const res = c.lzo1x_decompress(in.ptr, in.len, out.ptr, &out_len, null); + decodeError(res) catch |err| return lzoErrToDecompErr(err); + return out_len; +} + +inline fn decodeError(res: c_int) Error!void { + return switch (res) { + c.LZO_E_OK => {}, + c.LZO_E_EOF_NOT_FOUND => Error.EofNotFound, + c.LZO_E_INPUT_NOT_CONSUMED => Error.InputNotConsumed, + c.LZO_E_INPUT_OVERRUN => Error.InputOverrun, + c.LZO_E_INTERNAL_ERROR => Error.InternalError, + c.LZO_E_INVALID_ALIGNMENT => Error.InvalidAlignment, + c.LZO_E_INVALID_ARGUMENT => Error.InvalidArgument, + c.LZO_E_LOOKBEHIND_OVERRUN => Error.LookbehindOverrun, + c.LZO_E_NOT_COMPRESSIBLE => Error.NotCompressible, + c.LZO_E_NOT_YET_IMPLEMENTED => Error.NotYetImplemented, + c.LZO_E_OUTPUT_NOT_CONSUMED => Error.OutputNotConsumed, + c.LZO_E_OUTPUT_OVERRUN => Error.OutputOverrun, + c.LZO_E_OUT_OF_MEMORY => Error.OutOfMemory, + else => Error.Unknown, + }; +} +inline fn lzoErrToDecompErr(err: Error) Decompressor.Error { + return switch (err) { + Error.EofNotFound => Decompressor.Error.ReadFailed, + Error.InputNotConsumed => Decompressor.Error.ReadFailed, + Error.InputOverrun => Decompressor.Error.ReadFailed, + Error.InternalError => Decompressor.Error.ReadFailed, + Error.InvalidAlignment => Decompressor.Error.ReadFailed, + Error.InvalidArgument => Decompressor.Error.ReadFailed, + Error.LookbehindOverrun => Decompressor.Error.ReadFailed, + Error.NotCompressible => Decompressor.Error.ReadFailed, + Error.NotYetImplemented => Decompressor.Error.ReadFailed, + Error.OutputNotConsumed => Decompressor.Error.WriteFailed, + Error.OutputOverrun => Decompressor.Error.WriteFailed, + Error.OutOfMemory => Decompressor.Error.OutOfMemory, + else => Decompressor.Error.ReadFailed, + }; +} + +const Error = error{ + EofNotFound, + InputNotConsumed, + InputOverrun, + InternalError, + InvalidAlignment, + InvalidArgument, + LookbehindOverrun, + NotCompressible, + NotYetImplemented, + OutputNotConsumed, + OutputOverrun, + OutOfMemory, + Unknown, +}; diff --git a/src/decomp/c/xz.zig b/src/decomp/c/xz.zig new file mode 100644 index 0000000..e1c3d21 --- /dev/null +++ b/src/decomp/c/xz.zig @@ -0,0 +1,127 @@ +const std = @import("std"); + +const c = @import("../../c.zig").c; +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +alloc: std.mem.Allocator, +streams: std.AutoHashMap(std.Thread.Id, c.lzma_stream), + +interface: Decompressor, + +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .alloc = alloc, + .streams = .init(alloc), + .interface = .{ + .vtable = &.{ + .decompress = decompress, + .stateless = stateless, + }, + }, + }; +} +pub fn deinit(self: *Self) void { + self.streams.deinit(); +} + +fn decompress(decomp: *Decompressor, in: []u8, out: []u8) Decompressor.Error!usize { + var self: *Self = @fieldParentPtr("interface", decomp); + + var strm = try self.getOrCreate(); + strm.next_in = in.ptr; + strm.avail_in = in.len; + strm.next_out = out.ptr; + strm.avail_out = out.len; + var res = c.lzma_stream_decoder(strm, out.len * 2, 0); + decodeResult(res) catch |err| return lzmaErrToDecompErr(err); + while (res == c.LZMA_OK) + res = c.lzma_code(strm, c.LZMA_RUN); + decodeResult(res) catch |err| return lzmaErrToDecompErr(err); + return strm.total_out; +} +fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + var strm: c.lzma_stream = .{ + .allocator = &.{ + .alloc = lzmaAlloc, + .free = lzmaFree, + .@"opaque" = @ptrCast(@constCast(&alloc)), + }, + .next_in = in.ptr, + .avail_in = in.len, + .next_out = out.ptr, + .avail_out = out.len, + }; + var res = c.lzma_stream_decoder(&strm, out.len * 2, 0); + decodeResult(res) catch |err| return lzmaErrToDecompErr(err); + while (res == c.LZMA_OK) + res = c.lzma_code(&strm, c.LZMA_RUN); + decodeResult(res) catch |err| return lzmaErrToDecompErr(err); + return strm.total_out; +} + +inline fn getOrCreate(self: *Self) !*c.lzma_stream { + const res = try self.streams.getOrPut(std.Thread.getCurrentId()); + if (res.found_existing) return res.value_ptr; + res.value_ptr.* = .{ .allocator = &.{ + .alloc = lzmaAlloc, + .free = lzmaFree, + .@"opaque" = @ptrCast(&self.alloc), + } }; + return res.value_ptr; +} +inline fn decodeResult(res: usize) Error!void { + return switch (res) { + c.LZMA_OK, c.LZMA_STREAM_END => {}, + c.LZMA_NO_CHECK => Error.NoCheck, + c.LZMA_UNSUPPORTED_CHECK => Error.UnsupportedCheck, + c.LZMA_GET_CHECK => Error.GetCheck, + c.LZMA_MEM_ERROR, 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.Buffer, + c.LZMA_PROG_ERROR => Error.Program, + c.LZMA_SEEK_NEEDED => Error.SeekNeeded, + else => Error.Unknown, + }; +} +inline fn lzmaErrToDecompErr(err: Error) Decompressor.Error { + return switch (err) { + Error.OutOfMemory => Decompressor.Error.OutOfMemory, + Error.NoCheck => Decompressor.Error.ReadFailed, + Error.UnsupportedCheck => Decompressor.Error.ReadFailed, + Error.GetCheck => Decompressor.Error.ReadFailed, + Error.Format => Decompressor.Error.ReadFailed, + Error.Options => Decompressor.Error.ReadFailed, + Error.Data => Decompressor.Error.ReadFailed, + Error.Buffer => Decompressor.Error.WriteFailed, + Error.Program => Decompressor.Error.ReadFailed, + Error.SeekNeeded => Decompressor.Error.ReadFailed, + else => Decompressor.Error.ReadFailed, + }; +} + +fn lzmaAlloc(ptr: ?*anyopaque, size: usize, _: usize) callconv(.c) ?*anyopaque { + var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr))); + return alloc.rawAlloc(size, .@"1", 0); +} +fn lzmaFree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void { + var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr))); + alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0); +} + +const Error = error{ + OutOfMemory, + NoCheck, + UnsupportedCheck, + GetCheck, + Format, + Options, + Data, + Buffer, + Program, + SeekNeeded, + Unknown, +}; diff --git a/src/decomp/c/zlib.zig b/src/decomp/c/zlib.zig new file mode 100644 index 0000000..52edc10 --- /dev/null +++ b/src/decomp/c/zlib.zig @@ -0,0 +1,112 @@ +const std = @import("std"); + +const c = @import("../../c.zig").c; +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +alloc: std.mem.Allocator, +streams: std.AutoHashMap(std.Thread.Id, c.zng_stream), + +interface: Decompressor, + +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .alloc = alloc, + .streams = .init(alloc), + .interface = .{ + .vtable = &.{ + .decompress = decompress, + .stateless = stateless, + }, + }, + }; +} +pub fn deinit(self: *Self) void { + self.streams.deinit(); +} + +fn decompress(decomp: *Decompressor, in: []u8, out: []u8) Decompressor.Error!usize { + const self: *Self = @fieldParentPtr("interface", decomp); + + var strm = try self.getOrCreate(); + strm.next_in = in.ptr; + strm.avail_in = @truncate(in.len); + strm.next_out = out.ptr; + strm.total_out = out.len; + var res = c.zng_inflateReset(strm); + decodeError(res) catch |err| return zlibErrToDecompErr(err); + + res = c.zng_inflate(strm, c.Z_FULL_FLUSH); + decodeError(res) catch |err| return zlibErrToDecompErr(err); + return strm.total_out; +} +fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + var strm: c.zng_stream = .{ + .zalloc = zalloc, + .zfree = zfree, + .@"opaque" = @constCast(&alloc), + + .next_in = in.ptr, + .avail_in = @truncate(in.len), + .next_out = out.ptr, + .total_out = out.len, + }; + var res = c.zng_inflateInit(&strm); + decodeError(res) catch |err| return zlibErrToDecompErr(err); + + res = c.zng_inflate(&strm, c.Z_FULL_FLUSH); + decodeError(res) catch |err| return zlibErrToDecompErr(err); + return strm.total_out; +} + +fn getOrCreate(self: *Self) !*c.zng_stream { + const res = try self.streams.getOrPut(std.Thread.getCurrentId()); + if (res.found_existing) return res.value_ptr; + res.value_ptr.* = .{ + .zalloc = zalloc, + .zfree = zfree, + .@"opaque" = &self.alloc, + }; + return res.value_ptr; +} + +fn zalloc(ptr: ?*anyopaque, size: c_uint, len: c_uint) callconv(.c) ?*anyopaque { + var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr))); + return alloc.rawAlloc(size * len, .@"1", 0); +} +fn zfree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void { + var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr))); + alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0); +} + +inline fn decodeError(res: i32) Error!void { + if (res >= 0) return; + return switch (res) { + c.Z_STREAM_ERROR => Error.Stream, + c.Z_DATA_ERROR => Error.Data, + c.Z_MEM_ERROR => Error.OutOfMemory, + c.Z_BUF_ERROR => Error.Buffer, + c.Z_VERSION_ERROR => Error.Version, + else => Error.Misc, + }; +} +inline fn zlibErrToDecompErr(err: Error) Decompressor.Error { + return switch (err) { + Error.OutOfMemory => Decompressor.Error.OutOfMemory, + Error.Misc => Decompressor.Error.ReadFailed, + Error.Stream => Decompressor.Error.ReadFailed, + Error.Data => Decompressor.Error.ReadFailed, + Error.Buffer => Decompressor.Error.WriteFailed, + Error.Version => Decompressor.Error.ReadFailed, + }; +} + +const Error = error{ + OutOfMemory, + Misc, + Stream, + Data, + Buffer, + Version, +}; diff --git a/src/decomp/c/zstd.zig b/src/decomp/c/zstd.zig new file mode 100644 index 0000000..3d3c119 --- /dev/null +++ b/src/decomp/c/zstd.zig @@ -0,0 +1,165 @@ +const std = @import("std"); + +const c = @import("../../c.zig").c; +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +alloc: std.mem.Allocator, +ctx: std.AutoHashMap(std.Thread.Id, ?*c.ZSTD_DCtx), + +interface: Decompressor, + +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .alloc = alloc, + .ctx = .init(alloc), + .interface = .{ + .vtable = &.{ + .decompress = decompress, + .stateless = stateless, + }, + }, + }; +} +pub fn deinit(self: *Self) void { + self.ctx.deinit(); +} + +fn decompress(decomp: *Decompressor, in: []u8, out: []u8) Decompressor.Error!usize { + var self: *Self = @fieldParentPtr("interface", decomp); + + const ctx = try self.getOrCreate(); + const res = c.ZSTD_decompressDCtx(ctx, out.ptr, out.len, in.ptr, in.len); + decodeError(res) catch |err| return zstdErrToDecompErr(err); + return res; +} +fn stateless(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + const res = c.ZSTD_decompress(out.ptr, out.len, in.ptr, in.len); + decodeError(res) catch |err| return zstdErrToDecompErr(err); + return res; +} + +inline fn getOrCreate(self: *Self) !?*c.ZSTD_DCtx { + const res = try self.ctx.getOrPut(std.Thread.getCurrentId()); + if (res.found_existing) return res.value_ptr.*; + res.value_ptr.* = c.ZSTD_createDCtx(); + return res.value_ptr.*; +} + +inline fn decodeError(res: usize) Error!void { + if (c.ZSTD_isError(res) == 0) return; + return switch (c.ZSTD_getErrorCode(res)) { + c.ZSTD_error_prefix_unknown => Error.PrefixUnknown, + c.ZSTD_error_version_unsupported => Error.VersionUnsupported, + c.ZSTD_error_frameParameter_unsupported => Error.FrameParameterUnsupported, + c.ZSTD_error_frameParameter_windowTooLarge => Error.FrameParameterWindowTooLarge, + c.ZSTD_error_corruption_detected => Error.CorruptionDetected, + c.ZSTD_error_checksum_wrong => Error.ChecksumWrong, + c.ZSTD_error_literals_headerWrong => Error.LiteralsHeaderWrong, + c.ZSTD_error_dictionary_corrupted => Error.DictionaryCorrupted, + c.ZSTD_error_dictionary_wrong => Error.DictionaryWrong, + c.ZSTD_error_dictionaryCreation_failed => Error.DictionaryCreationFailed, + c.ZSTD_error_parameter_unsupported => Error.ParameterUnsupported, + c.ZSTD_error_parameter_combination_unsupported => Error.ParameterCombinationUnsupported, + c.ZSTD_error_parameter_outOfBound => Error.ParameterOutOfBound, + c.ZSTD_error_tableLog_tooLarge => Error.TableLogTooLarge, + c.ZSTD_error_maxSymbolValue_tooLarge => Error.MaxSymbolValueTooLarge, + c.ZSTD_error_maxSymbolValue_tooSmall => Error.MaxSymbolValueTooSmall, + c.ZSTD_error_cannotProduce_uncompressedBlock => Error.CannotProduceUncompressedBlock, + c.ZSTD_error_stabilityCondition_notRespected => Error.StabilityConditionNotRespected, + c.ZSTD_error_stage_wrong => Error.StageWrong, + c.ZSTD_error_init_missing => Error.InitMissing, + c.ZSTD_error_memory_allocation => Error.MemoryAllocation, + c.ZSTD_error_workSpace_tooSmall => Error.WorkSpaceTooSmall, + c.ZSTD_error_dstSize_tooSmall => Error.DstSizeTooSmall, + c.ZSTD_error_srcSize_wrong => Error.SrcSizeWrong, + c.ZSTD_error_dstBuffer_null => Error.DstBufferNull, + c.ZSTD_error_noForwardProgress_destFull => Error.NoForwardProgressDestFull, + c.ZSTD_error_noForwardProgress_inputEmpty => Error.NoForwardProgressInputEmpty, + c.ZSTD_error_frameIndex_tooLarge => Error.FrameIndexTooLarge, + c.ZSTD_error_seekableIO => Error.SeekableIo, + c.ZSTD_error_dstBuffer_wrong => Error.DstBufferWrong, + c.ZSTD_error_srcBuffer_wrong => Error.SrcBufferWrong, + c.ZSTD_error_sequenceProducer_failed => Error.SequenceProducerFailed, + c.ZSTD_error_externalSequences_invalid => Error.ExternalSequencesInvalid, + else => Error.Generic, + }; +} +inline fn zstdErrToDecompErr(err: Error) Decompressor.Error { + return switch (err) { + Error.OutOfMemory => Decompressor.Error.OutOfMemory, + Error.Generic => Decompressor.Error.ReadFailed, + Error.PrefixUnknown => Decompressor.Error.ReadFailed, + Error.VersionUnsupported => Decompressor.Error.ReadFailed, + Error.FrameParameterUnsupported => Decompressor.Error.ReadFailed, + Error.FrameParameterWindowTooLarge => Decompressor.Error.ReadFailed, + Error.CorruptionDetected => Decompressor.Error.ReadFailed, + Error.ChecksumWrong => Decompressor.Error.ReadFailed, + Error.LiteralsHeaderWrong => Decompressor.Error.ReadFailed, + Error.DictionaryCorrupted => Decompressor.Error.ReadFailed, + Error.DictionaryWrong => Decompressor.Error.ReadFailed, + Error.DictionaryCreationFailed => Decompressor.Error.ReadFailed, + Error.ParameterUnsupported => Decompressor.Error.ReadFailed, + Error.ParameterCombinationUnsupported => Decompressor.Error.ReadFailed, + Error.ParameterOutOfBound => Decompressor.Error.ReadFailed, + Error.TableLogTooLarge => Decompressor.Error.ReadFailed, + Error.MaxSymbolValueTooLarge => Decompressor.Error.ReadFailed, + Error.MaxSymbolValueTooSmall => Decompressor.Error.ReadFailed, + Error.CannotProduceUncompressedBlock => Decompressor.Error.ReadFailed, + Error.StabilityConditionNotRespected => Decompressor.Error.ReadFailed, + Error.StageWrong => Decompressor.Error.ReadFailed, + Error.InitMissing => Decompressor.Error.ReadFailed, + Error.MemoryAllocation => Decompressor.Error.OutOfMemory, + Error.WorkSpaceTooSmall => Decompressor.Error.WriteFailed, + Error.DstSizeTooSmall => Decompressor.Error.WriteFailed, + Error.SrcSizeWrong => Decompressor.Error.ReadFailed, + Error.DstBufferNull => Decompressor.Error.WriteFailed, + Error.NoForwardProgressDestFull => Decompressor.Error.WriteFailed, + Error.NoForwardProgressInputEmpty => Decompressor.Error.ReadFailed, + Error.FrameIndexTooLarge => Decompressor.Error.ReadFailed, + Error.SeekableIo => Decompressor.Error.ReadFailed, + Error.DstBufferWrong => Decompressor.Error.WriteFailed, + Error.SrcBufferWrong => Decompressor.Error.ReadFailed, + Error.SequenceProducerFailed => Decompressor.Error.ReadFailed, + Error.ExternalSequencesInvalid => Decompressor.Error.ReadFailed, + }; +} + +const Error = error{ + OutOfMemory, + Generic, + PrefixUnknown, + VersionUnsupported, + FrameParameterUnsupported, + FrameParameterWindowTooLarge, + CorruptionDetected, + ChecksumWrong, + LiteralsHeaderWrong, + DictionaryCorrupted, + DictionaryWrong, + DictionaryCreationFailed, + ParameterUnsupported, + ParameterCombinationUnsupported, + ParameterOutOfBound, + TableLogTooLarge, + MaxSymbolValueTooLarge, + MaxSymbolValueTooSmall, + CannotProduceUncompressedBlock, + StabilityConditionNotRespected, + StageWrong, + InitMissing, + MemoryAllocation, + WorkSpaceTooSmall, + DstSizeTooSmall, + SrcSizeWrong, + DstBufferNull, + NoForwardProgressDestFull, + NoForwardProgressInputEmpty, + FrameIndexTooLarge, + SeekableIo, + DstBufferWrong, + SrcBufferWrong, + SequenceProducerFailed, + ExternalSequencesInvalid, +}; diff --git a/src/decomp/gzip.zig b/src/decomp/gzip.zig deleted file mode 100644 index c87301b..0000000 --- a/src/decomp/gzip.zig +++ /dev/null @@ -1,114 +0,0 @@ -const std = @import("std"); -const Reader = std.Io.Reader; -const builtin = @import("builtin"); - -const Decompressor = @import("../decomp.zig"); -const c = @import("c.zig").c; -const zng_stream = c.zng_stream; - -const ZlibErrors = error{ - OutOfMemory, - OutputBufferTooSmall, - BadData, - StreamError, - Unknown, -}; - -const Self = @This(); - -alloc: std.mem.Allocator, - -streams: std.AutoHashMap(std.Thread.Id, zng_stream), -interface: Decompressor = .{ .vtable = &.{ - .decompress = decompress, - .stateless = stateless, -} }, - -err: ?ZlibErrors = null, - -pub fn init(alloc: std.mem.Allocator) !Self { - return .{ - .alloc = alloc, - .streams = .init(alloc), - }; -} -pub fn deinit(self: Self) void { - var iter = self.streams.keyIterator(); - while (iter.next()) |key| { - _ = c.inflateEnd(self.streams.getPtr(key).?); - } - self.streams.deinit(self.alloc); -} - -fn decompress(decomp: *Decompressor, in: []u8, out: []u8) Decompressor.Error!usize { - var self: *Self = @fieldParentPtr("interface", decomp); - - var stream = try self.getOrCreate(); - stream.next_in = in.ptr; - stream.avail_in = @truncate(in.len); - stream.next_out = out.ptr; - stream.avail_out = @truncate(out.len); - var res = c.zng_inflateReset(stream); - switch (res) { - c.Z_OK => {}, - c.Z_STREAM_ERROR => { - self.err = ZlibErrors.StreamError; - return Decompressor.Error.BadInput; - }, - else => { - self.err = ZlibErrors.Unknown; - return Decompressor.Error.BadInput; - }, - } - res = c.zng_inflate(stream, c.Z_FINISH); - switch (res) { - c.Z_OK => return stream.total_out, - c.Z_MEM_ERROR => { - self.err = ZlibErrors.OutOfMemory; - return Decompressor.Error.OutOfMemory; - }, - c.Z_BUF_ERROR => { - self.err = ZlibErrors.OutputBufferTooSmall; - return Decompressor.Error.OutputTooSmall; - }, - c.Z_DATA_ERROR => { - self.err = ZlibErrors.BadData; - return Decompressor.Error.BadInput; - }, - else => { - self.err = ZlibErrors.Unknown; - return Decompressor.Error.BadInput; - }, - } -} -inline fn getOrCreate(self: *Self) Decompressor.Error!*zng_stream { - const res = try self.streams.getOrPut(std.Thread.getCurrentId()); - if (res.found_existing) return res.value_ptr; - res.value_ptr.* = .{ - .zalloc = zalloc, - .zfree = zfree, - .@"opaque" = @ptrCast(self), - }; - return res.value_ptr; -} - -fn zalloc(self_ptr: ?*anyopaque, items: c_uint, size: c_uint) callconv(.c) ?*anyopaque { - var self: *Self = @ptrCast(@alignCast(self_ptr)); - return self.alloc.rawAlloc(items * size, .@"1", 0); -} -fn zfree(self_ptr: ?*anyopaque, alloc_ptr: ?*anyopaque) callconv(.c) void { - var self: *Self = @ptrCast(@alignCast(self_ptr)); - self.alloc.rawFree(@ptrCast(alloc_ptr), .@"1", 0); -} - -fn stateless(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { - var out_len = out.len; - const res = c.zng_uncompress(out.ptr, &out_len, in.ptr, in.len); - return switch (res) { - c.Z_OK => out_len, - c.Z_MEM_ERROR => Decompressor.Error.OutOfMemory, - c.Z_BUF_ERROR => Decompressor.Error.OutputTooSmall, - c.Z_DATA_ERROR => Decompressor.Error.BadInput, - else => Decompressor.Error.BadInput, - }; -} diff --git a/src/decomp/lzma.zig b/src/decomp/lzma.zig deleted file mode 100644 index 4fbab20..0000000 --- a/src/decomp/lzma.zig +++ /dev/null @@ -1,146 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -const Decompressor = @import("../decomp.zig"); -const c = @import("c.zig").c; -const stream = c.lzma_stream; - -const Self = @This(); - -alloc: std.mem.Allocator, -streams: std.AutoHashMap(std.Thread.Id, stream), - -xz: bool, -interface: Decompressor = .{ .vtable = &.{ - .decompress = decompress, - .stateless = stateless, -} }, - -err: ?LzmaError = null, - -pub fn init(alloc: std.mem.Allocator, xz: bool) !Self { - return .{ - .alloc = alloc, - .streams = .init(alloc), - .xz = xz, - }; -} -pub fn deinit(self: Self) void { - var iter = self.streams.keyIterator(); - while (iter.next()) |key| - c.lzma_end(self.streams.getPtr(key)); - self.streams.deinit(); -} - -pub fn decompress(decomp: *Decompressor, in: []u8, out: []u8) Decompressor.Error!usize { - var self: *Self = @fieldParentPtr("interface", decomp); - - var strm = try self.getOrCreate(); - strm.next_in = in.ptr; - strm.avail_in = in.len; - strm.next_out = out.ptr; - strm.avail_out = out.len; - - var res = if (self.xz) - c.lzma_stream_decoder(strm, in.len * 2, 0) - else - c.lzma_alone_decoder(strm, in.len * 2); - switch (res) { - c.LZMA_OK => {}, - c.LZMA_MEM_ERROR => { - self.err = LzmaError.LzmaMemoryError; - return Decompressor.Error.OutOfMemory; - }, - c.LZMA_PROG_ERROR => { - self.err = LzmaError.LzmaProgramError; - return Decompressor.Error.BadInput; - }, - else => { - self.err = LzmaError.Unknown; - return Decompressor.Error.BadInput; - }, - } - while (res == c.LZMA_OK) - res = c.lzma_code(strm, c.LZMA_RUN); - switch (res) { - c.LZMA_STREAM_END => return strm.total_out, - c.LZMA_MEM_ERROR => { - self.err = LzmaError.LzmaMemoryError; - return Decompressor.Error.OutOfMemory; - }, - c.LZMA_MEMLIMIT_ERROR => { - self.err = LzmaError.LzmaMemoryLimit; - return Decompressor.Error.OutOfMemory; - }, - c.LZMA_FORMAT_ERROR => { - self.err = LzmaError.LzmaBadFormat; - return Decompressor.Error.BadInput; - }, - c.LZMA_DATA_ERROR => { - self.err = LzmaError.LzmaDataCorrupt; - return Decompressor.Error.BadInput; - }, - c.LZMA_BUF_ERROR => { - self.err = LzmaError.LzmaCannotProgress; - return Decompressor.Error.BadInput; - }, - c.LZMA_PROG_ERROR => { - self.err = LzmaError.LzmaProgramError; - return Decompressor.Error.BadInput; - }, - else => { - self.err = LzmaError.Unknown; - return Decompressor.Error.BadInput; - }, - } -} -pub fn stateless(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { - var strm = c.lzma_stream{ - .next_in = in.ptr, - .avail_in = in.len, - .next_out = out.ptr, - .avail_out = out.len, - }; - - var res = c.lzma_auto_decoder(&strm, out.len * 2, 0); - switch (res) { - c.LZMA_OK => {}, - c.LZMA_MEM_ERROR => return Decompressor.Error.OutOfMemory, - c.LZMA_PROG_ERROR => return Decompressor.Error.BadInput, - else => return Decompressor.Error.BadInput, - } - while (res == c.LZMA_OK) - res = c.lzma_code(&strm, c.LZMA_RUN); - return switch (res) { - c.LZMA_STREAM_END => strm.total_out, - c.LZMA_MEM_ERROR => Decompressor.Error.OutOfMemory, - c.LZMA_MEMLIMIT_ERROR => Decompressor.Error.OutOfMemory, - c.LZMA_FORMAT_ERROR => Decompressor.Error.BadInput, - c.LZMA_DATA_ERROR => Decompressor.Error.BadInput, - c.LZMA_BUF_ERROR => Decompressor.Error.BadInput, - c.LZMA_PROG_ERROR => Decompressor.Error.BadInput, - else => Decompressor.Error.BadInput, - }; -} - -inline fn getOrCreate(self: *Self) Decompressor.Error!*stream { - const res = try self.streams.getOrPut(std.Thread.getCurrentId()); - if (res.found_existing) return res.value_ptr; - - // Ideally, the zero value should be LZMA_STREAM_INIT, but translate-c can't handle it properly. - // According to lzma, setting it to entirely zero values *should* work. - // res.value_ptr.* = c.LZMA_STREAM_INIT; - res.value_ptr.* = std.mem.zeroInit(stream, .{}); - return res.value_ptr; -} - -pub const LzmaError = error{ - OutOfMemory, - LzmaMemoryError, - LzmaMemoryLimit, - LzmaBadFormat, - LzmaDataCorrupt, - LzmaCannotProgress, - LzmaProgramError, - Unknown, -}; diff --git a/src/decomp/misc_c.zig b/src/decomp/misc_c.zig deleted file mode 100644 index 06d3d37..0000000 --- a/src/decomp/misc_c.zig +++ /dev/null @@ -1,43 +0,0 @@ -const std = @import("std"); - -const Decompressor = @import("../decomp.zig"); -const c = @import("c.zig").c; - -pub const Lzo = struct { - interface: Decompressor = .{ .vtable = &.{ .stateless = lzo } }, -}; - -fn lzo(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { - var res = c.lzo_init(); - if (res != 0) return Decompressor.Error.BadInput; - var out_len: usize = out.len; - res = c.lzo1x_decompress(in.ptr, in.len, out.ptr, &out_len, null); - - return switch (res) { - c.LZO_E_OK => out_len, - c.LZO_E_OUT_OF_MEMORY => Decompressor.Error.OutOfMemory, - c.LZO_E_ERROR, - c.LZO_E_INPUT_OVERRUN, - c.LZO_E_LOOKBEHIND_OVERRUN, - c.LZO_E_EOF_NOT_FOUND, - c.LZO_E_NOT_YET_IMPLEMENTED, - c.LZO_E_INVALID_ARGUMENT, - c.LZO_E_INVALID_ALIGNMENT, - => Decompressor.Error.BadInput, - c.LZO_E_INPUT_NOT_CONSUMED, - c.LZO_E_OUTPUT_NOT_CONSUMED, - c.LZO_E_OUTPUT_OVERRUN, - => Decompressor.Error.OutputTooSmall, - else => Decompressor.Error.BadInput, - }; -} - -pub const Lz4 = struct { - interface: Decompressor = .{ .vtable = &.{ .stateless = lz4 } }, -}; - -fn lz4(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { - const res = c.LZ4_decompress_safe(in.ptr, out.ptr, @intCast(in.len), @intCast(out.len)); - if (res > 0) return @abs(res); - return Decompressor.Error.BadInput; // TODO: Find out what error values it can return. -} diff --git a/src/decomp/types.zig b/src/decomp/types.zig new file mode 100644 index 0000000..aa1e94a --- /dev/null +++ b/src/decomp/types.zig @@ -0,0 +1,43 @@ +const config = @import("config"); + +const Decompressor = @import("../decomp.zig"); +const cLz4 = @import("c/lz4.zig"); +const cLzma = @import("c/lzma.zig"); +const cLzo = @import("c/lzo.zig"); +const cXz = @import("c/xz.zig"); +const cZlib = @import("c/zlib.zig"); +const cZstd = @import("c/zstd.zig"); +const zigLzma = @import("zig/lzma.zig"); +const zigXz = @import("zig/xz.zig"); +const zigZlib = @import("zig/zstd.zig"); +const zigZstd = @import("zig/zstd.zig"); + +pub const Decomp = union(enum) { + gzip: if (config.use_zig_decomp) zigZlib else cZlib, + lzma: if (config.use_zig_decomp) zigLzma else cLzma, + lzo: if (config.use_zig_decomp) void else cLzo, + xz: if (config.use_zig_decomp) zigXz else cXz, + lz4: if (config.use_zig_decomp) void else cLz4, + zstd: if (config.use_zig_decomp) zigZstd else cZstd, + + pub fn deinit(self: *Decomp) void { + switch (self) { + .gzip => self.gzip.deinit(), + .lzma => self.lzma.deinit(), + .xz => self.xz.deinit(), + .zstd => self.zstd.deinit(), + else => {}, + } + } + + pub fn decompressor(self: *Decomp) *Decompressor { + return switch (self) { + .gzip => &self.gzip.interface, + .lzma => &self.lzma.interface, + .lzo => &self.lzo.interface, + .xz => &self.xz.interface, + .lz4 => &self.lz4.interface, + .zstd => &self.zstd.interface, + }; + } +}; diff --git a/src/decomp/zig/lzma.zig b/src/decomp/zig/lzma.zig new file mode 100644 index 0000000..f8c8bdb --- /dev/null +++ b/src/decomp/zig/lzma.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const lzma = std.compress.lzma; +const Reader = std.Io.Reader; + +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, + +fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + var rdr: Reader = .fixed(in); + var decomp = try lzma.decompress(alloc, rdr.adaptToOldInterface()); + defer decomp.deinit(); + return decomp.read(out) catch |err| switch (err) { + error.CorruptInput, error.EndOfStream, error.Overflow => return Decompressor.Error.ReadFailed, + else => return err, + }; +} diff --git a/src/decomp/zig/xz.zig b/src/decomp/zig/xz.zig new file mode 100644 index 0000000..57e2e5d --- /dev/null +++ b/src/decomp/zig/xz.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const xz = std.compress.xz; +const Reader = std.Io.Reader; + +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, + +fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + var rdr: Reader = .fixed(in); + var decomp = try xz.decompress(alloc, rdr.adaptToOldInterface()); + defer decomp.deinit(); + return decomp.read(out) catch |err| switch (err) { + error.CorruptInput, + error.EndOfStream, + error.EndOfStreamWithNoError, + error.WrongChecksum, + error.Unsupported, + error.Overflow, + => Decompressor.Error.ReadFailed, + else => return err, + }; +} diff --git a/src/decomp/zig/zlib.zig b/src/decomp/zig/zlib.zig new file mode 100644 index 0000000..9f3fec5 --- /dev/null +++ b/src/decomp/zig/zlib.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const Reader = std.Io.Reader; +const flate = std.compress.flate; + +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, + +fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + const buf = try alloc.alloc(u8, out.len); + defer alloc.free(buf); + var rdr: Reader = .fixed(in); + + var decomp = flate.Decompress.init(&rdr, .zlib, buf); + return decomp.reader.readSliceShort(out); +} diff --git a/src/decomp/zig/zstd.zig b/src/decomp/zig/zstd.zig new file mode 100644 index 0000000..17d803d --- /dev/null +++ b/src/decomp/zig/zstd.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const Reader = std.Io.Reader; +const zstd = std.compress.zstd; + +const Decompressor = @import("../../decomp.zig"); + +const Self = @This(); + +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, + +fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { + const buf = try alloc.alloc(u8, out.len * 2); + defer alloc.free(buf); + var rdr: Reader = .fixed(in); + + var decomp = zstd.Decompress.init(&rdr, buf, .{ .window_len = @min(out.len, zstd.default_window_len) }); + return decomp.reader.readSliceShort(out); +} diff --git a/src/decomp/zig_decomp.zig b/src/decomp/zig_decomp.zig deleted file mode 100644 index 8a20883..0000000 --- a/src/decomp/zig_decomp.zig +++ /dev/null @@ -1,51 +0,0 @@ -const std = @import("std"); -const Reader = std.Io.Reader; -const builtin = @import("builtin"); - -const Decompressor = @import("../decomp.zig"); - -pub const Gzip = struct { - interface: Decompressor = .{ .vtable = &.{ .stateless = gzip } }, -}; - -pub fn gzip(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { - var rdr: Reader = .fixed(in); - const buf = try alloc.alloc(u8, out.len); - defer alloc.free(buf); - var decomp = std.compress.flate.Decompress.init(&rdr, .zlib, buf); - return decomp.reader.readSliceShort(out); -} - -pub const Lzma = struct { - interface: Decompressor = .{ .vtable = &.{ .stateless = lzma } }, -}; - -pub fn lzma(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { - var rdr: Reader = .fixed(in); - var decomp = try std.compress.lzma.decompress(alloc, rdr.adaptToOldInterface()); - return decomp.read(out); -} - -pub const Xz = struct { - interface: Decompressor = .{ .vtable = &.{ .stateless = xz } }, -}; - -pub fn xz(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { - var rdr: Reader = .fixed(in); - var decomp = try std.compress.xz.decompress(alloc, rdr.adaptToOldInterface()); - return decomp.read(out); -} - -pub const Zstd = struct { - interface: Decompressor = .{ .vtable = &.{ .stateless = zstd } }, -}; - -pub fn zstd(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { - var rdr: Reader = .fixed(in); - const buf = try alloc.alloc(u8, 1024 * 1024); - defer alloc.free(buf); - var decomp = std.compress.zstd.Decompress.init(&rdr, buf, .{}); - return decomp.reader.readSliceShort(out) catch |err| { - return decomp.err orelse err; - }; -} diff --git a/src/decomp/zstd.zig b/src/decomp/zstd.zig deleted file mode 100644 index 8a26140..0000000 --- a/src/decomp/zstd.zig +++ /dev/null @@ -1,216 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -const Decompressor = @import("../decomp.zig"); -const c = @import("c.zig").c; -const DCtx = c.ZSTD_DCtx; - -const Self = @This(); - -alloc: std.mem.Allocator, -ctx: std.AutoHashMap(std.Thread.Id, *DCtx), - -interface: Decompressor = .{ .vtable = &.{ - .decompress = decompress, - .stateless = stateless, -} }, -err: ?ZstdError = null, - -pub fn init(alloc: std.mem.Allocator) !Self { - return .{ - .alloc = alloc, - .ctx = .init(alloc), - }; -} -pub fn deinit(self: Self) void { - var iter = self.ctx.keyIterator(); - while (iter.next()) |key| { - _ = c.ZSTD_freeDCtx(self.ctx.getPtr(key)); - } - self.ctx.deinit(self.alloc); -} - -pub fn decompress(decomp: *Decompressor, in: []u8, out: []u8) Decompressor.Error!usize { - var self: *Self = @fieldParentPtr("interface", decomp); - - const ctx = try self.getOrCreate(); - const res = c.ZSTD_decompressDCtx(ctx, out.ptr, out.len, in.ptr, in.len); - try self.checkError(res); - return res; -} -inline fn getOrCreate(self: *Self) Decompressor.Error!*DCtx { - const res = try self.ctx.getOrPut(std.Thread.getCurrentId()); - if (res.found_existing) { - try self.checkError(c.ZSTD_DCtx_reset(res.value_ptr.*, c.ZSTD_reset_session_only)); - return res.value_ptr.*; - } - res.value_ptr.* = c.ZSTD_createDCtx() orelse return Decompressor.Error.OutOfMemory; - return res.value_ptr.*; -} - -fn stateless(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { - const res = c.ZSTD_decompress(out.ptr, out.len, in.ptr, in.len); - if (c.ZSTD_isError(res) == 0) return res; - return switch (c.ZSTD_getErrorCode(res)) { - c.ZSTD_error_memory_allocation => Decompressor.Error.OutOfMemory, - c.ZSTD_error_workSpace_tooSmall, - c.ZSTD_error_dstSize_tooSmall, - c.ZSTD_error_dstBuffer_null, - c.ZSTD_error_noForwardProgress_destFull, - => Decompressor.Error.OutputTooSmall, - else => Decompressor.Error.BadInput, - }; -} - -inline fn checkError(self: *Self, res: usize) Decompressor.Error!void { - if (res == 0) return; - if (c.ZSTD_isError(res) == 0) return; - switch (c.ZSTD_getErrorCode(res)) { - c.ZSTD_error_prefix_unknown => { - self.err = ZstdError.PrefixUnknown; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_version_unsupported => { - self.err = ZstdError.VersionUnsupported; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_frameParameter_unsupported => { - self.err = ZstdError.FrameParameterUnsupported; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_frameParameter_windowTooLarge => { - self.err = ZstdError.FrameParameterWindowTooLarge; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_corruption_detected => { - self.err = ZstdError.CorruptionDetected; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_checksum_wrong => { - self.err = ZstdError.ChecksumWrong; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_literals_headerWrong => { - self.err = ZstdError.LiteralsHeaderWrong; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_dictionary_corrupted => { - self.err = ZstdError.DictionaryCorrupted; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_dictionary_wrong => { - self.err = ZstdError.DictionaryWrong; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_dictionaryCreation_failed => { - self.err = ZstdError.DictionaryCreationFailed; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_parameter_unsupported => { - self.err = ZstdError.ParameterUnsupported; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_parameter_combination_unsupported => { - self.err = ZstdError.ParameterCombinationUnsupported; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_parameter_outOfBound => { - self.err = ZstdError.ParameterOutOfBound; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_tableLog_tooLarge => { - self.err = ZstdError.TableLogTooLarge; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_maxSymbolValue_tooLarge => { - self.err = ZstdError.MaxSymbolValueTooLarge; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_maxSymbolValue_tooSmall => { - self.err = ZstdError.MaxSymbolValueTooSmall; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_stabilityCondition_notRespected => { - self.err = ZstdError.StabilityConditionNotRespected; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_stage_wrong => { - self.err = ZstdError.StageWrong; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_init_missing => { - self.err = ZstdError.InitMissing; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_memory_allocation => { - self.err = ZstdError.MemoryAllocation; - return Decompressor.Error.OutOfMemory; - }, - c.ZSTD_error_workSpace_tooSmall => { - self.err = ZstdError.WorkSpaceTooSmall; - return Decompressor.Error.OutputTooSmall; - }, - c.ZSTD_error_dstSize_tooSmall => { - self.err = ZstdError.DstSizeTooSmall; - return Decompressor.Error.OutputTooSmall; - }, - c.ZSTD_error_srcSize_wrong => { - self.err = ZstdError.SrcSizeWrong; - return Decompressor.Error.BadInput; - }, - c.ZSTD_error_dstBuffer_null => { - self.err = ZstdError.DstBufferNull; - return Decompressor.Error.OutputTooSmall; - }, - c.ZSTD_error_noForwardProgress_destFull => { - self.err = ZstdError.NoForwardProgressDestFull; - return Decompressor.Error.OutputTooSmall; - }, - c.ZSTD_error_noForwardProgress_inputEmpty => { - self.err = ZstdError.NoForwardProgressInputEmpty; - return Decompressor.Error.BadInput; - }, - else => { - self.err = ZstdError.Generic; - return Decompressor.Error.BadInput; - }, - } -} -pub const ZstdError = error{ - OutOfMemory, - Generic, - PrefixUnknown, - VersionUnsupported, - FrameParameterUnsupported, - FrameParameterWindowTooLarge, - CorruptionDetected, - ChecksumWrong, - LiteralsHeaderWrong, - DictionaryCorrupted, - DictionaryWrong, - DictionaryCreationFailed, - ParameterUnsupported, - ParameterCombinationUnsupported, - ParameterOutOfBound, - TableLogTooLarge, - MaxSymbolValueTooLarge, - MaxSymbolValueTooSmall, - CannotProduceUncompressedBlock, - StabilityConditionNotRespected, - StageWrong, - InitMissing, - MemoryAllocation, - WorkSpaceTooSmall, - DstSizeTooSmall, - SrcSizeWrong, - DstBufferNull, - NoForwardProgressDestFull, - NoForwardProgressInputEmpty, - FrameIndexTooLarge, - SeekableIo, - DstBufferWrong, - SrcBufferWrong, - SequenceProducerFailed, - ExternalSequencesInvalid, - MaxCode, -}; diff --git a/src/tables.zig b/src/tables.zig index ae4016b..5341cca 100644 --- a/src/tables.zig +++ b/src/tables.zig @@ -8,6 +8,7 @@ const Superblock = @import("super.zig").Superblock; const MetadataReader = @import("util/metadata.zig"); const OffsetFile = @import("util/offset_file.zig"); const XattrTable = @import("xattr.zig"); +const Decompressor = @import("decomp.zig"); /// Information about a fragment section. Multiple fragments are contained in the block described by a single FragEntry. /// The offset into the block and fragment size is stored in the file's inode. @@ -48,7 +49,7 @@ pub fn Table(T: anytype) type { alloc: std.mem.Allocator, fil: OffsetFile, - decomp: DecompFn, + decomp: *Decompressor, tab_start: u64, tab: std.AutoHashMap(u32, []T), @@ -56,7 +57,7 @@ pub fn Table(T: anytype) type { mut: Mutex = .{}, - pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: DecompFn, tab_start: u64, values: u32) !Self { + pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: *Decompressor, tab_start: u64, values: u32) !Self { return .{ .alloc = alloc, .fil = fil, diff --git a/src/xattr.zig b/src/xattr.zig index e5203b7..3f0cc0d 100644 --- a/src/xattr.zig +++ b/src/xattr.zig @@ -1,6 +1,6 @@ const std = @import("std"); -const DecompFn = @import("decomp.zig").DecompFn; +const Decompressor = @import("decomp.zig"); const Table = @import("tables.zig").Table; const MetadataReader = @import("util/metadata.zig"); const OffsetFile = @import("util/offset_file.zig"); @@ -38,14 +38,14 @@ const XattrTable = @This(); alloc: std.mem.Allocator, fil: OffsetFile, -decomp: DecompFn, +decomp: *Decompressor, count: u32, start: u64, table: Table(Entry), -pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: DecompFn, table_start: u64) !XattrTable { +pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: *Decompressor, table_start: u64) !XattrTable { var info = packed struct { start: u64 = undefined, count: u32 = undefined,