Proper zig project organization

This commit is contained in:
Caleb Gardner
2025-05-14 11:46:09 -05:00
parent 6dd3054006
commit 5daffdafc7
17 changed files with 198 additions and 26 deletions
+23
View File
@@ -0,0 +1,23 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const lib_mod = b.createModule(.{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const lib = b.addLibrary(.{
.linkage = .static,
.name = "zig_squashfs",
.root_module = lib_mod,
});
b.installArtifact(lib);
const lib_unit_tests = b.addTest(.{
.root_module = lib_mod,
});
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
}
+61
View File
@@ -0,0 +1,61 @@
.{
.name = .zig_squashfs,
.version = "0.0.0",
.fingerprint = 0x527960c72c03ffe3, // Changing this has security and trust implications.
.minimum_zig_version = "0.14.0",
// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
//.example = .{
// // When updating this field to a new URL, be sure to delete the corresponding
// // `hash`, otherwise you are communicating that you expect to find the old hash at
// // the new URL. If the contents of a URL change this will result in a hash mismatch
// // which will prevent zig from using it.
// .url = "https://example.com/foo.tar.gz",
//
// // This is computed from the file contents of the directory of files that is
// // obtained after fetching `url` and applying the inclusion rules given by
// // `paths`.
// //
// // This field is the source of truth; packages do not come from a `url`; they
// // come from a `hash`. `url` is just one of many possible mirrors for how to
// // obtain a package matching this `hash`.
// //
// // Uses the [multihash](https://multiformats.io/multihash/) format.
// .hash = "...",
//
// // When this is provided, the package is found in a directory relative to the
// // build root. In this case the package's hash is irrelevant and therefore not
// // computed. This field and `url` are mutually exclusive.
// .path = "foo",
//
// // When this is set to `true`, a package is declared to be lazily
// // fetched. This makes the dependency only get fetched if it is
// // actually used.
// .lazy = false,
//},
},
// Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package. Only files listed here will remain on disk
// when using the zig package manager. As a rule of thumb, one should list
// files required for compilation plus any license(s).
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths = .{
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}
-14
View File
@@ -1,14 +0,0 @@
const inode = @import("inode.zig");
const Reader = @import("squashfs.zig").Reader;
const MetadataReader = @import("metadata_reader.zig").MetadataReader;
pub const File = struct {
rdr: *Reader,
inode: inode.Inode,
name: []const u8,
dir_entries: []const void = undefined, //TODO
pub fn fromRef(ref: inode.InodeRef, rdr: *Reader) !File {
var meta_rdr: MetadataReader = .init(rdr.super.comp, rdr: io.AnyReader, alloc: std.mem.Allocator)
}
};
-7
View File
@@ -1,7 +0,0 @@
const std = @import("std");
const FileOffsetReader = struct {
file: std.fs.File,
pub fn any(self: *FileOffsetReader) !std.io.AnyReader {}
};
+45
View File
@@ -0,0 +1,45 @@
const std = @import("std");
const DirHeader = packed struct {
count: u32,
inode_block_start: u32,
inode_num: u32,
};
const RawDirEntry = struct {
inode_offset: u16,
inode_num_difference: i16,
inode_type: u16,
name_size: u16,
name: []u8,
fn init(rdr: std.io.AnyReader, alloc: std.mem.Allocator) !DirEntry {
var out: DirEntry = .{
.inode_offset = try rdr.readInt(u16, std.builtin.Endian.little),
.inode_num_difference = try rdr.readInt(i16, std.builtin.Endian.little),
.inode_type = try rdr.readInt(u16, std.builtin.Endian.little),
.name_size = try rdr.readInt(u16, std.builtin.Endian.little),
.name = undefined,
};
out.name = try alloc.alloc(u8, out.name_size);
_ = try rdr.readAll(out.name);
return out;
}
};
pub const DirEntry = struct {
inode_offset: u16,
inode_block_start: u32,
inode_num: u32,
name: []u8,
fn init(raw: RawDirEntry, hdr: DirHeader) DirEntry {
return .{
.inode_offset = raw.inode_offset,
.inode_block_start = hdr.inode_block_start,
.inode_num = hdr.inode_num - raw.inode_num_difference,
.name = raw.name,
};
}
};
+23
View File
@@ -0,0 +1,23 @@
const inode = @import("inode.zig");
const Reader = @import("squashfs.zig").Reader;
const MetadataReader = @import("metadata_reader.zig").MetadataReader;
const FileOffsetReader = @import("file_offset_reader.zig").FileOffsetReader;
pub const File = struct {
rdr: *Reader,
inode: inode.Inode,
name: []const u8,
dir_entries: []const void = undefined, //TODO
pub fn fromRef(ref: inode.InodeRef, name: []const u8, rdr: *Reader) !File {
var offset_rdr: FileOffsetReader = .init(rdr.file, rdr.super.inode_table + ref.block_start);
var meta_rdr: MetadataReader = .init(rdr.super.comp, offset_rdr.any(), rdr.alloc.allocator());
try meta_rdr.skip(ref.offset);
const in = try inode.readInode(meta_rdr, rdr.super.block_size, rdr.alloc.allocator());
return .{
.rdr = rdr,
.inode = in,
.name = name,
};
}
};
+31
View File
@@ -0,0 +1,31 @@
const std = @import("std");
pub const FileOffsetReader = struct {
file: std.fs.File,
offset: u64,
pub fn init(file: std.fs.File, initial_offset: u64) FileOffsetReader {
return .{
.file = file,
.offset = initial_offset,
};
}
pub fn read(self: *FileOffsetReader, bytes: []u8) anyerror!usize {
const red = try self.file.preadAll(bytes, self.offset);
self.offset += @intCast(red);
return red;
}
pub fn any(self: *FileOffsetReader) std.io.AnyReader {
return .{
.context = @ptrCast(self),
.readFn = readOpaque,
};
}
fn readOpaque(context: *const anyopaque, buf: []u8) anyerror!usize {
var self: *FileOffsetReader = @constCast(@ptrCast(@alignCast(context)));
return self.read(buf);
}
};
View File
+15 -5
View File
@@ -5,6 +5,7 @@ const Superblock = @import("superblock.zig").Superblock;
const inode = @import("inode.zig");
const MetadataReader = @import("metadata_reader.zig").MetadataReader;
const File = @import("file.zig").File;
const FileOffsetReader = @import("file_offset_reader.zig").FileOffsetReader;
pub const Reader = struct {
super: Superblock,
@@ -25,15 +26,24 @@ pub fn newReader(filename: []const u8) !Reader {
errdefer _ = alloc.deinit();
const super = try file.reader().readStruct(Superblock);
try super.valid();
try file.seekTo(super.inode_table + super.root_inode.block_start);
var root_reader: MetadataReader = try .init(super.comp, file.reader().any(), alloc.allocator());
var offset_rdr: FileOffsetReader = .init(file, super.inode_table + super.root_inode.block_start);
var root_reader: MetadataReader = try .init(
super.comp,
offset_rdr.any(),
alloc.allocator(),
);
defer root_reader.deinit();
try root_reader.skip(super.root_inode.offset);
const root_inode = try inode.readInode(root_reader.any(), super.block_size, alloc.allocator());
return Reader{
var out: Reader = undefined;
out = Reader{
.super = super,
.rdr = file,
.root = root_inode,
.root = .{
.inode = try inode.readInode(root_reader.any(), super.block_size, alloc.allocator()),
.name = "",
.rdr = &out,
},
.alloc = alloc,
};
return out;
}