authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-14 17:17:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-14 17:19:17-07:00
logeac628024177434563d348272165d4e3a0a281af
treea11325a92c9ae3445d7a062b989704ec2a5dc2cb
parenta7c3cec65f1639195e787b98900c3f126953f880

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.

2 files changed, 119 insertions(+), 0 deletions(-)

lib/std/meta.zig+1
......@@ -6,6 +6,7 @@ const math = std.math;
66const testing = std.testing;
77
88pub const trait = @import("meta/trait.zig");
9pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
910
1011const TypeInfo = builtin.TypeInfo;
1112
lib/std/meta/trailer_flags.zig created+118
......@@ -0,0 +1,118 @@
1const std = @import("../std.zig");
2const meta = std.meta;
3const testing = std.testing;
4const mem = std.mem;
5const assert = std.debug.assert;
6
7/// This is useful for saving memory when allocating an object that has many
8/// optional components. The optional objects are allocated sequentially in
9/// memory, and a single integer is used to represent each optional object
10/// and whether it is present based on each corresponding bit.
11pub fn TrailerFlags(comptime Fields: type) type {
12 return struct {
13 bits: Int,
14
15 pub const Int = @Type(.{ .Int = .{ .bits = bit_count, .is_signed = false } });
16 pub const bit_count = @typeInfo(Fields).Struct.fields.len;
17
18 pub const Self = @This();
19
20 pub fn has(self: Self, comptime name: []const u8) bool {
21 const field_index = meta.fieldIndex(Fields, name).?;
22 return (self.bits & (1 << field_index)) != 0;
23 }
24
25 pub fn get(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) ?Field(name) {
26 if (!self.has(name))
27 return null;
28 return self.ptrConst(p, name).*;
29 }
30
31 pub fn setFlag(self: *Self, comptime name: []const u8) void {
32 const field_index = meta.fieldIndex(Fields, name).?;
33 self.bits |= 1 << field_index;
34 }
35
36 pub fn init(comptime names: anytype) Self {
37 var self: Self = .{ .bits = 0 };
38 inline for (@typeInfo(@TypeOf(names)).Struct.fields) |field| {
39 if (@field(names, field.name)) {
40 const field_index = meta.fieldIndex(Fields, field.name).?;
41 self.bits |= 1 << field_index;
42 }
43 }
44 return self;
45 }
46
47 pub fn set(
48 self: Self,
49 p: [*]align(@alignOf(Fields)) u8,
50 comptime name: []const u8,
51 value: Field(name),
52 ) void {
53 self.ptr(p, name).* = value;
54 }
55
56 pub fn ptr(self: Self, p: [*]align(@alignOf(Fields)) u8, comptime name: []const u8) *Field(name) {
57 const off = self.offset(p, name);
58 return @ptrCast(*Field(name), @alignCast(@alignOf(Field(name)), p + off));
59 }
60
61 pub fn ptrConst(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) *const Field(name) {
62 const off = self.offset(p, name);
63 return @ptrCast(*const Field(name), @alignCast(@alignOf(Field(name)), p + off));
64 }
65
66 pub fn offset(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) usize {
67 var off: usize = 0;
68 inline for (@typeInfo(Fields).Struct.fields) |field, i| {
69 const active = (self.bits & (1 << i)) != 0;
70 if (comptime mem.eql(u8, field.name, name)) {
71 assert(active);
72 return mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
73 } else if (active) {
74 off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
75 off += @sizeOf(field.field_type);
76 }
77 }
78 @compileError("no field named " ++ name ++ " in type " ++ @typeName(Fields));
79 }
80
81 pub fn Field(comptime name: []const u8) type {
82 return meta.fieldInfo(Fields, name).field_type;
83 }
84
85 pub fn sizeInBytes(self: Self) usize {
86 var off: usize = 0;
87 inline for (@typeInfo(Fields).Struct.fields) |field, i| {
88 if ((self.bits & (1 << i)) != 0) {
89 off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
90 off += @sizeOf(field.field_type);
91 }
92 }
93 return off;
94 }
95 };
96}
97
98test "TrailerFlags" {
99 const Flags = TrailerFlags(struct {
100 a: i32,
101 b: bool,
102 c: u64,
103 });
104 var flags = Flags.init(.{
105 .b = true,
106 .c = true,
107 });
108 testing.expect(flags.sizeInBytes() == 16);
109 const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact);
110 defer testing.allocator.free(slice);
111
112 flags.set(slice.ptr, "b", false);
113 flags.set(slice.ptr, "c", 12345678);
114
115 testing.expect(flags.get(slice.ptr, "a") == null);
116 testing.expect(!flags.get(slice.ptr, "b").?);
117 testing.expect(flags.get(slice.ptr, "c").? == 12345678);
118}