chore: clean up code + documentation
This commit is contained in:
parent
dbe6bd50cd
commit
fbbd25a4c8
|
@ -9,9 +9,11 @@ const std = @import("std");
|
||||||
|
|
||||||
test "doc test" {
|
test "doc test" {
|
||||||
const value: u8 = 0b10001011;
|
const value: u8 = 0b10001011;
|
||||||
try std.testing.expectEqual(true, match("10001011", value));
|
|
||||||
|
try std.testing.expectEqual(true, match("1000_1011", value));
|
||||||
try std.testing.expectEqual(false, match("11111011", value));
|
try std.testing.expectEqual(false, match("11111011", value));
|
||||||
try std.testing.expectEqual(true, match("1---1011", value));
|
try std.testing.expectEqual(true, match("1---1011", value));
|
||||||
|
|
||||||
{
|
{
|
||||||
const ret = extract("1000aaaa", value);
|
const ret = extract("1000aaaa", value);
|
||||||
try std.testing.expectEqual(@as(u4, 0b1011), ret.a);
|
try std.testing.expectEqual(@as(u4, 0b1011), ret.a);
|
||||||
|
@ -34,10 +36,11 @@ test "doc test" {
|
||||||
|
|
||||||
| Token | Meaning | Description
|
| Token | Meaning | Description
|
||||||
| ------- | --------- | -----------
|
| ------- | --------- | -----------
|
||||||
| `0` | Unset bit | In the equivalent position, the value's bit must be set.
|
| `0` | Clear bit | In the equivalent position, the value's bit must be cleared.
|
||||||
| `1` | Set bit | In the equivalent position, the value's bit must be set.
|
| `1` | Set bit | In the equivalent position, the value's bit must be set.
|
||||||
| `a..=z` | Variable | Given the 4-bit bit string, `"1aa0"`, the value `0b1010` would produce the variable `a` with the value `0b01`
|
| `a..=z` | Variable | Given the 4-bit bit string, `"1aa0"`, the value `0b1010` would produce the variable `a` with the value `0b01`
|
||||||
| `-` | Ignored | In the equivalent position, the value's bit does not matter.
|
| `-` | Ignored | In the equivalent position, the value's bit does not matter.
|
||||||
|
| `_` | Ignored* | Underscores are completely ignored during parsing, use to make bit strings easier to read e.g. `1111_1111`
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|
54
src/lib.zig
54
src/lib.zig
|
@ -45,16 +45,6 @@ const std = @import("std");
|
||||||
const Log2Int = std.math.Log2Int;
|
const Log2Int = std.math.Log2Int;
|
||||||
|
|
||||||
/// Test to see if a value matches the provided bit-string
|
/// Test to see if a value matches the provided bit-string
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```zig
|
|
||||||
/// match("1100", @as(u4, 0b1100)) // true
|
|
||||||
/// match("1100", @as(u4, 0b1110)) // false
|
|
||||||
///
|
|
||||||
/// match("1--0", @as(u4, 0b1010)) // true
|
|
||||||
/// match("1ab0", @as(u4, 0b1010)) // true
|
|
||||||
/// match("11_00", @as(u4, 0b1100)) // true
|
|
||||||
/// ```
|
|
||||||
pub fn match(comptime bit_string: []const u8, value: anytype) bool {
|
pub fn match(comptime bit_string: []const u8, value: anytype) bool {
|
||||||
@setEvalBranchQuota(std.math.maxInt(u32)); // FIXME: bad practice
|
@setEvalBranchQuota(std.math.maxInt(u32)); // FIXME: bad practice
|
||||||
|
|
||||||
|
@ -88,7 +78,7 @@ pub fn match(comptime bit_string: []const u8, value: anytype) bool {
|
||||||
return (value & set_mask) == set_mask and (~value & clr_mask) == clr_mask;
|
return (value & set_mask) == set_mask and (~value & clr_mask) == clr_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "match" {
|
test match {
|
||||||
// doc tests
|
// doc tests
|
||||||
try std.testing.expectEqual(true, match("1100", @as(u4, 0b1100)));
|
try std.testing.expectEqual(true, match("1100", @as(u4, 0b1100)));
|
||||||
try std.testing.expectEqual(false, match("1100", @as(u4, 0b1110)));
|
try std.testing.expectEqual(false, match("1100", @as(u4, 0b1110)));
|
||||||
|
@ -113,15 +103,6 @@ test "match" {
|
||||||
|
|
||||||
/// Extracts the variables (defined in the bit string) from a value.
|
/// Extracts the variables (defined in the bit string) from a value.
|
||||||
///
|
///
|
||||||
/// ### Examples
|
|
||||||
/// ```
|
|
||||||
/// const ret = extract("aaaa", @as(u4, 0b1001)); // ret.a == 0b1001
|
|
||||||
/// const ret = extract("abcd", @as(u4, 0b1001)); // ret.a == 0b1, ret.b == 0b0, ret.c == 0b0, ret.d == 0b1
|
|
||||||
/// const ret = extract("a0ab", @as(u4, 0b1001)); // ret.a == 0b10, ret.b == 0b1
|
|
||||||
/// const ret = extract("-a-a", @as(u4, 0b1001)); // ret.a == 0b01
|
|
||||||
/// const ret = extract("aa_aa", @as(u4, 0b1001)); // ret.a == 0b1001
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Note: In Debug and ReleaseSafe builds, there's a runtime assert that
|
/// Note: In Debug and ReleaseSafe builds, there's a runtime assert that
|
||||||
/// ensures that the value matches against the bit string.
|
/// ensures that the value matches against the bit string.
|
||||||
pub fn extract(comptime bit_string: []const u8, value: anytype) Bitfield(bit_string) {
|
pub fn extract(comptime bit_string: []const u8, value: anytype) Bitfield(bit_string) {
|
||||||
|
@ -161,19 +142,14 @@ pub fn extract(comptime bit_string: []const u8, value: anytype) Bitfield(bit_str
|
||||||
//
|
//
|
||||||
// we're confident in this because it's guaranteed to be the same bit_string,
|
// we're confident in this because it's guaranteed to be the same bit_string,
|
||||||
// and it's the same linear search. If you're reading this double check that this is still the case lol
|
// and it's the same linear search. If you're reading this double check that this is still the case lol
|
||||||
break :blk @truncate(if (bmi2 and !@inComptime()) pext.hardware(u32, value, masked_val) else pext.software(u32, value, masked_val));
|
break :blk @truncate(if (bmi2 and !@inComptime()) pext.hw(u32, value, masked_val) else pext.sw(u32, value, masked_val));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matchExtract(comptime bit_string: []const u8, value: anytype) ?Bitfield(bit_string) {
|
test extract {
|
||||||
if (!match(bit_string, value)) return null;
|
|
||||||
return extract(bit_string, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "extract" {
|
|
||||||
// doc tests
|
// doc tests
|
||||||
{
|
{
|
||||||
const ret = extract("aaaa", @as(u4, 0b1001));
|
const ret = extract("aaaa", @as(u4, 0b1001));
|
||||||
|
@ -237,11 +213,13 @@ test "extract" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn matchExtract(comptime bit_string: []const u8, value: anytype) ?Bitfield(bit_string) {
|
||||||
|
if (!match(bit_string, value)) return null;
|
||||||
|
return extract(bit_string, value);
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a bit string and reifies a struct that will contain fields that correspond to the variables present in the bit string.
|
/// Parses a bit string and reifies a struct that will contain fields that correspond to the variables present in the bit string.
|
||||||
///
|
///
|
||||||
///
|
|
||||||
/// Note: If it weren't for the return type of `extract()`, this type would be a private implementation detail
|
|
||||||
///
|
|
||||||
/// TODO: I will probably rename this type
|
/// TODO: I will probably rename this type
|
||||||
pub fn Bitfield(comptime bit_string: []const u8) type {
|
pub fn Bitfield(comptime bit_string: []const u8) type {
|
||||||
const StructField = std.builtin.Type.StructField;
|
const StructField = std.builtin.Type.StructField;
|
||||||
|
@ -323,7 +301,7 @@ fn verify(comptime T: type, comptime bit_string: []const u8) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
const pext = struct {
|
const pext = struct {
|
||||||
fn hardware(comptime T: type, value: T, mask: T) T {
|
fn hw(comptime T: type, value: T, mask: T) T {
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => asm ("pextl %[mask], %[value], %[ret]"
|
u32 => asm ("pextl %[mask], %[value], %[ret]"
|
||||||
: [ret] "=r" (-> T),
|
: [ret] "=r" (-> T),
|
||||||
|
@ -340,7 +318,7 @@ const pext = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// why we need this: https://github.com/ziglang/zig/issues/14995 (ideally compiler-rt implements this for us)
|
// why we need this: https://github.com/ziglang/zig/issues/14995 (ideally compiler-rt implements this for us)
|
||||||
fn software(comptime T: type, value: T, mask: T) T {
|
fn sw(comptime T: type, value: T, mask: T) T {
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => {
|
u32 => {
|
||||||
// TODO: Looks (and is) like C code :pensive:
|
// TODO: Looks (and is) like C code :pensive:
|
||||||
|
@ -375,13 +353,13 @@ const pext = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test "pext" {
|
test pext {
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
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)) {
|
||||||
try std.testing.expectEqual(@as(u32, 0x0001_2567), pext.hardware(u32, 0x12345678, 0xFF00FFF0));
|
try std.testing.expectEqual(@as(u32, 0x0001_2567), pext.hw(u32, 0x12345678, 0xFF00FFF0));
|
||||||
try std.testing.expectEqual(@as(u64, 0x0001_2567), pext.hardware(u64, 0x12345678, 0xFF00FFF0));
|
try std.testing.expectEqual(@as(u64, 0x0001_2567), pext.hw(u64, 0x12345678, 0xFF00FFF0));
|
||||||
|
|
||||||
// random tests
|
// random tests
|
||||||
// TODO: when implemented, test 64-bit fallback `PEXT` as well
|
// TODO: when implemented, test 64-bit fallback `PEXT` as well
|
||||||
|
@ -390,14 +368,14 @@ const pext = struct {
|
||||||
const value = rand_impl.random().int(u32);
|
const value = rand_impl.random().int(u32);
|
||||||
const mask = rand_impl.random().int(u32);
|
const mask = rand_impl.random().int(u32);
|
||||||
|
|
||||||
try std.testing.expectEqual(pext.hardware(u32, value, mask), pext.software(u32, value, mask));
|
try std.testing.expectEqual(pext.hw(u32, value, mask), pext.sw(u32, value, mask));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => {},
|
else => return error.SkipZigTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
// example values from: https://en.wikipedia.org/w/index.php?title=X86_Bit_manipulation_instruction_set&oldid=1170426748
|
// example values from: https://en.wikipedia.org/w/index.php?title=X86_Bit_manipulation_instruction_set&oldid=1170426748
|
||||||
try std.testing.expectEqual(@as(u32, 0x0001_2567), pext.software(u32, 0x12345678, 0xFF00FFF0));
|
try std.testing.expectEqual(@as(u32, 0x0001_2567), pext.sw(u32, 0x12345678, 0xFF00FFF0));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue