You can now set when building to use c or zig libraries.

This commit is contained in:
Caleb J. Gardner
2026-02-07 10:58:32 -06:00
parent b64a3ec44a
commit 067eaa87c2
4 changed files with 164 additions and 25 deletions
+29 -14
View File
@@ -1,29 +1,44 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const static = b.option(bool, "static_build", "Build static");
pub fn build(b: *std.Build) !void {
const static_option = b.option(bool, "static_build", "Build static");
const use_c_libs_option = b.option(bool, "use_c_libs", "Use C versions of decompression libraries instead of the Zig standard library ones");
const version_string_option = b.option([]const u8, "version", "Version of the library/binary");
const zig_squashfs_options = b.addOptions();
zig_squashfs_options.addOption(bool, "use_c_libs", use_c_libs_option orelse false);
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast });
const linkage: std.builtin.LinkMode = .static; // TODO: Add argument to set link mode.
const use_c_libs: bool = false;
_ = use_c_libs;
const mod = b.addModule("zig_squashfs", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
.link_libc = if (use_c_libs_option == true) true else false,
});
mod.addOptions("config", zig_squashfs_options);
if (use_c_libs_option == true)
mod.linkSystemLibrary("zstd", .{});
const unsquashfs_options = b.addOptions();
unsquashfs_options.addOption(std.SemanticVersion, "version_string", try std.SemanticVersion.parse(version_string_option orelse "0.0.0-testing"));
var exe_mod = b.createModule(.{
.root_source_file = b.path("src/bin/unsquashfs.zig"),
.target = target,
.optimize = optimize,
.link_libc = if (use_c_libs_option == true) true else false,
.imports = &.{
.{ .name = "zig_squashfs", .module = mod },
},
});
exe_mod.addOptions("config", unsquashfs_options);
const exe = b.addExecutable(.{
.name = "unsquashfs",
.linkage = linkage,
.root_module = b.createModule(.{
.root_source_file = b.path("src/bin/unsquashfs.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "zig_squashfs", .module = mod },
},
}),
.linkage = if (static_option == true) .static else .dynamic,
.root_module = exe_mod,
});
b.installArtifact(exe);
const run_step = b.step("run", "Run the app");
const run_cmd = b.addRunArtifact(exe);
+15 -1
View File
@@ -1,6 +1,7 @@
const std = @import("std");
const Writer = std.Io.Writer;
const config = @import("config");
const squashfs = @import("zig_squashfs");
//TODO: Add more options
@@ -8,8 +9,11 @@ const help_mgs =
\\Usage: unsquashfs [options] <archive>
\\
\\Options:
\\ -o <offset> Start reading the archive at the given offset.
\\ -d <location> Extract to the given location instead of "squashfs-root"
\\
\\ -o <offset> Start reading the archive at the given offset.
\\
\\ --version Display the version
;
const errors = error{InvalidArguments};
@@ -24,6 +28,10 @@ pub fn main() !void {
var out = stdout.writer(&[0]u8{});
defer out.interface.flush() catch {};
try handleArgs(alloc, &out.interface);
if (archive.len == 0) {
try out.interface.print("You must provide a squashfs archive\n", .{});
return;
}
var fil: std.fs.File = try std.fs.cwd().openFile(archive, .{}); //TODO: Handle error gracefully.
defer fil.close();
var arc: squashfs.Archive = try .initAdvanced(alloc, fil, offset, try std.Thread.getCpuCount(), 0); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
@@ -55,6 +63,12 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
}
extLoc = nxt.?;
continue;
} else if (std.mem.eql(u8, arg, "--version")) {
_ = try out.write("v");
try config.version_string.format(out);
_ = try out.write("\n");
std.process.cleanExit();
return;
}
if (archive.len > 0) {
try out.print("you can only provide one file at a time\n", .{});
+118 -8
View File
@@ -1,6 +1,15 @@
//! Implementations for decompression.
//! TODO: change to vtable interface to allow for shared decompressors for better performance/resource usage.
const std = @import("std");
const Reader = std.Io.Reader;
const config = @import("config");
const c = @cImport({
@cInclude("zstd.h");
});
pub const CompressionType = enum(u16) {
gzip = 1,
lzma,
@@ -12,32 +21,53 @@ pub const CompressionType = enum(u16) {
pub const DecompFn = *const fn (alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize; // TODO: replace anyerror to definitive error types.
// pub const DecompressError = error{
// ReadFailed,
// anyerror,
// };
pub const gzipDecompress = if (config.use_c_libs) cGzip else zigGzip;
pub fn gzipDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
fn zigGzip(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);
}
fn cGzip(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
_ = alloc;
_ = in;
_ = out;
return error.TODO;
}
pub fn lzmaDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
pub const lzmaDecompress = if (config.use_c_libs) cLzma else zigLzma;
fn zigLzma(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);
}
fn cLzma(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
_ = alloc;
_ = in;
_ = out;
return error.TODO;
}
pub fn xzDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
pub const xzDecompress = if (config.use_c_libs) cXz else zigXz;
fn zigXz(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);
}
fn cXz(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
_ = alloc;
_ = in;
_ = out;
return error.TODO;
}
pub fn zstdDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
pub const zstdDecompress = if (config.use_c_libs) cZstd else zigZstd;
pub fn zigZstd(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
var rdr: Reader = .fixed(in);
const buf = try alloc.alloc(u8, std.compress.zstd.default_window_len + std.compress.zstd.block_size_max);
defer alloc.free(buf);
@@ -46,3 +76,83 @@ pub fn zstdDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!us
return decomp.err orelse err;
};
}
fn cZstd(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
_ = alloc;
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_prefix_unknown => cZstdError.PrefixUnknown,
c.ZSTD_error_version_unsupported => cZstdError.VersionUnsupported,
c.ZSTD_error_frameParameter_unsupported => cZstdError.FrameParameterUnsupported,
c.ZSTD_error_frameParameter_windowTooLarge => cZstdError.FrameParameterWindowTooLarge,
c.ZSTD_error_corruption_detected => cZstdError.CorruptionDetected,
c.ZSTD_error_checksum_wrong => cZstdError.ChecksumWrong,
c.ZSTD_error_literals_headerWrong => cZstdError.LiteralsHeaderWrong,
c.ZSTD_error_dictionary_corrupted => cZstdError.DictionaryCorrupted,
c.ZSTD_error_dictionary_wrong => cZstdError.DictionaryWrong,
c.ZSTD_error_dictionaryCreation_failed => cZstdError.DictionaryCreationFailed,
c.ZSTD_error_parameter_unsupported => cZstdError.ParameterUnsupported,
c.ZSTD_error_parameter_combination_unsupported => cZstdError.ParameterCombinationUnsupported,
c.ZSTD_error_parameter_outOfBound => cZstdError.ParameterOutOfBound,
c.ZSTD_error_tableLog_tooLarge => cZstdError.TableLogTooLarge,
c.ZSTD_error_maxSymbolValue_tooLarge => cZstdError.MaxSymbolValueTooLarge,
c.ZSTD_error_maxSymbolValue_tooSmall => cZstdError.MaxSymbolValueTooSmall,
c.ZSTD_error_cannotProduce_uncompressedBlock => cZstdError.CannotProduceUncompressedBlock,
c.ZSTD_error_stabilityCondition_notRespected => cZstdError.StabilityConditionNotRespected,
c.ZSTD_error_stage_wrong => cZstdError.StageWrong,
c.ZSTD_error_init_missing => cZstdError.InitMissing,
c.ZSTD_error_memory_allocation => cZstdError.MemoryAllocation,
c.ZSTD_error_workSpace_tooSmall => cZstdError.WorkSpaceTooSmall,
c.ZSTD_error_dstSize_tooSmall => cZstdError.DstSizeTooSmall,
c.ZSTD_error_srcSize_wrong => cZstdError.SrcSizeWrong,
c.ZSTD_error_dstBuffer_null => cZstdError.DstBufferNull,
c.ZSTD_error_noForwardProgress_destFull => cZstdError.NoForwardProgressDestFull,
c.ZSTD_error_noForwardProgress_inputEmpty => cZstdError.NoForwardProgressInputEmpty,
c.ZSTD_error_frameIndex_tooLarge => cZstdError.FrameIndexTooLarge,
c.ZSTD_error_seekableIO => cZstdError.SeekableIo,
c.ZSTD_error_dstBuffer_wrong => cZstdError.DstBufferWrong,
c.ZSTD_error_srcBuffer_wrong => cZstdError.SrcBufferWrong,
c.ZSTD_error_sequenceProducer_failed => cZstdError.SequenceProducerFailed,
c.ZSTD_error_externalSequences_invalid => cZstdError.ExternalSequencesInvalid,
c.ZSTD_error_maxCode => cZstdError.MaxCode,
else => cZstdError.Generic,
};
}
pub const cZstdError = error{
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,
};
+2 -2
View File
@@ -92,10 +92,10 @@ fn advance(self: *DataReader) !void {
const tmp_buf = try self.alloc.alloc(u8, self.frag.?.size.size);
defer self.alloc.free(tmp_buf);
try rdr.interface.readSliceAll(tmp_buf);
const needed_block = try self.alloc.alloc(u8, self.frag_offset + cur_block_size);
const needed_block = try self.alloc.alloc(u8, self.block_size);
defer self.alloc.free(needed_block);
_ = try self.decomp(self.alloc, tmp_buf, needed_block);
@memcpy(self.interface.buffer, needed_block[self.frag_offset..]);
@memcpy(self.interface.buffer, needed_block[self.frag_offset .. self.frag_offset + cur_block_size]);
return;
}
const block = self.blocks[self.block_idx];