feature. See also
. The project being documented here (as the example) is the Zig library itself.
Configuration.Storage
pub const Storage = enum
File
Code
pub const Storage = enum {
flag_optional,
enum_optional,
extended,
length_prefixed_list,
flag_length_prefixed_list,
union_list,
flag_union,
multi_list,
flag_list,
pub fn FlagOptional(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
comptime ValueArg: type,
) type {
return struct {
value: ?Value,
pub const storage: Storage = .flag_optional;
pub const flags = flags_arg;
pub const flag = flag_arg;
pub const Value = ValueArg;
};
}
pub fn FlagUnion(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
comptime UnionArg: type,
) type {
return struct {
u: Union,
pub const storage: Storage = .flag_union;
pub const flags = flags_arg;
pub const flag = flag_arg;
pub const Union = UnionArg;
pub const Tag = @typeInfo(Union).@"union".tag_type.?;
};
}
pub fn EnumOptional(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
comptime tag_arg: @EnumLiteral(),
comptime ValueArg: type,
) type {
return struct {
value: ?Value,
pub const storage: Storage = .enum_optional;
pub const flags = flags_arg;
pub const flag = flag_arg;
pub const tag = tag_arg;
pub const Value = ValueArg;
};
}
pub fn Extended(comptime BaseFlags: type, comptime U: type) type {
return enum(u32) {
_,
pub const storage: Storage = .extended;
pub fn tag(this: @This(), c: *const Configuration) @FieldType(BaseFlags, "tag") {
const base_flags: BaseFlags = @bitCast(c.extra[@backingInt(this)]);
return base_flags.tag;
}
pub fn cast(this: @This(), c: *const Configuration, comptime S: type) ?S {
const wanted_tag = blk: {
const info = @typeInfo(S.Flags).@"struct";
break :blk info.field_attrs[0].defaultValue(info.field_types[0]).?;
};
const base_flags: BaseFlags = @bitCast(c.extra[@backingInt(this)]);
if (base_flags.tag != wanted_tag) return null;
var i: usize = @backingInt(this);
return data(c.extra, &i, S);
}
pub fn get(this: @This(), buffer: []const u32) U {
var i: usize = @backingInt(this);
const base_flags: BaseFlags = @bitCast(buffer[i]);
return switch (base_flags.tag) {
inline else => |t| @unionInit(U, @tagName(t), data(buffer, &i, @FieldType(U, @tagName(t)))),
};
}
};
}
pub fn FlagLengthPrefixedList(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
comptime ElemArg: type,
) type {
return struct {
slice: []const Elem,
pub const storage: Storage = .flag_length_prefixed_list;
pub const flags = flags_arg;
pub const flag = flag_arg;
pub const Elem = ElemArg;
pub fn initErased(s: []const u32) @This() {
return .{ .slice = @ptrCast(s) };
}
};
}
pub fn LengthPrefixedList(comptime ElemArg: type) type {
return struct {
slice: []const Elem,
pub const storage: Storage = .length_prefixed_list;
pub const Elem = ElemArg;
pub fn initErased(s: []const u32) @This() {
return .{ .slice = @ptrCast(s) };
}
};
}
pub fn FlagList(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
comptime ElemArg: type,
) type {
return struct {
slice: []const Elem,
pub const storage: Storage = .flag_list;
pub const flags = flags_arg;
pub const flag = flag_arg;
pub const Elem = ElemArg;
pub fn initErased(s: []const u32) @This() {
return .{ .slice = @ptrCast(s) };
}
};
}
pub fn MultiList(comptime ElemArg: type) type {
return struct {
mal: std.MultiArrayList(Elem),
pub const storage: Storage = .multi_list;
pub const Elem = ElemArg;
};
}
pub fn UnionList(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
comptime UnionArg: type,
) type {
return struct {
data: ?*const anyopaque,
len: usize,
pub const storage: Storage = .union_list;
pub const flags = flags_arg;
pub const flag = flag_arg;
pub const Union = UnionArg;
pub const Tag = @typeInfo(Union).@"union".tag_type.?;
pub const MetaInt = @Int(.unsigned, @bitSizeOf(Tag) + 1);
pub const Meta = packed struct(MetaInt) {
tag: Tag,
last: bool,
};
pub fn init(s: []const Union) @This() {
return .{ .data = s.ptr, .len = s.len };
}
pub fn slice(this: *const @This(), extra: []const u32) []const u32 {
return extra[@intFromPtr(this.data)..][0..this.len];
}
pub fn get(this: *const @This(), extra: []const u32, i: usize) Union {
const elem = slice(this, extra)[i];
return switch (this.tag(extra, i)) {
inline else => |comptime_tag| @unionInit(Union, @tagName(comptime_tag), @fromBackingInt(@intCast(elem))),
};
}
pub fn tag(this: *const @This(), extra: []const u32, i: usize) Tag {
const start = @intFromPtr(this.data);
const meta_start = start - (this.len * @bitSizeOf(Meta) + 31) / 32;
return loadBits(u32, extra[meta_start..], i * @bitSizeOf(Meta), Meta).tag;
}
fn extraLen(len: usize) usize {
return len + (len * @bitSizeOf(Meta) + 31) / 32;
}
};
}
pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize {
var end = i;
_ = data(buffer, &end, S);
return end - i;
}
pub fn data(buffer: []const u32, i: *usize, comptime T: type) T {
switch (@typeInfo(T)) {
.@"struct" => |info| {
var result: T = undefined;
inline for (info.field_names, info.field_types) |field_name, field_type| {
@field(result, field_name) = dataField(buffer, i, &result, field_type);
}
return result;
},
.@"union" => |info| {
const flags: T.Flags = @bitCast(buffer[i.*]);
return switch (flags.tag) {
inline else => |comptime_tag| @unionInit(
T,
@tagName(comptime_tag),
data(buffer, i, info.field_types[@backingInt(comptime_tag)]),
),
};
},
else => comptime unreachable,
}
}
fn dataField(buffer: []const u32, i: *usize, container: anytype, comptime Field: type) Field {
switch (@typeInfo(Field)) {
.void => return {},
.int => |info| switch (info.bits) {
32 => {
defer i.* += 1;
return buffer[i.*];
},
64 => {
defer i.* += 2;
return @bitCast(buffer[i.*..][0..2].*);
},
else => comptime unreachable,
},
.@"enum" => {
defer i.* += 1;
return @fromBackingInt(@intCast(buffer[i.*]));
},
.@"struct" => |info| switch (info.layout) {
.@"packed" => switch (info.backing_integer.?) {
u32 => {
defer i.* += 1;
return @bitCast(buffer[i.*]);
},
u64 => {
defer i.* += 2;
return @bitCast(buffer[i.*..][0..2].*);
},
else => comptime unreachable,
},
.auto => switch (Field) {
std.Target.Cpu.Feature.Set => {
const u32_count = (Field.usize_count * @sizeOf(usize)) / @sizeOf(u32);
defer i.* += u32_count;
return .{ .ints = @as(
*align(@alignOf(u32)) const [Field.usize_count]usize,
@ptrCast(buffer[i.*..][0..u32_count]),
).* };
},
else => switch (Field.storage) {
.flag_optional => {
const flags = @field(container, @tagName(Field.flags));
const flag = @field(flags, @tagName(Field.flag));
return .{
.value = if (flag) dataField(buffer, i, container, Field.Value) else null,
};
},
.flag_union => {
const flags = @field(container, @tagName(Field.flags));
const tag: Field.Tag = @field(flags, @tagName(Field.flag));
return .{
.u = switch (tag) {
inline else => |comptime_tag| @unionInit(
Field.Union,
@tagName(comptime_tag),
dataField(
buffer,
i,
container,
@typeInfo(Field.Union).@"union".field_types[@backingInt(comptime_tag)],
),
),
},
};
},
.enum_optional => {
const flags = @field(container, @tagName(Field.flags));
const tag = @field(flags, @tagName(Field.flag));
const match = tag == Field.tag;
return .{
.value = if (match) dataField(buffer, i, container, Field.Value) else null,
};
},
.extended => @compileError("unimplemented"),
.length_prefixed_list => {
const n = @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
const data_start = i.* + 1;
const buf_len = buffer[data_start - 1] * n;
defer i.* = data_start + buf_len;
return .{ .slice = @ptrCast(buffer[data_start..][0..buf_len]) };
},
.flag_length_prefixed_list => {
const flags = @field(container, @tagName(Field.flags));
const flag = @field(flags, @tagName(Field.flag));
if (!flag) return .{ .slice = &.{} };
const n = @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
const data_start = i.* + 1;
const buf_len = buffer[data_start - 1] * n;
defer i.* = data_start + buf_len;
return .{ .slice = @ptrCast(buffer[data_start..][0..buf_len]) };
},
.flag_list => {
const flags = @field(container, @tagName(Field.flags));
const len: u32 = @field(flags, @tagName(Field.flag));
const data_start = i.*;
defer i.* = data_start + len;
return .{ .slice = @ptrCast(buffer[data_start..][0..len]) };
},
.multi_list => {
const data_start = i.* + 1;
const len = buffer[data_start - 1];
defer i.* = data_start + len * @typeInfo(Field.Elem).@"struct".field_names.len;
return .{ .mal = .{
.bytes = @ptrCast(@constCast(buffer[data_start..][0..len])),
.len = len,
.capacity = len,
} };
},
.union_list => {
const flags = @field(container, @tagName(Field.flags));
const flag = @field(flags, @tagName(Field.flag));
if (!flag) return .{ .data = null, .len = 0 };
const meta_start = i.*;
const meta_buffer = buffer[meta_start..];
var len: u32 = 0;
var bit_offset: usize = 0;
while (true) : (bit_offset += @bitSizeOf(Field.Meta)) {
const meta = loadBits(u32, meta_buffer, bit_offset, Field.Meta);
len += 1;
if (meta.last) break;
}
const end = meta_start + Field.extraLen(len);
i.* = end;
return .{ .data = @ptrFromInt(end - len), .len = len };
},
},
},
.@"extern" => {
const n = @divExact(@sizeOf(Field), @sizeOf(u32));
defer i.* += n;
const ptr: *align(@alignOf(u32)) const Field = @ptrCast(buffer[i.*..][0..n]);
return ptr.*;
},
},
else => comptime unreachable,
}
}
fn setExtra(buffer: []u32, index: usize, extra: anytype) usize {
const info = @typeInfo(@TypeOf(extra)).@"struct";
var i = index;
inline for (info.field_names, info.field_types) |field_name, field_type| {
i += setExtraField(buffer, i, field_type, @field(extra, field_name));
}
return i;
}
fn extraFieldLen(field: anytype) usize {
const Field = @TypeOf(field);
return switch (@typeInfo(Field)) {
.void => 0,
.int => |info| switch (info.bits) {
32 => 1,
64 => 2,
else => comptime unreachable,
},
.@"enum" => 1,
.@"struct" => |info| switch (info.layout) {
.@"packed" => switch (info.backing_integer.?) {
u32 => 1,
u64 => 2,
else => comptime unreachable,
},
.auto => switch (Field.storage) {
.flag_optional, .enum_optional => (@sizeOf(Field.Value) + 3) / 4,
.extended => 1,
.length_prefixed_list,
.flag_length_prefixed_list,
.flag_list,
=> 1 + @divExact(@sizeOf(Field.Elem), @sizeOf(u32)) * field.slice.len,
.multi_list => 1 + field.mal.len * @typeInfo(Field.Elem).@"struct".field_names.len,
.union_list => Field.extraLen(field.len),
.flag_union => switch (field.u) {
inline else => |v| extraFieldLen(v),
},
},
.@"extern" => @divExact(@sizeOf(Field), @sizeOf(u32)),
},
else => @compileError("bad type: " ++ @typeName(Field)),
};
}
fn extraLen(extra: anytype) usize {
const field_names = @typeInfo(@TypeOf(extra)).@"struct".field_names;
var i: usize = 0;
inline for (field_names) |name| {
i += Storage.extraFieldLen(@field(extra, name));
}
return i;
}
inline fn setExtraField(buffer: []u32, i: usize, comptime Field: type, value: anytype) usize {
switch (@typeInfo(Field)) {
.void => return 0,
.int => |info| switch (info.bits) {
32 => {
buffer[i] = value;
return 1;
},
64 => {
buffer[i..][0..2].* = @bitCast(value);
return 2;
},
else => comptime unreachable,
},
.@"enum" => {
buffer[i] = @backingInt(value);
return 1;
},
.@"struct" => |info| switch (info.layout) {
.@"packed" => switch (info.backing_integer.?) {
u32 => {
buffer[i] = @bitCast(value);
return 1;
},
u64 => {
buffer[i..][0..2].* = @bitCast(value);
return 2;
},
else => comptime unreachable,
},
.auto => switch (Field) {
std.Target.Cpu.Feature.Set => {
const casted: []const u32 = @ptrCast(&value.ints);
@memcpy(buffer[i..][0..casted.len], casted);
return casted.len;
},
else => switch (Field.storage) {
.flag_optional, .enum_optional => {
return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0;
},
.flag_union => return switch (value.u) {
inline else => |x| setExtraField(buffer, i, @TypeOf(x), x),
},
.extended => @compileError("unimplemented"),
.flag_length_prefixed_list => {
const len: u32 = @intCast(value.slice.len);
if (len == 0) return 0;
buffer[i] = len;
const buf_len = len * @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
@memcpy(buffer[i + 1 ..][0..buf_len], @as([]const u32, @ptrCast(value.slice)));
return 1 + buf_len;
},
.length_prefixed_list => {
const len: u32 = @intCast(value.slice.len);
buffer[i] = len;
const buf_len = len * @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
@memcpy(buffer[i + 1 ..][0..buf_len], @as([]const u32, @ptrCast(value.slice)));
return 1 + buf_len;
},
.flag_list => {
const len: u32 = @intCast(value.slice.len);
@memcpy(buffer[i..][0..len], @as([]const u32, @ptrCast(value.slice)));
return len;
},
.multi_list => {
const len: u32 = @intCast(value.mal.len);
buffer[i] = len;
const field_names = @typeInfo(Field.Elem).@"struct".field_names;
inline for (0..field_names.len) |field_i| @memcpy(
buffer[i + 1 + field_i * len ..][0..len],
@as([]const u32, @ptrCast(value.mal.items(@fromBackingInt(@intCast(field_i))))),
);
return 1 + field_names.len * len;
},
.union_list => {
if (value.len == 0) return 0;
const Tag = @typeInfo(Field.Union).@"union".tag_type.?;
const slice_ptr: [*]const Field.Union = @ptrCast(@alignCast(value.data));
const slice = slice_ptr[0..value.len];
const meta_buffer = buffer[i..][0 .. (slice.len * @bitSizeOf(Field.Meta) + 31) / 32];
for (slice[0 .. slice.len - 1], 0..) |elem, elem_index| {
const union_tag: Tag = elem;
storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{
.tag = union_tag,
.last = false,
}));
} else {
const elem_index = slice.len - 1;
const elem = slice[elem_index];
const union_tag: Tag = elem;
storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{
.tag = union_tag,
.last = true,
}));
}
var total: usize = meta_buffer.len;
for (i + meta_buffer.len.., slice) |elem_index, src| switch (src) {
inline else => |x| total += setExtraField(buffer, elem_index, @TypeOf(x), x),
};
return total;
},
},
},
.@"extern" => {
const n = @divExact(@sizeOf(Field), @sizeOf(u32));
const ptr: *align(@alignOf(Field)) const [n]u32 = @ptrCast(&value);
buffer[i..][0..n].* = ptr.*;
return n;
},
},
else => @compileError("bad field type: " ++ @typeName(Field)),
}
}
}