| ... | @@ -8,6 +8,7 @@ const meta = std.meta; | ... | @@ -8,6 +8,7 @@ const meta = std.meta; |
| 8 | const testing = std.testing; | 8 | const testing = std.testing; |
| 9 | const mem = std.mem; | 9 | const mem = std.mem; |
| 10 | const assert = std.debug.assert; | 10 | const assert = std.debug.assert; |
| | 11 | const TypeInfo = std.builtin.TypeInfo; |
| 11 | | 12 | |
| 12 | /// This is useful for saving memory when allocating an object that has many | 13 | /// This is useful for saving memory when allocating an object that has many |
| 13 | /// optional components. The optional objects are allocated sequentially in | 14 | /// 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, |
| 19 | | 20 | |
| 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; |
| 22 | | 23 | |
| | 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(); |
| 24 | | 62 | |
| 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 | } |
| 29 | | 67 | |
| 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 | } |
| 35 | | 73 | |
| 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 | } |
| 40 | | 78 | |
| 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 | } |
| 53 | | 90 | |
| 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 | } |
| 64 | | 99 | |
| 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 | } |
| 73 | | 108 | |
| 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 | } |
| 80 | | 115 | |
| 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 | } |
| 87 | | 122 | |
| 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 | } |
| 102 | | 136 | |
| 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 | } |
| 106 | | 143 | |
| 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); |
| 134 | | 173 | |
| 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); |
| 137 | | 176 | |
| 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); |
| 141 | | 180 | |
| 142 | flags.setMany(slice.ptr, .{ | 181 | flags.setMany(slice.ptr, .{ |
| 143 | .b = true, | 182 | .b = true, |
| 144 | .c = 5678, | 183 | .c = 5678, |
| 145 | }); | 184 | }); |
| 146 | | 185 | |
| 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 | } |