From 714f200bc1445f17d0b5be28e3519de206a19583 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Sun, 17 Nov 2024 20:55:22 -0700 Subject: [PATCH] module: implement cross-Object-pool graph relations Signed-off-by: Stephen Gutekanst --- examples/core-custom-entrypoint/main.zig | 3 +- examples/core-triangle/main.zig | 3 +- examples/custom-renderer/main.zig | 3 +- examples/glyphs/main.zig | 3 +- examples/hardware-check/main.zig | 3 +- examples/piano/main.zig | 3 +- examples/play-opus/main.zig | 3 +- examples/sprite/main.zig | 3 +- examples/text/main.zig | 3 +- src/module.zig | 98 ++++++++++++++++++------ 10 files changed, 91 insertions(+), 34 deletions(-) diff --git a/examples/core-custom-entrypoint/main.zig b/examples/core-custom-entrypoint/main.zig index 46495be7..9b069ab9 100644 --- a/examples/core-custom-entrypoint/main.zig +++ b/examples/core-custom-entrypoint/main.zig @@ -11,7 +11,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = try Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // On some platforms, you can drive the mach.Core main loop yourself - but this isn't possible // on all platforms. If mach.Core.non_blocking is set to true, and the platform supports diff --git a/examples/core-triangle/main.zig b/examples/core-triangle/main.zig index 32e48ee4..cadfe0ce 100644 --- a/examples/core-triangle/main.zig +++ b/examples/core-triangle/main.zig @@ -12,7 +12,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = try Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // TODO: enable mods.deinit(allocator); for allocator leak detection // defer mods.deinit(allocator); diff --git a/examples/custom-renderer/main.zig b/examples/custom-renderer/main.zig index 9f92f526..7e971762 100644 --- a/examples/custom-renderer/main.zig +++ b/examples/custom-renderer/main.zig @@ -13,7 +13,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // TODO: enable mods.deinit(allocator); for allocator leak detection // defer mods.deinit(allocator); diff --git a/examples/glyphs/main.zig b/examples/glyphs/main.zig index d355e4f4..1c0afef7 100644 --- a/examples/glyphs/main.zig +++ b/examples/glyphs/main.zig @@ -14,7 +14,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // TODO: enable mods.deinit(allocator); for allocator leak detection // defer mods.deinit(allocator); diff --git a/examples/hardware-check/main.zig b/examples/hardware-check/main.zig index 47c22182..ad482968 100644 --- a/examples/hardware-check/main.zig +++ b/examples/hardware-check/main.zig @@ -15,7 +15,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // TODO: enable mods.deinit(allocator); for allocator leak detection // defer mods.deinit(allocator); diff --git a/examples/piano/main.zig b/examples/piano/main.zig index 7f6299bf..e489a200 100644 --- a/examples/piano/main.zig +++ b/examples/piano/main.zig @@ -13,7 +13,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // TODO: enable mods.deinit(allocator); for allocator leak detection // defer mods.deinit(allocator); diff --git a/examples/play-opus/main.zig b/examples/play-opus/main.zig index 7f6299bf..e489a200 100644 --- a/examples/play-opus/main.zig +++ b/examples/play-opus/main.zig @@ -13,7 +13,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // TODO: enable mods.deinit(allocator); for allocator leak detection // defer mods.deinit(allocator); diff --git a/examples/sprite/main.zig b/examples/sprite/main.zig index 002b21e9..a91affae 100644 --- a/examples/sprite/main.zig +++ b/examples/sprite/main.zig @@ -13,7 +13,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // TODO: enable mods.deinit(allocator); for allocator leak detection // defer mods.deinit(allocator); diff --git a/examples/text/main.zig b/examples/text/main.zig index 1462a313..3040c025 100644 --- a/examples/text/main.zig +++ b/examples/text/main.zig @@ -13,7 +13,8 @@ pub fn main() !void { const allocator = std.heap.c_allocator; // The set of Mach modules our application may use. - var mods = Modules.init(allocator); + var mods: Modules = undefined; + try mods.init(allocator); // TODO: enable mods.deinit(allocator); for allocator leak detection // defer mods.deinit(allocator); diff --git a/src/module.zig b/src/module.zig index da1d8b90..0da8b953 100644 --- a/src/module.zig +++ b/src/module.zig @@ -1,6 +1,7 @@ const std = @import("std"); const mach = @import("../main.zig"); const StringTable = @import("StringTable.zig"); +const Graph = @import("graph.zig").Graph; /// An ID representing a mach object. This is an opaque identifier which effectively encodes: /// @@ -54,6 +55,9 @@ pub fn Objects(comptime T: type) type { /// on the floor and forgotten about. This means there are dead items recorded by dead.set(index) /// which aren't in the recycling_bin, and the next call to new() may consider cleaning up. thrown_on_the_floor: u32 = 0, + + /// Global pointer to object relations graph + graph: *Graph, }, pub const IsMachObjects = void; @@ -217,6 +221,7 @@ pub fn Objects(comptime T: type) type { }; } + // TODO: this doesn't type check currently, but it should (verify id is from this pool of objects.) fn validateAndUnpack(objs: *@This(), id: ObjectID, comptime fn_name: []const u8) PackedID { const dead = &objs.internal.dead; const generation = &objs.internal.generation; @@ -232,6 +237,59 @@ pub fn Objects(comptime T: type) type { } return unpacked; } + + /// Tells if the given object (which must be alive and valid) is from this pool of objects. + pub fn is(objs: *const @This(), id: ObjectID) bool { + const unpacked = objs.validateAndUnpack(id, "is"); + return unpacked.type_id == objs.internal.type_id; + } + + /// Get the parent of the child, or null. + /// + /// Object relations may cross the object-pool boundary; for example the parent or child of + /// an object in this pool may not itself be in this pool. It might be from a different + /// pool and a different type of object. + pub fn getParent(objs: *@This(), id: ObjectID) !?ObjectID { + return objs.internal.graph.getParent(objs.internal.allocator, id); + } + + /// Set the parent of the child, or no-op if already the case. + /// + /// Object relations may cross the object-pool boundary; for example the parent or child of + /// an object in this pool may not itself be in this pool. It might be from a different + /// pool and a different type of object. + pub fn setParent(objs: *@This(), id: ObjectID, parent: ?ObjectID) !void { + try objs.internal.graph.setParent(objs.internal.allocator, id, parent orelse return objs.internal.graph.removeParent(objs.internal.allocator, id)); + } + + /// Get the children of the parent; returning a results.items slice which is read-only. + /// Call results.deinit() when you are done to return memory to the graph's memory pool for + /// reuse later. + /// + /// Object relations may cross the object-pool boundary; for example the parent or child of + /// an object in this pool may not itself be in this pool. It might be from a different + /// pool and a different type of object. + pub fn getChildren(objs: *@This(), id: ObjectID) !Graph.Results { + return objs.internal.graph.getChildren(objs.internal.allocator, id); + } + + /// Add the given child to the parent, or no-op if already the case. + /// + /// Object relations may cross the object-pool boundary; for example the parent or child of + /// an object in this pool may not itself be in this pool. It might be from a different + /// pool and a different type of object. + pub fn addChild(objs: *@This(), id: ObjectID, child: ObjectID) !void { + return objs.internal.graph.addChild(objs.internal.allocator, id, child); + } + + /// Remove the given child from the parent, or no-op if not the case. + /// + /// Object relations may cross the object-pool boundary; for example the parent or child of + /// an object in this pool may not itself be in this pool. It might be from a different + /// pool and a different type of object. + pub fn removeChild(objs: *@This(), id: ObjectID, child: ObjectID) !void { + return objs.internal.graph.removeChild(objs.internal.allocator, id, child); + } }; } @@ -323,6 +381,7 @@ pub fn Modules(module_lists: anytype) type { module_names: StringTable = .{}, object_names: StringTable = .{}, + graph: Graph, /// Enum describing all declarations for a given comptime-known module. fn ModuleFunctionName(comptime module_name: ModuleName) type { @@ -346,10 +405,19 @@ pub fn Modules(module_lists: anytype) type { }); } - pub fn init(allocator: std.mem.Allocator) std.mem.Allocator.Error!@This() { - var m: @This() = .{ + pub fn init(m: *@This(), allocator: std.mem.Allocator) std.mem.Allocator.Error!void { + m.* = .{ .mods = undefined, + .graph = undefined, }; + try m.graph.init(allocator, .{ + // TODO(object): measured preallocations + .queue_size = 32, + .nodes_size = 32, + .num_result_lists = 8, + .result_list_size = 8, + }); + // TODO(object): errdefer release allocations made in this loop inline for (@typeInfo(@TypeOf(m.mods)).@"struct".fields) |field| { // TODO(objects): module-state-init @@ -369,18 +437,17 @@ pub fn Modules(module_lists: anytype) type { @field(mod, mod_field.name).internal = .{ .allocator = allocator, .type_id = object_type_id, + .graph = &m.graph, }; } } @field(m.mods, field.name) = mod; } - return m; } pub fn deinit(m: *@This(), allocator: std.mem.Allocator) void { - // TODO - _ = m; - _ = allocator; + m.graph.deinit(allocator); + // TODO: remainder of deinit } pub fn Module(module_tag_or_type: anytype) type { @@ -638,22 +705,3 @@ fn ModulesByName(comptime modules: anytype) type { }, }); } - -// // Returns true if a and b are both functions and are equal -// fn isFnAndEqual(comptime a: anytype, comptime b: anytype) bool { -// const A = @TypeOf(a); -// const B = @TypeOf(b); -// if (@typeInfo(A) != .Fn or @typeInfo(B) != .Fn) return false; -// const x = @typeInfo(A).Fn; -// const y = @typeInfo(B).Fn; -// if (x.calling_convention != y.calling_convention) return false; -// if (x.is_generic != y.is_generic) return false; -// if (x.is_var_args != y.is_var_args) return false; -// if ((x.return_type != null) != (y.return_type != null)) return false; -// if (x.return_type != null) if (x.return_type.? != y.return_type.?) return false; -// if (x.params.len != y.params.len) return false; -// if (x.params.ptr != y.params.ptr) return false; -// if (A != B) return false; -// if (a != b) return false; -// return true; -// }