diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-07-14 17:17:37 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-07-14 17:19:17 -0700 |
| commit | eac628024177434563d348272165d4e3a0a281af (patch) | |
| tree | a11325a92c9ae3445d7a062b989704ec2a5dc2cb /lib/std | |
| parent | a7c3cec65f1639195e787b98900c3f126953f880 (diff) | |
| download | zig-eac628024177434563d348272165d4e3a0a281af.tar.gz zig-eac628024177434563d348272165d4e3a0a281af.zip | |
add std.meta.TrailerFlags API
This is useful for saving memory when allocating an object that has many
optional components. The optional objects are allocated sequentially in
memory, and a single integer is used to represent each optional object
and whether it is present based on each corresponding bit.
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/meta.zig | 1 | ||||
| -rw-r--r-- | lib/std/meta/trailer_flags.zig | 118 |
2 files changed, 119 insertions, 0 deletions
diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 2827cfecf4..6340a44fae 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -6,6 +6,7 @@ const math = std.math; const testing = std.testing; pub const trait = @import("meta/trait.zig"); +pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; const TypeInfo = builtin.TypeInfo; diff --git a/lib/std/meta/trailer_flags.zig b/lib/std/meta/trailer_flags.zig new file mode 100644 index 0000000000..eb8cd6d98c --- /dev/null +++ b/lib/std/meta/trailer_flags.zig @@ -0,0 +1,118 @@ +const std = @import("../std.zig"); +const meta = std.meta; +const testing = std.testing; +const mem = std.mem; +const assert = std.debug.assert; + +/// This is useful for saving memory when allocating an object that has many +/// optional components. The optional objects are allocated sequentially in +/// memory, and a single integer is used to represent each optional object +/// and whether it is present based on each corresponding bit. +pub fn TrailerFlags(comptime Fields: type) type { + return struct { + bits: Int, + + pub const Int = @Type(.{ .Int = .{ .bits = bit_count, .is_signed = false } }); + pub const bit_count = @typeInfo(Fields).Struct.fields.len; + + pub const Self = @This(); + + pub fn has(self: Self, comptime name: []const u8) bool { + const field_index = meta.fieldIndex(Fields, name).?; + return (self.bits & (1 << field_index)) != 0; + } + + pub fn get(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) ?Field(name) { + if (!self.has(name)) + return null; + return self.ptrConst(p, name).*; + } + + pub fn setFlag(self: *Self, comptime name: []const u8) void { + const field_index = meta.fieldIndex(Fields, name).?; + self.bits |= 1 << field_index; + } + + pub fn init(comptime names: anytype) Self { + var self: Self = .{ .bits = 0 }; + inline for (@typeInfo(@TypeOf(names)).Struct.fields) |field| { + if (@field(names, field.name)) { + const field_index = meta.fieldIndex(Fields, field.name).?; + self.bits |= 1 << field_index; + } + } + return self; + } + + pub fn set( + self: Self, + p: [*]align(@alignOf(Fields)) u8, + comptime name: []const u8, + value: Field(name), + ) void { + self.ptr(p, name).* = value; + } + + pub fn ptr(self: Self, p: [*]align(@alignOf(Fields)) u8, comptime name: []const u8) *Field(name) { + const off = self.offset(p, name); + return @ptrCast(*Field(name), @alignCast(@alignOf(Field(name)), p + off)); + } + + pub fn ptrConst(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) *const Field(name) { + const off = self.offset(p, name); + return @ptrCast(*const Field(name), @alignCast(@alignOf(Field(name)), p + off)); + } + + pub fn offset(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) usize { + var off: usize = 0; + inline for (@typeInfo(Fields).Struct.fields) |field, i| { + const active = (self.bits & (1 << i)) != 0; + if (comptime mem.eql(u8, field.name, name)) { + assert(active); + return mem.alignForwardGeneric(usize, off, @alignOf(field.field_type)); + } else if (active) { + off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type)); + off += @sizeOf(field.field_type); + } + } + @compileError("no field named " ++ name ++ " in type " ++ @typeName(Fields)); + } + + pub fn Field(comptime name: []const u8) type { + return meta.fieldInfo(Fields, name).field_type; + } + + pub fn sizeInBytes(self: Self) usize { + var off: usize = 0; + inline for (@typeInfo(Fields).Struct.fields) |field, i| { + if ((self.bits & (1 << i)) != 0) { + off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type)); + off += @sizeOf(field.field_type); + } + } + return off; + } + }; +} + +test "TrailerFlags" { + const Flags = TrailerFlags(struct { + a: i32, + b: bool, + c: u64, + }); + var flags = Flags.init(.{ + .b = true, + .c = true, + }); + testing.expect(flags.sizeInBytes() == 16); + const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact); + defer testing.allocator.free(slice); + + flags.set(slice.ptr, "b", false); + flags.set(slice.ptr, "c", 12345678); + + testing.expect(flags.get(slice.ptr, "a") == null); + testing.expect(!flags.get(slice.ptr, "b").?); + testing.expect(flags.get(slice.ptr, "c").? == 12345678); +} |
