authorgravatar for me@tadeo.caTadeo Kondrak <me@tadeo.ca> 2020-08-27 15:00:15-06:00
committergravatar for me@tadeo.caTadeo Kondrak <me@tadeo.ca> 2020-08-27 15:11:33-06:00
logf94583076ee517be311df6c70654e0cc41bf2b16
tree52a4680edbd6882cc9990f373fd2cdf4b9cc0f20
parentf6cedfaaca2a1ca22786b32aa76ef6348b74ac38
signaturelock-open Commit is signed but in an unrecognized format.

std.meta.TrailerFlags: use @Type to improve API

- Use an enum of all field names instead of string literals - Create a struct type with all fields optional instead of relying on anonymous struct literals This should provide better type inference, compile errors, and a (subjectively) cleaner API.

1 files changed, 86 insertions(+), 47 deletions(-)

lib/std/meta/trailer_flags.zig+86-47
...@@ -8,6 +8,7 @@ const meta = std.meta;...@@ -8,6 +8,7 @@ const meta = std.meta;
8const testing = std.testing;8const testing = std.testing;
9const mem = std.mem;9const mem = std.mem;
10const assert = std.debug.assert;10const assert = std.debug.assert;
11const TypeInfo = std.builtin.TypeInfo;
1112
12/// This is useful for saving memory when allocating an object that has many13/// This is useful for saving memory when allocating an object that has many
13/// optional components. The optional objects are allocated sequentially in14/// optional components. The optional objects are allocated sequentially in
...@@ -17,91 +18,127 @@ pub fn TrailerFlags(comptime Fields: type) type {...@@ -17,91 +18,127 @@ pub fn TrailerFlags(comptime Fields: type) type {
17 return struct {18 return struct {
18 bits: Int,19 bits: Int,
1920
20 pub const Int = @Type(.{ .Int = .{ .bits = bit_count, .is_signed = false } });21 pub const Int = meta.Int(false, bit_count);
21 pub const bit_count = @typeInfo(Fields).Struct.fields.len;22 pub const bit_count = @typeInfo(Fields).Struct.fields.len;
2223
24 pub const FieldEnum = blk: {
25 comptime var fields: []const TypeInfo.EnumField = &[_]TypeInfo.EnumField{};
26 inline for (@typeInfo(Fields).Struct.fields) |struct_field, i| {
27 const field = TypeInfo.EnumField{ .name = struct_field.name, .value = i };
28 fields = fields ++ [_]TypeInfo.EnumField{field};
29 }
30 break :blk @Type(.{
31 .Enum = .{
32 .layout = .Auto,
33 .tag_type = std.math.IntFittingRange(0, bit_count - 1),
34 .fields = fields,
35 .decls = &[_]TypeInfo.Declaration{},
36 .is_exhaustive = true,
37 },
38 });
39 };
40
41 pub const InitStruct = blk: {
42 comptime var fields: []const TypeInfo.StructField = &[_]TypeInfo.StructField{};
43 inline for (@typeInfo(Fields).Struct.fields) |struct_field, i| {
44 const field = TypeInfo.StructField{
45 .name = struct_field.name,
46 .field_type = ?struct_field.field_type,
47 .default_value = @as(??struct_field.field_type, @as(?struct_field.field_type, null)),
48 };
49 fields = fields ++ [_]TypeInfo.StructField{field};
50 }
51 break :blk @Type(.{
52 .Struct = .{
53 .layout = .Auto,
54 .fields = fields,
55 .decls = &[_]TypeInfo.Declaration{},
56 .is_tuple = false,
57 },
58 });
59 };
60
23 pub const Self = @This();61 pub const Self = @This();
2462
25 pub fn has(self: Self, comptime name: []const u8) bool {63 pub fn has(self: Self, comptime field: FieldEnum) bool {
26 const field_index = meta.fieldIndex(Fields, name).?;64 const field_index = @enumToInt(field);
27 return (self.bits & (1 << field_index)) != 0;65 return (self.bits & (1 << field_index)) != 0;
28 }66 }
2967
30 pub fn get(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) ?Field(name) {68 pub fn get(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime field: FieldEnum) ?Field(field) {
31 if (!self.has(name))69 if (!self.has(field))
32 return null;70 return null;
33 return self.ptrConst(p, name).*;71 return self.ptrConst(p, field).*;
34 }72 }
3573
36 pub fn setFlag(self: *Self, comptime name: []const u8) void {74 pub fn setFlag(self: *Self, comptime field: FieldEnum) void {
37 const field_index = meta.fieldIndex(Fields, name).?;75 const field_index = @enumToInt(field);
38 self.bits |= 1 << field_index;76 self.bits |= 1 << field_index;
39 }77 }
4078
41 /// `fields` is a struct with each field set to an optional value.79 /// `fields` is a struct with each field set to an optional value.
42 /// Missing fields are assumed to be `null`.80 /// Missing fields are assumed to be `null`.
43 /// Only the non-null bits are observed and are used to set the flag bits.81 /// Only the non-null bits are observed and are used to set the flag bits.
44 pub fn init(fields: anytype) Self {82 pub fn init(fields: InitStruct) Self {
45 var self: Self = .{ .bits = 0 };83 var self: Self = .{ .bits = 0 };
46 inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {84 inline for (@typeInfo(Fields).Struct.fields) |field, i| {
47 const opt: ?Field(field.name) = @field(fields, field.name);85 if (@field(fields, field.name)) |_|
48 const field_index = meta.fieldIndex(Fields, field.name).?;86 self.bits |= 1 << i;
49 self.bits |= @as(Int, @boolToInt(opt != null)) << field_index;
50 }87 }
51 return self;88 return self;
52 }89 }
5390
54 /// `fields` is a struct with each field set to an optional value (same as `init`).91 /// `fields` is a struct with each field set to an optional value (same as `init`).
55 /// Missing fields are assumed to be `null`.92 /// Missing fields are assumed to be `null`.
56 pub fn setMany(self: Self, p: [*]align(@alignOf(Fields)) u8, fields: anytype) void {93 pub fn setMany(self: Self, p: [*]align(@alignOf(Fields)) u8, fields: InitStruct) void {
57 inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {94 inline for (@typeInfo(Fields).Struct.fields) |field, i| {
58 const opt: ?Field(field.name) = @field(fields, field.name);95 if (@field(fields, field.name)) |value|
59 if (opt) |value| {96 self.set(p, @intToEnum(FieldEnum, i), value);
60 self.set(p, field.name, value);
61 }
62 }97 }
63 }98 }
6499
65 pub fn set(100 pub fn set(
66 self: Self,101 self: Self,
67 p: [*]align(@alignOf(Fields)) u8,102 p: [*]align(@alignOf(Fields)) u8,
68 comptime name: []const u8,103 comptime field: FieldEnum,
69 value: Field(name),104 value: Field(field),
70 ) void {105 ) void {
71 self.ptr(p, name).* = value;106 self.ptr(p, field).* = value;
72 }107 }
73108
74 pub fn ptr(self: Self, p: [*]align(@alignOf(Fields)) u8, comptime name: []const u8) *Field(name) {109 pub fn ptr(self: Self, p: [*]align(@alignOf(Fields)) u8, comptime field: FieldEnum) *Field(field) {
75 if (@sizeOf(Field(name)) == 0)110 if (@sizeOf(Field(field)) == 0)
76 return undefined;111 return undefined;
77 const off = self.offset(p, name);112 const off = self.offset(p, field);
78 return @ptrCast(*Field(name), @alignCast(@alignOf(Field(name)), p + off));113 return @ptrCast(*Field(field), @alignCast(@alignOf(Field(field)), p + off));
79 }114 }
80115
81 pub fn ptrConst(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) *const Field(name) {116 pub fn ptrConst(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime field: FieldEnum) *const Field(field) {
82 if (@sizeOf(Field(name)) == 0)117 if (@sizeOf(Field(field)) == 0)
83 return undefined;118 return undefined;
84 const off = self.offset(p, name);119 const off = self.offset(p, field);
85 return @ptrCast(*const Field(name), @alignCast(@alignOf(Field(name)), p + off));120 return @ptrCast(*const Field(field), @alignCast(@alignOf(Field(field)), p + off));
86 }121 }
87122
88 pub fn offset(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) usize {123 pub fn offset(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime field: FieldEnum) usize {
89 var off: usize = 0;124 var off: usize = 0;
90 inline for (@typeInfo(Fields).Struct.fields) |field, i| {125 inline for (@typeInfo(Fields).Struct.fields) |field_info, i| {
91 const active = (self.bits & (1 << i)) != 0;126 const active = (self.bits & (1 << i)) != 0;
92 if (comptime mem.eql(u8, field.name, name)) {127 if (i == @enumToInt(field)) {
93 assert(active);128 assert(active);
94 return mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));129 return mem.alignForwardGeneric(usize, off, @alignOf(field_info.field_type));
95 } else if (active) {130 } else if (active) {
96 off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));131 off = mem.alignForwardGeneric(usize, off, @alignOf(field_info.field_type));
97 off += @sizeOf(field.field_type);132 off += @sizeOf(field_info.field_type);
98 }133 }
99 }134 }
100 @compileError("no field named " ++ name ++ " in type " ++ @typeName(Fields));
101 }135 }
102136
103 pub fn Field(comptime name: []const u8) type {137 pub fn Field(comptime field: FieldEnum) type {
104 return meta.fieldInfo(Fields, name).field_type;138 inline for (@typeInfo(Fields).Struct.fields) |field_info, i| {
139 if (i == @enumToInt(field))
140 return field_info.field_type;
141 }
105 }142 }
106143
107 pub fn sizeInBytes(self: Self) usize {144 pub fn sizeInBytes(self: Self) usize {
...@@ -125,6 +162,8 @@ test "TrailerFlags" {...@@ -125,6 +162,8 @@ test "TrailerFlags" {
125 b: bool,162 b: bool,
126 c: u64,163 c: u64,
127 });164 });
165 testing.expectEqual(u2, @TagType(Flags.FieldEnum));
166
128 var flags = Flags.init(.{167 var flags = Flags.init(.{
129 .b = true,168 .b = true,
130 .c = 1234,169 .c = 1234,
...@@ -132,19 +171,19 @@ test "TrailerFlags" {...@@ -132,19 +171,19 @@ test "TrailerFlags" {
132 const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact);171 const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact);
133 defer testing.allocator.free(slice);172 defer testing.allocator.free(slice);
134173
135 flags.set(slice.ptr, "b", false);174 flags.set(slice.ptr, .b, false);
136 flags.set(slice.ptr, "c", 12345678);175 flags.set(slice.ptr, .c, 12345678);
137176
138 testing.expect(flags.get(slice.ptr, "a") == null);177 testing.expect(flags.get(slice.ptr, .a) == null);
139 testing.expect(!flags.get(slice.ptr, "b").?);178 testing.expect(!flags.get(slice.ptr, .b).?);
140 testing.expect(flags.get(slice.ptr, "c").? == 12345678);179 testing.expect(flags.get(slice.ptr, .c).? == 12345678);
141180
142 flags.setMany(slice.ptr, .{181 flags.setMany(slice.ptr, .{
143 .b = true,182 .b = true,
144 .c = 5678,183 .c = 5678,
145 });184 });
146185
147 testing.expect(flags.get(slice.ptr, "a") == null);186 testing.expect(flags.get(slice.ptr, .a) == null);
148 testing.expect(flags.get(slice.ptr, "b").?);187 testing.expect(flags.get(slice.ptr, .b).?);
149 testing.expect(flags.get(slice.ptr, "c").? == 5678);188 testing.expect(flags.get(slice.ptr, .c).? == 5678);
150}189}