2020-07-15 01:17:37 +01:00
|
|
|
const std = @import("../std.zig");
|
|
|
|
const meta = std.meta;
|
|
|
|
const testing = std.testing;
|
|
|
|
const mem = std.mem;
|
|
|
|
const assert = std.debug.assert;
|
2020-08-27 22:00:15 +01:00
|
|
|
const TypeInfo = std.builtin.TypeInfo;
|
2020-07-15 01:17:37 +01:00
|
|
|
|
|
|
|
/// 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,
|
|
|
|
|
2020-10-17 13:09:59 +01:00
|
|
|
pub const Int = meta.Int(.unsigned, bit_count);
|
2020-07-15 01:17:37 +01:00
|
|
|
pub const bit_count = @typeInfo(Fields).Struct.fields.len;
|
|
|
|
|
2020-12-13 11:27:33 +00:00
|
|
|
pub const FieldEnum = std.meta.FieldEnum(Fields);
|
2020-08-27 22:00:15 +01:00
|
|
|
|
|
|
|
pub const InitStruct = blk: {
|
2020-08-27 23:01:08 +01:00
|
|
|
comptime var fields: [bit_count]TypeInfo.StructField = undefined;
|
2020-08-27 22:00:15 +01:00
|
|
|
inline for (@typeInfo(Fields).Struct.fields) |struct_field, i| {
|
2020-08-27 23:01:08 +01:00
|
|
|
fields[i] = TypeInfo.StructField{
|
2020-08-27 22:00:15 +01:00
|
|
|
.name = struct_field.name,
|
|
|
|
.field_type = ?struct_field.field_type,
|
2020-08-27 23:01:08 +01:00
|
|
|
.default_value = @as(
|
|
|
|
??struct_field.field_type,
|
|
|
|
@as(?struct_field.field_type, null),
|
|
|
|
),
|
2020-09-02 01:55:36 +01:00
|
|
|
.is_comptime = false,
|
2020-09-26 15:48:26 +01:00
|
|
|
.alignment = @alignOf(?struct_field.field_type),
|
2020-08-27 22:00:15 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
break :blk @Type(.{
|
|
|
|
.Struct = .{
|
|
|
|
.layout = .Auto,
|
2020-08-27 23:01:08 +01:00
|
|
|
.fields = &fields,
|
2020-08-27 22:00:15 +01:00
|
|
|
.decls = &[_]TypeInfo.Declaration{},
|
|
|
|
.is_tuple = false,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-07-15 01:17:37 +01:00
|
|
|
pub const Self = @This();
|
|
|
|
|
2020-08-27 22:00:15 +01:00
|
|
|
pub fn has(self: Self, comptime field: FieldEnum) bool {
|
|
|
|
const field_index = @enumToInt(field);
|
2020-07-15 01:17:37 +01:00
|
|
|
return (self.bits & (1 << field_index)) != 0;
|
|
|
|
}
|
|
|
|
|
2020-08-27 22:00:15 +01:00
|
|
|
pub fn get(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime field: FieldEnum) ?Field(field) {
|
|
|
|
if (!self.has(field))
|
2020-07-15 01:17:37 +01:00
|
|
|
return null;
|
2020-08-27 22:00:15 +01:00
|
|
|
return self.ptrConst(p, field).*;
|
2020-07-15 01:17:37 +01:00
|
|
|
}
|
|
|
|
|
2020-08-27 22:00:15 +01:00
|
|
|
pub fn setFlag(self: *Self, comptime field: FieldEnum) void {
|
|
|
|
const field_index = @enumToInt(field);
|
2020-07-15 01:17:37 +01:00
|
|
|
self.bits |= 1 << field_index;
|
|
|
|
}
|
|
|
|
|
stage2: VarDecl and FnProto take advantage of TrailerFlags API
These AST nodes now have a flags field and then a bunch of optional
trailing objects. The end result is lower memory usage and consequently
better performance. This is part of an ongoing effort to reduce the
amount of memory parsed ASTs take up.
Running `zig fmt` on the std lib:
* cache-misses: 2,554,321 => 2,534,745
* instructions: 3,293,220,119 => 3,302,479,874
* peak memory: 74.0 MiB => 73.0 MiB
Holding the entire std lib AST in memory at the same time:
93.9 MiB => 88.5 MiB
2020-07-15 09:38:31 +01:00
|
|
|
/// `fields` is a struct with each field set to an optional value.
|
|
|
|
/// Only the non-null bits are observed and are used to set the flag bits.
|
2020-08-27 22:00:15 +01:00
|
|
|
pub fn init(fields: InitStruct) Self {
|
2020-07-15 01:17:37 +01:00
|
|
|
var self: Self = .{ .bits = 0 };
|
2020-08-27 22:00:15 +01:00
|
|
|
inline for (@typeInfo(Fields).Struct.fields) |field, i| {
|
|
|
|
if (@field(fields, field.name)) |_|
|
|
|
|
self.bits |= 1 << i;
|
2020-07-15 01:17:37 +01:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
stage2: VarDecl and FnProto take advantage of TrailerFlags API
These AST nodes now have a flags field and then a bunch of optional
trailing objects. The end result is lower memory usage and consequently
better performance. This is part of an ongoing effort to reduce the
amount of memory parsed ASTs take up.
Running `zig fmt` on the std lib:
* cache-misses: 2,554,321 => 2,534,745
* instructions: 3,293,220,119 => 3,302,479,874
* peak memory: 74.0 MiB => 73.0 MiB
Holding the entire std lib AST in memory at the same time:
93.9 MiB => 88.5 MiB
2020-07-15 09:38:31 +01:00
|
|
|
/// `fields` is a struct with each field set to an optional value (same as `init`).
|
2020-08-27 22:00:15 +01:00
|
|
|
pub fn setMany(self: Self, p: [*]align(@alignOf(Fields)) u8, fields: InitStruct) void {
|
|
|
|
inline for (@typeInfo(Fields).Struct.fields) |field, i| {
|
|
|
|
if (@field(fields, field.name)) |value|
|
|
|
|
self.set(p, @intToEnum(FieldEnum, i), value);
|
stage2: VarDecl and FnProto take advantage of TrailerFlags API
These AST nodes now have a flags field and then a bunch of optional
trailing objects. The end result is lower memory usage and consequently
better performance. This is part of an ongoing effort to reduce the
amount of memory parsed ASTs take up.
Running `zig fmt` on the std lib:
* cache-misses: 2,554,321 => 2,534,745
* instructions: 3,293,220,119 => 3,302,479,874
* peak memory: 74.0 MiB => 73.0 MiB
Holding the entire std lib AST in memory at the same time:
93.9 MiB => 88.5 MiB
2020-07-15 09:38:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:17:37 +01:00
|
|
|
pub fn set(
|
|
|
|
self: Self,
|
|
|
|
p: [*]align(@alignOf(Fields)) u8,
|
2020-08-27 22:00:15 +01:00
|
|
|
comptime field: FieldEnum,
|
|
|
|
value: Field(field),
|
2020-07-15 01:17:37 +01:00
|
|
|
) void {
|
2020-08-27 22:00:15 +01:00
|
|
|
self.ptr(p, field).* = value;
|
2020-07-15 01:17:37 +01:00
|
|
|
}
|
|
|
|
|
2020-08-27 22:00:15 +01:00
|
|
|
pub fn ptr(self: Self, p: [*]align(@alignOf(Fields)) u8, comptime field: FieldEnum) *Field(field) {
|
|
|
|
if (@sizeOf(Field(field)) == 0)
|
stage2: VarDecl and FnProto take advantage of TrailerFlags API
These AST nodes now have a flags field and then a bunch of optional
trailing objects. The end result is lower memory usage and consequently
better performance. This is part of an ongoing effort to reduce the
amount of memory parsed ASTs take up.
Running `zig fmt` on the std lib:
* cache-misses: 2,554,321 => 2,534,745
* instructions: 3,293,220,119 => 3,302,479,874
* peak memory: 74.0 MiB => 73.0 MiB
Holding the entire std lib AST in memory at the same time:
93.9 MiB => 88.5 MiB
2020-07-15 09:38:31 +01:00
|
|
|
return undefined;
|
2021-06-21 20:44:25 +01:00
|
|
|
const off = self.offset(field);
|
2020-08-27 22:00:15 +01:00
|
|
|
return @ptrCast(*Field(field), @alignCast(@alignOf(Field(field)), p + off));
|
2020-07-15 01:17:37 +01:00
|
|
|
}
|
|
|
|
|
2020-08-27 22:00:15 +01:00
|
|
|
pub fn ptrConst(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime field: FieldEnum) *const Field(field) {
|
|
|
|
if (@sizeOf(Field(field)) == 0)
|
stage2: VarDecl and FnProto take advantage of TrailerFlags API
These AST nodes now have a flags field and then a bunch of optional
trailing objects. The end result is lower memory usage and consequently
better performance. This is part of an ongoing effort to reduce the
amount of memory parsed ASTs take up.
Running `zig fmt` on the std lib:
* cache-misses: 2,554,321 => 2,534,745
* instructions: 3,293,220,119 => 3,302,479,874
* peak memory: 74.0 MiB => 73.0 MiB
Holding the entire std lib AST in memory at the same time:
93.9 MiB => 88.5 MiB
2020-07-15 09:38:31 +01:00
|
|
|
return undefined;
|
2021-06-21 20:44:25 +01:00
|
|
|
const off = self.offset(field);
|
2020-08-27 22:00:15 +01:00
|
|
|
return @ptrCast(*const Field(field), @alignCast(@alignOf(Field(field)), p + off));
|
2020-07-15 01:17:37 +01:00
|
|
|
}
|
|
|
|
|
2021-06-21 20:44:25 +01:00
|
|
|
pub fn offset(self: Self, comptime field: FieldEnum) usize {
|
2020-07-15 01:17:37 +01:00
|
|
|
var off: usize = 0;
|
2020-08-27 22:00:15 +01:00
|
|
|
inline for (@typeInfo(Fields).Struct.fields) |field_info, i| {
|
2020-07-15 01:17:37 +01:00
|
|
|
const active = (self.bits & (1 << i)) != 0;
|
2020-08-27 22:00:15 +01:00
|
|
|
if (i == @enumToInt(field)) {
|
2020-07-15 01:17:37 +01:00
|
|
|
assert(active);
|
2020-08-27 22:00:15 +01:00
|
|
|
return mem.alignForwardGeneric(usize, off, @alignOf(field_info.field_type));
|
2020-07-15 01:17:37 +01:00
|
|
|
} else if (active) {
|
2020-08-27 22:00:15 +01:00
|
|
|
off = mem.alignForwardGeneric(usize, off, @alignOf(field_info.field_type));
|
|
|
|
off += @sizeOf(field_info.field_type);
|
2020-07-15 01:17:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 22:00:15 +01:00
|
|
|
pub fn Field(comptime field: FieldEnum) type {
|
2020-12-13 12:14:51 +00:00
|
|
|
return @typeInfo(Fields).Struct.fields[@enumToInt(field)].field_type;
|
2020-07-15 01:17:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sizeInBytes(self: Self) usize {
|
|
|
|
var off: usize = 0;
|
|
|
|
inline for (@typeInfo(Fields).Struct.fields) |field, i| {
|
stage2: VarDecl and FnProto take advantage of TrailerFlags API
These AST nodes now have a flags field and then a bunch of optional
trailing objects. The end result is lower memory usage and consequently
better performance. This is part of an ongoing effort to reduce the
amount of memory parsed ASTs take up.
Running `zig fmt` on the std lib:
* cache-misses: 2,554,321 => 2,534,745
* instructions: 3,293,220,119 => 3,302,479,874
* peak memory: 74.0 MiB => 73.0 MiB
Holding the entire std lib AST in memory at the same time:
93.9 MiB => 88.5 MiB
2020-07-15 09:38:31 +01:00
|
|
|
if (@sizeOf(field.field_type) == 0)
|
|
|
|
continue;
|
2020-07-15 01:17:37 +01:00
|
|
|
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,
|
|
|
|
});
|
2021-05-04 18:47:26 +01:00
|
|
|
try testing.expectEqual(u2, meta.Tag(Flags.FieldEnum));
|
2020-08-27 22:00:15 +01:00
|
|
|
|
2020-07-15 01:17:37 +01:00
|
|
|
var flags = Flags.init(.{
|
|
|
|
.b = true,
|
stage2: VarDecl and FnProto take advantage of TrailerFlags API
These AST nodes now have a flags field and then a bunch of optional
trailing objects. The end result is lower memory usage and consequently
better performance. This is part of an ongoing effort to reduce the
amount of memory parsed ASTs take up.
Running `zig fmt` on the std lib:
* cache-misses: 2,554,321 => 2,534,745
* instructions: 3,293,220,119 => 3,302,479,874
* peak memory: 74.0 MiB => 73.0 MiB
Holding the entire std lib AST in memory at the same time:
93.9 MiB => 88.5 MiB
2020-07-15 09:38:31 +01:00
|
|
|
.c = 1234,
|
2020-07-15 01:17:37 +01:00
|
|
|
});
|
|
|
|
const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact);
|
|
|
|
defer testing.allocator.free(slice);
|
|
|
|
|
2020-08-27 22:00:15 +01:00
|
|
|
flags.set(slice.ptr, .b, false);
|
|
|
|
flags.set(slice.ptr, .c, 12345678);
|
2020-07-15 01:17:37 +01:00
|
|
|
|
2021-05-04 18:47:26 +01:00
|
|
|
try testing.expect(flags.get(slice.ptr, .a) == null);
|
|
|
|
try testing.expect(!flags.get(slice.ptr, .b).?);
|
|
|
|
try testing.expect(flags.get(slice.ptr, .c).? == 12345678);
|
stage2: VarDecl and FnProto take advantage of TrailerFlags API
These AST nodes now have a flags field and then a bunch of optional
trailing objects. The end result is lower memory usage and consequently
better performance. This is part of an ongoing effort to reduce the
amount of memory parsed ASTs take up.
Running `zig fmt` on the std lib:
* cache-misses: 2,554,321 => 2,534,745
* instructions: 3,293,220,119 => 3,302,479,874
* peak memory: 74.0 MiB => 73.0 MiB
Holding the entire std lib AST in memory at the same time:
93.9 MiB => 88.5 MiB
2020-07-15 09:38:31 +01:00
|
|
|
|
|
|
|
flags.setMany(slice.ptr, .{
|
|
|
|
.b = true,
|
|
|
|
.c = 5678,
|
|
|
|
});
|
|
|
|
|
2021-05-04 18:47:26 +01:00
|
|
|
try testing.expect(flags.get(slice.ptr, .a) == null);
|
|
|
|
try testing.expect(flags.get(slice.ptr, .b).?);
|
|
|
|
try testing.expect(flags.get(slice.ptr, .c).? == 5678);
|
2020-07-15 01:17:37 +01:00
|
|
|
}
|