feat: update for Zig v0.14.0

This commit is contained in:
Rekai Nyangadzayi Musuka 2025-05-09 03:26:40 -05:00
parent d72a39006a
commit 57e1b7e42f
4 changed files with 57 additions and 33 deletions

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
zig-out/ zig-out/
zig-cache/
.zig-cache/ .zig-cache/

View File

@ -15,8 +15,12 @@ pub fn build(b: *std.Build) void {
// set a preferred release mode, allowing the user to decide how to optimize. // set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const lib = b.addStaticLibrary(.{ // This creates a "module", which represents a collection of source files alongside
.name = "bit-string", // some compilation options, such as optimization mode and linked system libraries.
// Every executable or library we compile will be based on one or more modules.
const lib_mod = b.createModule(.{
// `root_source_file` is the Zig "entry point" of the module. If a module
// only contains e.g. external object files, you can make this `null`.
// In this case the main source file is merely a path, however, in more // In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file. // complicated build scripts, this could be a generated file.
.root_source_file = b.path("src/lib.zig"), .root_source_file = b.path("src/lib.zig"),
@ -24,10 +28,13 @@ pub fn build(b: *std.Build) void {
.optimize = optimize, .optimize = optimize,
}); });
const docs = b.addInstallDirectory(.{ // Now, we will create a static library based on the module we created above.
.install_dir = .prefix, // This creates a `std.Build.Step.Compile`, which is the build step responsible
.install_subdir = "docs", // for actually invoking the compiler.
.source_dir = lib.getEmittedDocs(), const lib = b.addLibrary(.{
.linkage = .static,
.name = "bit_string",
.root_module = lib_mod,
}); });
// This declares intent for the library to be installed into the standard // This declares intent for the library to be installed into the standard
@ -35,12 +42,16 @@ pub fn build(b: *std.Build) void {
// running `zig build`). // running `zig build`).
b.installArtifact(lib); b.installArtifact(lib);
const docs = b.addInstallDirectory(.{
.install_dir = .prefix,
.install_subdir = "docs",
.source_dir = lib.getEmittedDocs(),
});
// Creates a step for unit testing. This only builds the test executable // Creates a step for unit testing. This only builds the test executable
// but does not run it. // but does not run it.
const lib_unit_tests = b.addTest(.{ const lib_unit_tests = b.addTest(.{
.root_source_file = b.path("src/test.zig"), .root_module = lib_mod,
.target = target,
.optimize = optimize,
}); });
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

View File

@ -6,16 +6,29 @@
// //
// It is redundant to include "zig" in this name because it is already // It is redundant to include "zig" in this name because it is already
// within the Zig package namespace. // within the Zig package namespace.
.name = "bit-string", .name = .bit_string,
// This is a [Semantic Version](https://semver.org/). // This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication. // In a future version of Zig it will be used for package deduplication.
.version = "0.1.0", .version = "0.1.0",
// This field is optional. // Together with name, this represents a globally unique package
// This is currently advisory only; Zig does not yet do anything // identifier. This field is generated by the Zig toolchain when the
// with this value. // package is first created, and then *never changes*. This allows
//.minimum_zig_version = "0.11.0", // unambiguous detection of one package being an updated version of
// another.
//
// When forking a Zig project, this id should be regenerated (delete the
// field and run `zig build`) if the upstream project is still maintained.
// Otherwise, the fork is *hostile*, attempting to take control over the
// original project's identity. Thus it is recommended to leave the comment
// on the following line intact, so that it shows up in code reviews that
// modify the field.
.fingerprint = 0x14a39efa6e959493, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a
// supported use case.
.minimum_zig_version = "0.14.0",
// This field is optional. // This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`. // Each dependency must either provide a `url` and `hash`, or a `path`.
@ -27,7 +40,8 @@
//.example = .{ //.example = .{
// // When updating this field to a new URL, be sure to delete the corresponding // // 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 // // `hash`, otherwise you are communicating that you expect to find the old hash at
// // the new URL. // // 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", // .url = "https://example.com/foo.tar.gz",
// //
// // This is computed from the file contents of the directory of files that is // // This is computed from the file contents of the directory of files that is
@ -45,7 +59,7 @@
// // build root. In this case the package's hash is irrelevant and therefore not // // build root. In this case the package's hash is irrelevant and therefore not
// // computed. This field and `url` are mutually exclusive. // // computed. This field and `url` are mutually exclusive.
// .path = "foo", // .path = "foo",
//
// // When this is set to `true`, a package is declared to be lazily // // When this is set to `true`, a package is declared to be lazily
// // fetched. This makes the dependency only get fetched if it is // // fetched. This makes the dependency only get fetched if it is
// // actually used. // // actually used.

View File

@ -52,7 +52,7 @@ pub fn match(comptime bit_string: []const u8, value: anytype) bool {
comptime verify(ValT, bit_string); comptime verify(ValT, bit_string);
const masks: struct { ValT, ValT } = comptime blk: { const masks: struct { ValT, ValT } = comptime blk: {
const bit_count = @typeInfo(ValT).Int.bits; const bit_count = @typeInfo(ValT).int.bits;
var set: ValT = 0; var set: ValT = 0;
var clr: ValT = 0; var clr: ValT = 0;
@ -123,13 +123,13 @@ pub fn extract(comptime bit_string: []const u8, value: anytype) Bitfield(bit_str
var ret: ReturnT = undefined; var ret: ReturnT = undefined;
inline for (@typeInfo(ReturnT).Struct.fields) |field| { inline for (@typeInfo(ReturnT).@"struct".fields) |field| {
@field(ret, field.name) = blk: { @field(ret, field.name) = blk: {
var masked_val: ValT = 0; var masked_val: ValT = 0;
var offset: usize = 0; // FIXME(URGENT): this whole block should be happening at comptime... var offset: usize = 0; // FIXME(URGENT): this whole block should be happening at comptime...
for (bit_string, 0..) |char, i| { for (bit_string, 0..) |char, i| {
const rev = @typeInfo(ValT).Int.bits - 1 - (i - offset); const rev = @typeInfo(ValT).int.bits - 1 - (i - offset);
switch (char) { switch (char) {
'_' => offset += 1, '_' => offset += 1,
@ -139,7 +139,7 @@ pub fn extract(comptime bit_string: []const u8, value: anytype) Bitfield(bit_str
} }
} }
const PextT = if (@typeInfo(ValT).Int.bits > 32) u64 else u32; const PextT = if (@typeInfo(ValT).int.bits > 32) u64 else u32;
const use_hw = bmi2 and !@inComptime(); const use_hw = bmi2 and !@inComptime();
break :blk @truncate(if (use_hw) pext.hw(PextT, value, masked_val) else pext.sw(PextT, value, masked_val)); break :blk @truncate(if (use_hw) pext.hw(PextT, value, masked_val) else pext.sw(PextT, value, masked_val));
@ -197,19 +197,19 @@ test extract {
const ret = extract("--------", @as(u8, 0b00000000)); const ret = extract("--------", @as(u8, 0b00000000));
const T = @TypeOf(ret); const T = @TypeOf(ret);
try std.testing.expectEqual(@as(usize, 0), @typeInfo(T).Struct.fields.len); try std.testing.expectEqual(@as(usize, 0), @typeInfo(T).@"struct".fields.len);
} }
{ {
const ret = extract("00000000", @as(u8, 0b00000000)); const ret = extract("00000000", @as(u8, 0b00000000));
const T = @TypeOf(ret); const T = @TypeOf(ret);
try std.testing.expectEqual(@as(usize, 0), @typeInfo(T).Struct.fields.len); try std.testing.expectEqual(@as(usize, 0), @typeInfo(T).@"struct".fields.len);
} }
{ {
const ret = extract("0-0-0-0-", @as(u8, 0b01010101)); const ret = extract("0-0-0-0-", @as(u8, 0b01010101));
const T = @TypeOf(ret); const T = @TypeOf(ret);
try std.testing.expectEqual(@as(usize, 0), @typeInfo(T).Struct.fields.len); try std.testing.expectEqual(@as(usize, 0), @typeInfo(T).@"struct".fields.len);
} }
{ {
@ -272,12 +272,12 @@ pub fn Bitfield(comptime bit_string: []const u8) type {
} }
for (things, &tmp) |th, *field| { for (things, &tmp) |th, *field| {
const FieldInt = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = th.bits } }); const FieldInt = @Type(.{ .int = .{ .signedness = .unsigned, .bits = th.bits } });
field.* = .{ field.* = .{
.name = &.{th.char.?}, .name = &.{th.char.?},
.type = FieldInt, .type = FieldInt,
.default_value = null, .default_value_ptr = null,
.is_comptime = false, .is_comptime = false,
.alignment = @alignOf(FieldInt), .alignment = @alignOf(FieldInt),
}; };
@ -286,7 +286,7 @@ pub fn Bitfield(comptime bit_string: []const u8) type {
break :blk tmp; break :blk tmp;
}; };
return @Type(.{ .Struct = .{ return @Type(.{ .@"struct" = .{
.layout = .auto, .layout = .auto,
.fields = &fields, .fields = &fields,
.decls = &.{}, .decls = &.{},
@ -297,16 +297,16 @@ pub fn Bitfield(comptime bit_string: []const u8) type {
fn verify(comptime T: type, comptime bit_string: []const u8) void { fn verify(comptime T: type, comptime bit_string: []const u8) void {
const info = @typeInfo(T); const info = @typeInfo(T);
std.debug.assert(info != .ComptimeInt); std.debug.assert(info != .comptime_int);
std.debug.assert(info.Int.signedness == .unsigned); std.debug.assert(info.int.signedness == .unsigned);
std.debug.assert(info.Int.bits <= 64); // x86 PEXT u32 and u64 operands only std.debug.assert(info.int.bits <= 64); // x86 PEXT u32 and u64 operands only
var underscore_count = 0; var underscore_count = 0;
for (bit_string) |c| { for (bit_string) |c| {
if (c == '_') underscore_count += 1; if (c == '_') underscore_count += 1;
} }
std.debug.assert((bit_string.len - underscore_count) == info.Int.bits); std.debug.assert((bit_string.len - underscore_count) == info.int.bits);
} }
const pext = struct { const pext = struct {
@ -333,7 +333,7 @@ const pext = struct {
u32, u64 => { u32, u64 => {
// code source: https://stackoverflow.com/questions/41720249/detecting-matching-bits-in-c // code source: https://stackoverflow.com/questions/41720249/detecting-matching-bits-in-c
// TODO: rewrite more in generic/idiomatic zig // TODO: rewrite more in generic/idiomatic zig
const log2_bits = @typeInfo(Log2Int(T)).Int.bits; const log2_bits = @typeInfo(Log2Int(T)).int.bits;
var val: T = value & mask; // immediately clear irrelevant bits var val: T = value & mask; // immediately clear irrelevant bits
var msk: T = mask; var msk: T = mask;
@ -369,7 +369,7 @@ const pext = struct {
switch (builtin.cpu.arch) { switch (builtin.cpu.arch) {
.x86_64 => if (std.Target.x86.featureSetHas(builtin.cpu.features, .bmi2)) { .x86_64 => if (std.Target.x86.featureSetHas(builtin.cpu.features, .bmi2)) {
var rand_impl = std.rand.DefaultPrng.init(0xBAADF00D_DEADCAFE); var rand_impl = std.Random.DefaultPrng.init(0xBAADF00D_DEADCAFE);
for (0..100) |_| { for (0..100) |_| {
const value = rand_impl.random().int(u32); const value = rand_impl.random().int(u32);