module: update to latest Zig + unify object ID validation/unpacking

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2024-11-17 16:34:07 -07:00 committed by Emi Gutekanst
parent 8a61c70409
commit 59bbea454a

View file

@ -138,7 +138,7 @@ pub fn Objects(comptime T: type) type {
objs.internal.mu.unlock(); objs.internal.mu.unlock();
} }
pub inline fn new(objs: *@This(), value: T) std.mem.Allocator.Error!ObjectID { pub fn new(objs: *@This(), value: T) std.mem.Allocator.Error!ObjectID {
const allocator = objs.internal.allocator; const allocator = objs.internal.allocator;
const data = &objs.internal.data; const data = &objs.internal.data;
const dead = &objs.internal.dead; const dead = &objs.internal.dead;
@ -184,50 +184,24 @@ pub fn Objects(comptime T: type) type {
pub fn set(objs: *@This(), id: ObjectID, value: T) void { pub fn set(objs: *@This(), id: ObjectID, value: T) void {
const data = &objs.internal.data; const data = &objs.internal.data;
const dead = &objs.internal.dead;
const generation = &objs.internal.generation;
const unpacked: PackedID = @bitCast(id); const unpacked = objs.validateAndUnpack(id, "set");
if (unpacked.generation != generation.items[unpacked.index]) {
@panic("mach: set() called with an object that is no longer valid");
}
if (dead.isSet(unpacked.index)) {
@panic("mach: set() called on a dead object");
}
data.set(unpacked.index, value); data.set(unpacked.index, value);
} }
pub fn get(objs: *@This(), id: ObjectID) ?T { pub fn get(objs: *@This(), id: ObjectID) ?T {
const data = &objs.internal.data; const data = &objs.internal.data;
const dead = &objs.internal.dead;
const generation = &objs.internal.generation;
const unpacked: PackedID = @bitCast(id); const unpacked = objs.validateAndUnpack(id, "get");
if (unpacked.generation != generation.items[unpacked.index]) {
@panic("mach: get() called with an object that is no longer valid");
}
if (dead.isSet(unpacked.index)) {
@panic("mach: get() called on a dead object");
}
return data.get(unpacked.index); return data.get(unpacked.index);
} }
pub fn delete(objs: *@This(), id: ObjectID) void { pub fn delete(objs: *@This(), id: ObjectID) void {
const data = &objs.internal.data; const data = &objs.internal.data;
const dead = &objs.internal.dead; const dead = &objs.internal.dead;
const generation = &objs.internal.generation;
const recycling_bin = &objs.internal.recycling_bin; const recycling_bin = &objs.internal.recycling_bin;
// TODO(object): decide whether to disable safety checks like this in some conditions, const unpacked = objs.validateAndUnpack(id, "delete");
// e.g. in release builds
const unpacked: PackedID = @bitCast(id);
if (unpacked.generation != generation.items[unpacked.index]) {
@panic("mach: delete() called with an object that is no longer valid");
}
if (dead.isSet(unpacked.index)) {
@panic("mach: delete() called on a dead object");
}
if (recycling_bin.items.len < recycling_bin.capacity) { if (recycling_bin.items.len < recycling_bin.capacity) {
recycling_bin.appendAssumeCapacity(unpacked.index); recycling_bin.appendAssumeCapacity(unpacked.index);
} else objs.internal.thrown_on_the_floor += 1; } else objs.internal.thrown_on_the_floor += 1;
@ -242,6 +216,22 @@ pub fn Objects(comptime T: type) type {
.objs = objs, .objs = objs,
}; };
} }
fn validateAndUnpack(objs: *@This(), id: ObjectID, comptime fn_name: []const u8) PackedID {
const dead = &objs.internal.dead;
const generation = &objs.internal.generation;
// TODO(object): decide whether to disable safety checks like this in some conditions,
// e.g. in release builds
const unpacked: PackedID = @bitCast(id);
if (unpacked.generation != generation.items[unpacked.index]) {
@panic("mach: " ++ fn_name ++ "() called with an object that is no longer valid");
}
if (dead.isSet(unpacked.index)) {
@panic("mach: " ++ fn_name ++ "() called on a dead object");
}
return unpacked;
}
}; };
} }
@ -288,7 +278,7 @@ pub fn ModFunctionIDs(comptime Module: type) type {
}}; }};
} }
return @Type(.{ return @Type(.{
.Struct = .{ .@"struct" = .{
.layout = .auto, .layout = .auto,
.is_tuple = false, .is_tuple = false,
.fields = fields, .fields = fields,
@ -309,7 +299,7 @@ fn ModuleFunctionName2(comptime M: type) type {
i += 1; i += 1;
} }
return @Type(.{ return @Type(.{
.Enum = .{ .@"enum" = .{
.tag_type = if (enum_fields.len > 0) std.math.IntFittingRange(0, enum_fields.len - 1) else u0, .tag_type = if (enum_fields.len > 0) std.math.IntFittingRange(0, enum_fields.len - 1) else u0,
.fields = enum_fields, .fields = enum_fields,
.decls = &[_]std.builtin.Type.Declaration{}, .decls = &[_]std.builtin.Type.Declaration{},
@ -347,7 +337,7 @@ pub fn Modules(module_lists: anytype) type {
i += 1; i += 1;
} }
return @Type(.{ return @Type(.{
.Enum = .{ .@"enum" = .{
.tag_type = if (enum_fields.len > 0) std.math.IntFittingRange(0, enum_fields.len - 1) else u0, .tag_type = if (enum_fields.len > 0) std.math.IntFittingRange(0, enum_fields.len - 1) else u0,
.fields = enum_fields, .fields = enum_fields,
.decls = &[_]std.builtin.Type.Declaration{}, .decls = &[_]std.builtin.Type.Declaration{},
@ -360,13 +350,14 @@ pub fn Modules(module_lists: anytype) type {
var m: @This() = .{ var m: @This() = .{
.mods = undefined, .mods = undefined,
}; };
inline for (@typeInfo(@TypeOf(m.mods)).Struct.fields) |field| { // TODO(object): errdefer release allocations made in this loop
inline for (@typeInfo(@TypeOf(m.mods)).@"struct".fields) |field| {
// TODO(objects): module-state-init // TODO(objects): module-state-init
const Mod2 = @TypeOf(@field(m.mods, field.name)); const Mod2 = @TypeOf(@field(m.mods, field.name));
var mod: Mod2 = undefined; var mod: Mod2 = undefined;
const module_name_id = try m.module_names.indexOrPut(allocator, @tagName(Mod2.mach_module)); const module_name_id = try m.module_names.indexOrPut(allocator, @tagName(Mod2.mach_module));
inline for (@typeInfo(@TypeOf(mod)).Struct.fields) |mod_field| { inline for (@typeInfo(@TypeOf(mod)).@"struct".fields) |mod_field| {
if (@typeInfo(mod_field.type) == .Struct and @hasDecl(mod_field.type, "IsMachObjects")) { if (@typeInfo(mod_field.type) == .@"struct" and @hasDecl(mod_field.type, "IsMachObjects")) {
const object_name_id = try m.module_names.indexOrPut(allocator, mod_field.name); const object_name_id = try m.module_names.indexOrPut(allocator, mod_field.name);
// TODO: use packed struct(TypeID) here. Same thing, just get the type from central location // TODO: use packed struct(TypeID) here. Same thing, just get the type from central location
@ -394,7 +385,7 @@ pub fn Modules(module_lists: anytype) type {
pub fn Module(module_tag_or_type: anytype) type { pub fn Module(module_tag_or_type: anytype) type {
const module_name: ModuleName = blk: { const module_name: ModuleName = blk: {
if (@typeInfo(@TypeOf(module_tag_or_type)) == .EnumLiteral or @typeInfo(@TypeOf(module_tag_or_type)) == .Enum) break :blk @as(ModuleName, module_tag_or_type); if (@typeInfo(@TypeOf(module_tag_or_type)) == .enum_literal or @typeInfo(@TypeOf(module_tag_or_type)) == .@"enum") break :blk @as(ModuleName, module_tag_or_type);
validate(module_tag_or_type); validate(module_tag_or_type);
break :blk module_tag_or_type.mach_module; break :blk module_tag_or_type.mach_module;
}; };
@ -423,7 +414,7 @@ pub fn Modules(module_lists: anytype) type {
const f = @field(module, @tagName(fn_name)); const f = @field(module, @tagName(fn_name));
const F = @TypeOf(f); const F = @TypeOf(f);
if (@typeInfo(F) == .Struct and @typeInfo(F).Struct.is_tuple) { if (@typeInfo(F) == .@"struct" and @typeInfo(F).@"struct".is_tuple) {
// Run a list of functions instead of a single function // Run a list of functions instead of a single function
// TODO: verify this is a mach.schedule() decl // TODO: verify this is a mach.schedule() decl
if (module_name != .app) @compileLog(module_name); if (module_name != .app) @compileLog(module_name);
@ -438,9 +429,9 @@ pub fn Modules(module_lists: anytype) type {
// Inject arguments // Inject arguments
var args: std.meta.ArgsTuple(F) = undefined; var args: std.meta.ArgsTuple(F) = undefined;
outer: inline for (@typeInfo(std.meta.ArgsTuple(F)).Struct.fields) |arg| { outer: inline for (@typeInfo(std.meta.ArgsTuple(F)).@"struct".fields) |arg| {
if (@typeInfo(arg.type) == .Pointer and if (@typeInfo(arg.type) == .pointer and
@typeInfo(std.meta.Child(arg.type)) == .Struct and @typeInfo(std.meta.Child(arg.type)) == .@"struct" and
comptime isValid(std.meta.Child(arg.type))) comptime isValid(std.meta.Child(arg.type)))
{ {
// *Module argument // *Module argument
@ -448,7 +439,7 @@ pub fn Modules(module_lists: anytype) type {
@field(args, arg.name) = &@field(m.mods, @tagName(std.meta.Child(arg.type).mach_module)); @field(args, arg.name) = &@field(m.mods, @tagName(std.meta.Child(arg.type).mach_module));
continue :outer; continue :outer;
} }
if (@typeInfo(arg.type) == .Struct and @hasDecl(arg.type, "IsMachMod")) { if (@typeInfo(arg.type) == .@"struct" and @hasDecl(arg.type, "IsMachMod")) {
const M = arg.type.Module; const M = arg.type.Module;
var mv: Mod(M) = .{ var mv: Mod(M) = .{
.id = undefined, .id = undefined,
@ -469,10 +460,10 @@ pub fn Modules(module_lists: anytype) type {
@compileError("mach: function " ++ debug_name ++ " has an invalid argument(" ++ arg.name ++ ") type: " ++ @typeName(arg.type)); @compileError("mach: function " ++ debug_name ++ " has an invalid argument(" ++ arg.name ++ ") type: " ++ @typeName(arg.type));
} }
const Ret = @typeInfo(F).Fn.return_type orelse void; const Ret = @typeInfo(F).@"fn".return_type orelse void;
switch (@typeInfo(Ret)) { switch (@typeInfo(Ret)) {
// TODO: define error handling of runnable functions // TODO: define error handling of runnable functions
.ErrorUnion => @call(.auto, f, args) catch |err| std.debug.panic("error: {s}", .{@errorName(err)}), .error_union => @call(.auto, f, args) catch |err| std.debug.panic("error: {s}", .{@errorName(err)}),
else => @call(.auto, f, args), else => @call(.auto, f, args),
} }
} }
@ -504,12 +495,12 @@ pub fn Modules(module_lists: anytype) type {
/// Validates that the given struct is a Mach module. /// Validates that the given struct is a Mach module.
fn validate(comptime module: anytype) void { fn validate(comptime module: anytype) void {
if (!@hasDecl(module, "mach_module")) @compileError("mach: invalid module, missing `pub const mach_module = .foo_name;` declaration: " ++ @typeName(@TypeOf(module))); if (!@hasDecl(module, "mach_module")) @compileError("mach: invalid module, missing `pub const mach_module = .foo_name;` declaration: " ++ @typeName(@TypeOf(module)));
if (@typeInfo(@TypeOf(module.mach_module)) != .EnumLiteral) @compileError("mach: invalid module, expected `pub const mach_module = .foo_name;` declaration, found: " ++ @typeName(@TypeOf(module.mach_module))); if (@typeInfo(@TypeOf(module.mach_module)) != .enum_literal) @compileError("mach: invalid module, expected `pub const mach_module = .foo_name;` declaration, found: " ++ @typeName(@TypeOf(module.mach_module)));
} }
fn isValid(comptime module: anytype) bool { fn isValid(comptime module: anytype) bool {
if (!@hasDecl(module, "mach_module")) return false; if (!@hasDecl(module, "mach_module")) return false;
if (@typeInfo(@TypeOf(module.mach_module)) != .EnumLiteral) return false; if (@typeInfo(@TypeOf(module.mach_module)) != .enum_literal) return false;
return true; return true;
} }
@ -522,7 +513,7 @@ fn NameEnum(comptime mods: anytype) type {
enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = @tagName(module.mach_module), .value = i }}; enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = @tagName(module.mach_module), .value = i }};
} }
return @Type(.{ return @Type(.{
.Enum = .{ .@"enum" = .{
.tag_type = std.math.IntFittingRange(0, enum_fields.len - 1), .tag_type = std.math.IntFittingRange(0, enum_fields.len - 1),
.fields = enum_fields, .fields = enum_fields,
.decls = &[_]std.builtin.Type.Declaration{}, .decls = &[_]std.builtin.Type.Declaration{},
@ -552,13 +543,13 @@ fn moduleTuple(comptime tuple: anytype) ModuleTuple(tuple) {
/// Type-returning variant of merge() /// Type-returning variant of merge()
fn ModuleTuple(comptime tuple: anytype) type { fn ModuleTuple(comptime tuple: anytype) type {
if (@typeInfo(@TypeOf(tuple)) != .Struct or !@typeInfo(@TypeOf(tuple)).Struct.is_tuple) { if (@typeInfo(@TypeOf(tuple)) != .@"struct" or !@typeInfo(@TypeOf(tuple)).@"struct".is_tuple) {
@compileError("Expected to find a tuple, found: " ++ @typeName(@TypeOf(tuple))); @compileError("Expected to find a tuple, found: " ++ @typeName(@TypeOf(tuple)));
} }
var tuple_fields: []const std.builtin.Type.StructField = &[0]std.builtin.Type.StructField{}; var tuple_fields: []const std.builtin.Type.StructField = &[0]std.builtin.Type.StructField{};
loop: inline for (tuple) |elem| { loop: inline for (tuple) |elem| {
if (@typeInfo(@TypeOf(elem)) == .Type and @typeInfo(elem) == .Struct) { if (@typeInfo(@TypeOf(elem)) == .type and @typeInfo(elem) == .@"struct") {
// Struct type // Struct type
validate(elem); validate(elem);
for (tuple_fields) |field| if (@as(*const type, @ptrCast(field.default_value.?)).* == elem) for (tuple_fields) |field| if (@as(*const type, @ptrCast(field.default_value.?)).* == elem)
@ -572,7 +563,7 @@ fn ModuleTuple(comptime tuple: anytype) type {
.is_comptime = false, .is_comptime = false,
.alignment = if (@sizeOf(elem) > 0) @alignOf(elem) else 0, .alignment = if (@sizeOf(elem) > 0) @alignOf(elem) else 0,
}}; }};
} else if (@typeInfo(@TypeOf(elem)) == .Struct and @typeInfo(@TypeOf(elem)).Struct.is_tuple) { } else if (@typeInfo(@TypeOf(elem)) == .@"struct" and @typeInfo(@TypeOf(elem)).@"struct".is_tuple) {
// Nested tuple // Nested tuple
inline for (moduleTuple(elem)) |nested| { inline for (moduleTuple(elem)) |nested| {
validate(nested); validate(nested);
@ -593,7 +584,7 @@ fn ModuleTuple(comptime tuple: anytype) type {
} }
} }
return @Type(.{ return @Type(.{
.Struct = .{ .@"struct" = .{
.is_tuple = true, .is_tuple = true,
.layout = .auto, .layout = .auto,
.decls = &.{}, .decls = &.{},
@ -616,7 +607,7 @@ fn ModuleTypesByName(comptime modules: anytype) type {
}}; }};
} }
return @Type(.{ return @Type(.{
.Struct = .{ .@"struct" = .{
.layout = .auto, .layout = .auto,
.is_tuple = false, .is_tuple = false,
.fields = fields, .fields = fields,
@ -639,7 +630,7 @@ fn ModulesByName(comptime modules: anytype) type {
}}; }};
} }
return @Type(.{ return @Type(.{
.Struct = .{ .@"struct" = .{
.layout = .auto, .layout = .auto,
.is_tuple = false, .is_tuple = false,
.fields = fields, .fields = fields,