Last active
August 25, 2020 21:56
-
-
Save lemon32767/489da29d71329436cb17935dcddf6b38 to your computer and use it in GitHub Desktop.
zig bitreg type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const assert = std.debug.assert; | |
const trait = std.meta.trait; | |
//example: | |
// BitReg(u32, .{ | |
// .{.first_4_bits, 0, 4}, | |
// .{.next_7_bits, 4, 7}, | |
// }) | |
pub fn BitReg(comptime T: type, comptime fields: anytype) type { | |
const check_or = struct { | |
fn check_or(comptime ok: bool, comptime msg: []const u8) void { | |
if (!ok) @compileError(msg); | |
} | |
}.check_or; | |
check_or(trait.isUnsignedInt(T), "type must be unsigned integer type, was " ++ @typeName(T)); | |
assert(trait.isIndexable(@TypeOf(fields))); | |
check_or(fields.len > 0, "empty fields parameter"); | |
for (fields) |field, i| { | |
//check_or correct format | |
assert(field.len == 3); | |
check_or(trait.is(.EnumLiteral)(@TypeOf(field[0])), "field name must be @Type(.EnumLiteral)"); | |
//check_or that it's not duplicate | |
var idx: usize = 0; | |
while (idx < i) : (idx += 1) { | |
check_or(fields[idx][0] != field[0], "field ." ++ @tagName(field[0]) ++ " is a duplicate"); | |
} | |
//continue checking format | |
const Field1 = @TypeOf(field[1]); | |
const Field2 = @TypeOf(field[2]); | |
check_or((trait.is(.Int)(Field1) or trait.is(.ComptimeInt)(Field1)) and field[1] >= 0, "field offset must be positive/zero integer"); | |
check_or((trait.is(.Int)(Field2) or trait.is(.ComptimeInt)(Field2)) and field[2] > 0, "field width must be positive integer"); | |
check_or(field[1] + field[2] <= @bitSizeOf(T), "field ." ++ @tagName(field[0]) ++ " doesn't fit in " ++ @typeName(T)); | |
} | |
//for comptime error reporting | |
comptime var field_list_str: []const u8 = ""; | |
for (fields) |field, i| { | |
field_list_str = field_list_str ++ "." ++ @tagName(field[0]); | |
if (i < fields.len - 2) { | |
field_list_str = field_list_str ++ ", "; | |
} else if (i < fields.len - 1) { | |
field_list_str = field_list_str ++ " or "; | |
} | |
} | |
const bestFitInt = struct { | |
fn bestFitInt(comptime reg: @Type(.EnumLiteral)) type { | |
for (fields) |field| { | |
if (field[0] == reg) { | |
return @Type(.{ .Int = .{ .is_signed = false, .bits = field[2] } }); | |
} | |
} | |
@compileError("invalid field: ." ++ @tagName(reg) ++ "; should be one of: " ++ field_list_str); | |
unreachable; | |
} | |
}.bestFitInt; | |
return extern struct { | |
raw: T, | |
//get value of field 'reg', as an unsigned integer of bit size equal to field bit width | |
//compile error if field isn't defined | |
pub inline fn get(this: @This(), comptime reg: @Type(.EnumLiteral)) bestFitInt(reg) { | |
inline for (fields) |field| { | |
if (field[0] == reg) { | |
const offset: T = field[1]; | |
const width: T = field[2]; | |
return @truncate(bestFitInt(reg), this.raw >> offset); | |
} | |
} | |
comptime unreachable; | |
} | |
//get value of field 'reg', as an unsigned integer of the backing integer type | |
//compile error if field isn't defined | |
pub inline fn getCast(this: @This(), comptime reg: @Type(.EnumLiteral)) T { | |
return @intCast(T, this.get(reg)); | |
} | |
//set value of field 'reg', from an unsigned integer of bit size equal to field bit width | |
//compile error if field isn't defined | |
pub inline fn set(this: *@This(), comptime reg: @Type(.EnumLiteral), value: bestFitInt(reg)) void { | |
inline for (fields) |field| { | |
if (field[0] == reg) { | |
const offset: T = field[1]; | |
const width: T = field[2]; | |
const mask: T = ((1 << width) - 1) << offset; | |
this.raw = (this.raw & ~mask) | ((@as(T, value) << offset) & mask); | |
return; | |
} | |
} | |
comptime unreachable; | |
} | |
//set value of field 'reg', from an arbitrary integer value safely casted to an unsigned integer of bit size equal to field bit width | |
//compile error if field isn't defined | |
pub inline fn setCast(this: *@This(), comptime reg: @Type(.EnumLiteral), value: anytype) void { | |
return this.set(reg, @intCast(bestFitInt(reg), value)); | |
} | |
//set value of field 'reg', from an arbitrary integer value truncated to an unsigned integer of bit size equal to field bit width | |
//compile error if field isn't defined | |
pub inline fn setTrunc(this: *@This(), comptime reg: @Type(.EnumLiteral), value: anytype) void { | |
return this.set(reg, @truncate(bestFitInt(reg), value)); | |
} | |
}; | |
} | |
const testing = std.testing; | |
test "bitreg" { | |
const Testt = BitReg(u32, .{ | |
.{ .a, 0, 4 }, | |
.{ .b, 4, 8 }, | |
.{ .c, 14, 2 }, | |
//.{ .toobig, 30, 3 }, //this will error because offset(30)+width(3) doesn't fit in u32 | |
//.{ .zerosize, 0, 0 }, | |
//.{ .negative, -1, 1 }, | |
//.{ .negative2, 0, -1 }, | |
// .{ .a, 0, 1 }, //duplicate | |
}); | |
testing.expectEqual(@sizeOf(Testt), @sizeOf(u32)); | |
var foo: Testt = .{ .raw = 0b1110001_0110 }; | |
testing.expectEqual(@TypeOf(foo.get(.a)), u4); | |
testing.expectEqual(@TypeOf(foo.get(.b)), u8); | |
testing.expectEqual(@TypeOf(foo.get(.c)), u2); | |
testing.expectEqual(foo.get(.a), 0b0110); | |
testing.expectEqual(foo.get(.b), 0b1110001); | |
testing.expectEqual(foo.get(.c), 0); | |
foo.set(.a, 0b1001); | |
testing.expectEqual(foo.get(.a), 0b1001); | |
testing.expectEqual(foo.get(.b), 0b1110001); | |
testing.expectEqual(foo.get(.c), 0); | |
foo.set(.b, 1); | |
testing.expectEqual(foo.get(.a), 0b1001); | |
testing.expectEqual(foo.get(.b), 0b0000001); | |
testing.expectEqual(foo.raw, 0b0000001_1001); | |
foo.set(.c, 2); | |
testing.expectEqual(foo.raw, 0b10_00_00000001_1001); | |
// foo.set(.c, 4); // compile error: 4 cannot be coerced into u2 | |
var x = @src().line * 100; //force runtime evaluation | |
//foo.set(.a, @intCast(u4, x)); //panic: integer cast truncated bits | |
foo.setTrunc(.a, x); //works | |
//foo.setCast(.a, x); //panic: integer cast truncated bits | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment