Add deferred system operations
Some checks reported errors
continuous-integration/drone/push Build was killed
Some checks reported errors
continuous-integration/drone/push Build was killed
This commit is contained in:
parent
340f843740
commit
89d975f668
@ -16,19 +16,19 @@ pub fn CallTask(comptime function: anytype) type {
|
|||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
args: Args,
|
args: Args,
|
||||||
@"return": Future(function_fn.return_type.?),
|
returned: Future(function_fn.return_type.?),
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(args: Args) Self {
|
pub fn init(args: Args) Self {
|
||||||
return .{
|
return .{
|
||||||
.args = args,
|
.args = args,
|
||||||
.@"return" = .empty,
|
.returned = .empty,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self: *Self) void {
|
pub fn run(self: *Self) void {
|
||||||
std.debug.assert(self.@"return".resolve(@call(.auto, function, self.args)));
|
std.debug.assert(self.returned.resolve(@call(.auto, function, self.args)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ pub const TaskQueue = struct {
|
|||||||
while (true) {
|
while (true) {
|
||||||
const runner = self.pending.dequeue();
|
const runner = self.pending.dequeue();
|
||||||
|
|
||||||
if (runner.is_fn(poison_pill)) {
|
if (runner.isFn(poison_pill)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,16 +26,15 @@ pub const utf8 = @import("./utf8.zig");
|
|||||||
|
|
||||||
pub fn Callable(comptime Output: type, comptime input_types: []const type) type {
|
pub fn Callable(comptime Output: type, comptime input_types: []const type) type {
|
||||||
const InputTuple = std.meta.Tuple(input_types);
|
const InputTuple = std.meta.Tuple(input_types);
|
||||||
const fn_context: *allowzero anyopaque = @ptrFromInt(0);
|
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
context: *allowzero anyopaque,
|
has_context: ?*anyopaque,
|
||||||
call_with_context: *const fn (*anyopaque, InputTuple) Output,
|
call_with_context: *const fn (?*anyopaque, InputTuple) Output,
|
||||||
|
|
||||||
fn FnCall(comptime invoke: anytype) type {
|
fn FnCall(comptime invoke: anytype) type {
|
||||||
return struct {
|
return struct {
|
||||||
fn call(context: *anyopaque, inputs: InputTuple) Output {
|
fn call(context: ?*anyopaque, inputs: InputTuple) Output {
|
||||||
std.debug.assert(context == fn_context);
|
std.debug.assert(context == null);
|
||||||
|
|
||||||
return @call(.auto, invoke, inputs);
|
return @call(.auto, invoke, inputs);
|
||||||
}
|
}
|
||||||
@ -47,16 +46,14 @@ pub fn Callable(comptime Output: type, comptime input_types: []const type) type
|
|||||||
const is_zero_aligned = @typeInfo(ContextPtr).pointer.alignment == 0;
|
const is_zero_aligned = @typeInfo(ContextPtr).pointer.alignment == 0;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn invoke_concrete(context: *anyopaque, inputs: InputTuple) Output {
|
fn invoke_concrete(context: ?*anyopaque, inputs: InputTuple) Output {
|
||||||
std.debug.assert(context != fn_context);
|
|
||||||
|
|
||||||
if (is_zero_aligned) {
|
if (is_zero_aligned) {
|
||||||
return @call(.auto, invoke, .{@as(ContextPtr, @ptrCast(context))} ++ inputs);
|
return @call(.auto, invoke, .{@as(ContextPtr, @ptrCast(context.?))} ++ inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
var args: std.meta.ArgsTuple(Invoke) = undefined;
|
var args: std.meta.ArgsTuple(Invoke) = undefined;
|
||||||
|
|
||||||
args[0] = @as(ContextPtr, @ptrCast(@alignCast(context)));
|
args[0] = @as(ContextPtr, @ptrCast(@alignCast(context.?)));
|
||||||
|
|
||||||
inline for (1..args.len, &inputs) |i, input| {
|
inline for (1..args.len, &inputs) |i, input| {
|
||||||
args[i] = input;
|
args[i] = input;
|
||||||
@ -73,29 +70,29 @@ pub fn Callable(comptime Output: type, comptime input_types: []const type) type
|
|||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn call(self: Self, inputs: InputTuple) Output {
|
pub fn call(self: Self, inputs: InputTuple) Output {
|
||||||
return self.call_with_context(self.context, inputs);
|
return self.call_with_context(self.has_context, inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initFn(comptime invoke: anytype) Self {
|
pub fn initFn(comptime invoke: anytype) Self {
|
||||||
return .{
|
return .{
|
||||||
.context = fn_context,
|
.has_context = null,
|
||||||
.call_with_context = FnCall(invoke).call,
|
.call_with_context = FnCall(invoke).call,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initRef(context_ptr: anytype, comptime invoke: anytype) Self {
|
pub fn initRef(context_ptr: anytype, comptime invoke: anytype) Self {
|
||||||
return .{
|
return .{
|
||||||
.context = @ptrCast(context_ptr),
|
.has_context = @ptrCast(context_ptr),
|
||||||
.call_with_context = RefCall(@TypeOf(context_ptr), invoke).invoke_concrete,
|
.call_with_context = RefCall(@TypeOf(context_ptr), invoke).invoke_concrete,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fn(self: Self, invoke: anytype) bool {
|
pub fn isFn(self: Self, invoke: anytype) bool {
|
||||||
const Invoke = @TypeOf(invoke);
|
const Invoke = @TypeOf(invoke);
|
||||||
|
|
||||||
return switch (@typeInfo(Invoke)) {
|
return switch (@typeInfo(Invoke)) {
|
||||||
.pointer => self.is_fn(invoke.*),
|
.pointer => self.isFn(invoke.*),
|
||||||
.@"fn" => (self.context == fn_context) and (self.call_with_context == FnCall(invoke).call),
|
.@"fn" => (self.has_context == null) and (self.call_with_context == FnCall(invoke).call),
|
||||||
else => @compileError(std.fmt.comptimePrint("parameter `fn` must be a function type or pointer to one, not {s}", .{@typeName(Invoke)})),
|
else => @compileError(std.fmt.comptimePrint("parameter `fn` must be a function type or pointer to one, not {s}", .{@typeName(Invoke)})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,20 @@ pub fn Unwrapped(comptime Value: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn UnwrappedError(comptime Value: type) type {
|
||||||
|
return switch (@typeInfo(Value)) {
|
||||||
|
.error_union => |error_union| error_union.payload,
|
||||||
|
else => Value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn UnwrappedOptional(comptime Value: type) type {
|
||||||
|
return switch (@typeInfo(Value)) {
|
||||||
|
.optional => |optional| optional.child,
|
||||||
|
else => Value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn isContainer(@"type": std.builtin.Type) bool {
|
pub fn isContainer(@"type": std.builtin.Type) bool {
|
||||||
return switch (@"type") {
|
return switch (@"type") {
|
||||||
.@"struct", .@"union", .@"enum", .@"opaque" => true,
|
.@"struct", .@"union", .@"enum", .@"opaque" => true,
|
||||||
|
@ -4,32 +4,128 @@ const std = @import("std");
|
|||||||
|
|
||||||
pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(Key)) type {
|
pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(Key)) type {
|
||||||
return struct {
|
return struct {
|
||||||
free_nodes: ?*Node = null,
|
free_nodes: Pool = .{},
|
||||||
active_nodes: ?*Node = null,
|
has_root: ?*Node = null,
|
||||||
|
|
||||||
const Node = struct {
|
const Node = struct {
|
||||||
key: Key,
|
key: Key,
|
||||||
value: Value,
|
value: Value,
|
||||||
has_left: ?*Node = null,
|
has_lesser: ?*Node = null,
|
||||||
has_right: ?*Node = null,
|
has_greater: ?*Node = null,
|
||||||
has_parent: ?*Node = null,
|
has_parent: ?*Node = null,
|
||||||
|
|
||||||
fn deinit(self: *Node, allocator: std.mem.Allocator) void {
|
fn deinit(self: *Node, allocator: std.mem.Allocator) void {
|
||||||
self.has_parent = undefined;
|
if (self.has_lesser) |left| {
|
||||||
|
|
||||||
if (self.has_left) |left| {
|
|
||||||
left.deinit(allocator);
|
left.deinit(allocator);
|
||||||
allocator.destroy(left);
|
allocator.destroy(left);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.has_left = undefined;
|
if (self.has_greater) |right| {
|
||||||
|
|
||||||
if (self.has_right) |right| {
|
|
||||||
right.deinit(allocator);
|
right.deinit(allocator);
|
||||||
allocator.destroy(right);
|
allocator.destroy(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.has_right = undefined;
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find(self: *Node, key: Key) ?*Node {
|
||||||
|
var node = self;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const next_node = switch (traits.compare(key, node.key)) {
|
||||||
|
.lesser => node.has_lesser,
|
||||||
|
.greater => node.has_greater,
|
||||||
|
|
||||||
|
.equal => {
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
node = next_node orelse {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getMax(self: *Node) *Node {
|
||||||
|
var node = self;
|
||||||
|
|
||||||
|
while (self.has_lesser) |lesser| {
|
||||||
|
node = lesser;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getMin(self: *Node) *Node {
|
||||||
|
var node = self;
|
||||||
|
|
||||||
|
while (self.has_lesser) |lesser| {
|
||||||
|
node = lesser;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(self: *Node, key: Key) ?*Node {
|
||||||
|
const node = self.find(key) orelse {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (node.has_lesser == null) {
|
||||||
|
std.debug.assert(self.transplant(node.has_greater));
|
||||||
|
} else if (node.has_greater == null) {
|
||||||
|
std.debug.assert(self.transplant(node.has_lesser));
|
||||||
|
} else {
|
||||||
|
var successor = node.has_greater.?.getMin();
|
||||||
|
|
||||||
|
if (successor.has_parent != node) {
|
||||||
|
// Move successor up: replace successor with its right child first
|
||||||
|
std.debug.assert(successor.transplant(successor.has_greater));
|
||||||
|
|
||||||
|
// Attach node.right to successor
|
||||||
|
successor.has_greater = node.has_greater;
|
||||||
|
|
||||||
|
if (successor.has_greater) |g| {
|
||||||
|
g.has_parent = successor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace node with successor
|
||||||
|
std.debug.assert(node.transplant(successor));
|
||||||
|
|
||||||
|
// Attach node.left to successor
|
||||||
|
successor.has_lesser = node.has_lesser;
|
||||||
|
|
||||||
|
if (successor.has_lesser) |l| {
|
||||||
|
l.has_parent = successor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach the removed node completely and return it
|
||||||
|
node.has_parent = null;
|
||||||
|
node.has_lesser = null;
|
||||||
|
node.has_greater = null;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transplant(self: *Node, has_node: ?*Node) bool {
|
||||||
|
const parent = self.has_parent orelse {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parent.has_lesser == self) {
|
||||||
|
parent.has_lesser = has_node;
|
||||||
|
} else {
|
||||||
|
parent.has_greater = has_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_node) |node| {
|
||||||
|
node.has_parent = self.has_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,8 +136,8 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
|||||||
var nodes = self.nodes;
|
var nodes = self.nodes;
|
||||||
|
|
||||||
while (nodes) |node| {
|
while (nodes) |node| {
|
||||||
const left = node.has_left orelse {
|
const left = node.has_lesser orelse {
|
||||||
self.nodes = node.has_right;
|
self.nodes = node.has_greater;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.key = node.key,
|
.key = node.key,
|
||||||
@ -51,13 +147,13 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
|||||||
|
|
||||||
// Find the rightmost node in left subtree or link back to current
|
// Find the rightmost node in left subtree or link back to current
|
||||||
var pred = left;
|
var pred = left;
|
||||||
while (pred.has_right != null and pred.has_right != node) {
|
while (pred.has_greater != null and pred.has_greater != node) {
|
||||||
pred = pred.has_right.?;
|
pred = pred.has_greater.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pred.has_right != null) {
|
if (pred.has_greater != null) {
|
||||||
pred.has_right = null;
|
pred.has_greater = null;
|
||||||
self.nodes = node.has_right;
|
self.nodes = node.has_greater;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.key = node.key,
|
.key = node.key,
|
||||||
@ -65,8 +161,8 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pred.has_right = node;
|
pred.has_greater = node;
|
||||||
self.nodes = node.has_left;
|
self.nodes = node.has_lesser;
|
||||||
nodes = self.nodes;
|
nodes = self.nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,16 +178,48 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Pool = struct {
|
||||||
|
has_node: ?*Node = null,
|
||||||
|
|
||||||
|
fn create(self: *Pool, allocator: std.mem.Allocator, node: Node) std.mem.Allocator.Error!*Node {
|
||||||
|
if (self.has_node) |free_node| {
|
||||||
|
self.has_node = free_node.has_parent;
|
||||||
|
|
||||||
|
free_node.* = node;
|
||||||
|
|
||||||
|
return free_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const created_node = try allocator.create(Node);
|
||||||
|
|
||||||
|
created_node.* = node;
|
||||||
|
|
||||||
|
return created_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(self: *Pool, node: *Node) void {
|
||||||
|
node.* = .{
|
||||||
|
.key = undefined,
|
||||||
|
.value = undefined,
|
||||||
|
.has_parent = self.has_node,
|
||||||
|
.has_lesser = null,
|
||||||
|
.has_greater = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.has_node = node;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn clear(self: *Self) void {
|
pub fn clear(self: *Self) void {
|
||||||
var free_nodes: ?*Node = null;
|
var free_nodes: ?*Node = null;
|
||||||
|
|
||||||
if (self.active_nodes) |root| {
|
if (self.has_root) |root| {
|
||||||
// Push root onto stack
|
// Push root onto stack
|
||||||
root.has_parent = free_nodes;
|
root.has_parent = free_nodes;
|
||||||
free_nodes = root;
|
free_nodes = root;
|
||||||
self.active_nodes = null;
|
self.has_root = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (free_nodes) |node| {
|
while (free_nodes) |node| {
|
||||||
@ -99,60 +227,37 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
|||||||
free_nodes = node.has_parent;
|
free_nodes = node.has_parent;
|
||||||
|
|
||||||
// Push children onto stack
|
// Push children onto stack
|
||||||
if (node.has_left) |left| {
|
if (node.has_lesser) |left| {
|
||||||
left.has_parent = free_nodes;
|
left.has_parent = free_nodes;
|
||||||
free_nodes = left;
|
free_nodes = left;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has_right) |right| {
|
if (node.has_greater) |right| {
|
||||||
right.has_parent = free_nodes;
|
right.has_parent = free_nodes;
|
||||||
free_nodes = right;
|
free_nodes = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add node to free list
|
self.free_nodes.destroy(node);
|
||||||
node.has_left = null;
|
|
||||||
node.has_right = null;
|
|
||||||
node.has_parent = null;
|
|
||||||
node.key = undefined;
|
|
||||||
node.value = undefined;
|
|
||||||
node.has_right = self.free_nodes;
|
|
||||||
self.free_nodes = node;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createNode(self: *Self, allocator: std.mem.Allocator, node: Node) std.mem.Allocator.Error!*Node {
|
|
||||||
if (self.free_nodes) |free_node| {
|
|
||||||
self.free_nodes = free_node.has_parent;
|
|
||||||
|
|
||||||
free_node.* = node;
|
|
||||||
|
|
||||||
return free_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
const created_node = try allocator.create(Node);
|
|
||||||
|
|
||||||
created_node.* = node;
|
|
||||||
|
|
||||||
return created_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||||
if (self.active_nodes) |node| {
|
if (self.has_root) |root| {
|
||||||
node.deinit(allocator);
|
root.deinit(allocator);
|
||||||
allocator.destroy(node);
|
allocator.destroy(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.active_nodes = undefined;
|
self.has_root = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(self: *Self, allocator: std.mem.Allocator, key: Key, value: Value) std.mem.Allocator.Error!?*Value {
|
pub fn insert(self: *Self, allocator: std.mem.Allocator, key: Key, value: Value) std.mem.Allocator.Error!?*Value {
|
||||||
var node = self.active_nodes orelse {
|
var node = self.has_root orelse {
|
||||||
self.active_nodes = try self.createNode(allocator, .{
|
self.has_root = try self.free_nodes.create(allocator, .{
|
||||||
.key = key,
|
.key = key,
|
||||||
.value = value,
|
.value = value,
|
||||||
});
|
});
|
||||||
|
|
||||||
return &self.active_nodes.?.value;
|
return &self.has_root.?.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -162,26 +267,26 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
|||||||
},
|
},
|
||||||
|
|
||||||
.lesser => {
|
.lesser => {
|
||||||
node = node.has_left orelse {
|
node = node.has_lesser orelse {
|
||||||
node.has_left = try self.createNode(allocator, .{
|
node.has_lesser = try self.free_nodes.create(allocator, .{
|
||||||
.key = key,
|
.key = key,
|
||||||
.value = value,
|
.value = value,
|
||||||
.has_parent = node,
|
.has_parent = node,
|
||||||
});
|
});
|
||||||
|
|
||||||
return &node.has_left.?.value;
|
return &node.has_lesser.?.value;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
.greater => {
|
.greater => {
|
||||||
node = node.has_right orelse {
|
node = node.has_greater orelse {
|
||||||
node.has_right = try self.createNode(allocator, .{
|
node.has_greater = try self.free_nodes.create(allocator, .{
|
||||||
.key = key,
|
.key = key,
|
||||||
.value = value,
|
.value = value,
|
||||||
.has_parent = node,
|
.has_parent = node,
|
||||||
});
|
});
|
||||||
|
|
||||||
return &node.has_right.?.value;
|
return &node.has_greater.?.value;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -189,49 +294,54 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const empty = Self{
|
pub const empty = Self{
|
||||||
.active_nodes = null,
|
.has_root = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn get(self: Self, key: Key) ?*Value {
|
pub fn get(self: Self, key: Key) ?*Value {
|
||||||
var nodes = self.active_nodes;
|
if (self.has_root) |root| {
|
||||||
|
if (root.find(key)) |node| {
|
||||||
while (nodes) |node| {
|
return &node.value;
|
||||||
nodes = switch (traits.compare(key, node.key)) {
|
}
|
||||||
.lesser => node.has_left,
|
|
||||||
.greater => node.has_right,
|
|
||||||
|
|
||||||
.equal => {
|
|
||||||
return &node.value;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getKey(self: Self, key: Key) ?Key {
|
pub fn getKey(self: Self, key: Key) ?Key {
|
||||||
var nodes = self.active_nodes;
|
if (self.has_root) |root| {
|
||||||
|
if (root.find(key)) |node| {
|
||||||
while (nodes) |node| {
|
return &node.key;
|
||||||
nodes = switch (traits.compare(key, node.key)) {
|
}
|
||||||
.lesser => node.has_left,
|
|
||||||
.greater => node.has_right,
|
|
||||||
|
|
||||||
.equal => {
|
|
||||||
return node.key;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isEmpty(self: *Self) bool {
|
pub fn isEmpty(self: *Self) bool {
|
||||||
return self.active_nodes == null;
|
return self.has_root == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keyValues(self: *const Self) KeyValues {
|
pub fn keyValues(self: *const Self) KeyValues {
|
||||||
return .{ .nodes = self.active_nodes };
|
return .{ .nodes = self.has_root };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(self: *Self, key: Key) ?coral.KeyValuePair(Key, Value) {
|
||||||
|
const root = self.has_root orelse {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const node = root.remove(key) orelse {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
defer {
|
||||||
|
self.free_nodes.destroy(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.key = node.key,
|
||||||
|
.value = node.value,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ fn scheduleName(comptime schedule: anytype) [:0]const u8 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hasState(self: *Self, comptime State: type) ?*State {
|
pub fn hasState(self: *const Self, comptime State: type) ?*State {
|
||||||
if (self.initialized_states.get(.of(State))) |boxed_state| {
|
if (self.initialized_states.get(.of(State))) |boxed_state| {
|
||||||
return boxed_state.has(State).?;
|
return boxed_state.has(State).?;
|
||||||
}
|
}
|
||||||
@ -92,6 +92,8 @@ pub fn run(self: *Self, tasks: *coral.asio.TaskQueue, comptime schedule: anytype
|
|||||||
const systems = self.named_systems.get(schedule_name) orelse (try self.named_systems.insert(coral.heap.allocator, schedule_name, .empty)).?;
|
const systems = self.named_systems.get(schedule_name) orelse (try self.named_systems.insert(coral.heap.allocator, schedule_name, .empty)).?;
|
||||||
|
|
||||||
try systems.run(self, tasks);
|
try systems.run(self, tasks);
|
||||||
|
|
||||||
|
systems.applyDeferred(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setState(self: *Self, state: anytype) error{OutOfMemory}!void {
|
pub fn setState(self: *Self, state: anytype) error{OutOfMemory}!void {
|
||||||
|
@ -7,261 +7,279 @@ const ona = @import("../ona.zig");
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
label: [*:0]const u8,
|
label: [*:0]const u8,
|
||||||
onInsertion: *const fn (*const Self, std.mem.Allocator, *ona.App, *SystemGraph) std.mem.Allocator.Error![]coral.Box,
|
apply: *const fn (coral.Box, *ona.App) void,
|
||||||
onRun: *const fn (*ona.App, []const coral.Box) void,
|
bind: *const fn (*const Self, *ona.App, *SystemGraph) std.mem.Allocator.Error!coral.Box,
|
||||||
|
call: *const fn (coral.Box, *const ona.App) void,
|
||||||
is_blocking: bool,
|
is_blocking: bool,
|
||||||
|
|
||||||
const Param = struct {
|
|
||||||
uses_bind: bool,
|
|
||||||
init_params: []const InitParam,
|
|
||||||
is_blocking: bool,
|
|
||||||
|
|
||||||
const InitParam = struct {
|
|
||||||
type_id: *const coral.TypeId,
|
|
||||||
is_required: bool,
|
|
||||||
is_read_only: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn getInitParams(params: []const std.builtin.Type.Fn.Param) []const InitParam {
|
|
||||||
const init_params = struct {
|
|
||||||
const instance = get: {
|
|
||||||
var buffer: [params.len]InitParam = undefined;
|
|
||||||
|
|
||||||
for (&buffer, params) |*init_param, fn_param| {
|
|
||||||
const FnParam = fn_param.type orelse {
|
|
||||||
@compileError("generic params on Param.init fns are disallowed");
|
|
||||||
};
|
|
||||||
|
|
||||||
init_param.* = switch (@typeInfo(FnParam)) {
|
|
||||||
.pointer => |pointer| .{
|
|
||||||
.type_id = .of(pointer.child),
|
|
||||||
.is_required = true,
|
|
||||||
.is_read_only = pointer.is_const,
|
|
||||||
},
|
|
||||||
|
|
||||||
.optional => |optional| switch (@typeInfo(optional.child)) {
|
|
||||||
.pointer => |pointer| .{
|
|
||||||
.type_id = .of(pointer.child),
|
|
||||||
.is_required = false,
|
|
||||||
.is_read_only = pointer.is_const,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => @compileError("non-pointer optional params on Param.init fns are disallowed"),
|
|
||||||
},
|
|
||||||
|
|
||||||
else => @compileError("params on Param.init fns must be optionals or pointers"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
break :get buffer;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return &init_params.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(comptime fn_param: std.builtin.Type.Fn.Param) Param {
|
|
||||||
const FnParam = fn_param.type orelse {
|
|
||||||
@compileError("generic behavior params are not permitted");
|
|
||||||
};
|
|
||||||
|
|
||||||
const init_fn = coral.meta.hasFn(FnParam, "init") orelse {
|
|
||||||
@compileError(std.fmt.comptimePrint("{s} must contain a .init declaration", .{@typeName(FnParam)}));
|
|
||||||
};
|
|
||||||
|
|
||||||
comptime var uses_bind = false;
|
|
||||||
|
|
||||||
if (coral.meta.hasFn(FnParam, "bind")) |bind_fn| {
|
|
||||||
if (bind_fn.params.len != 0) {
|
|
||||||
@compileError(std.fmt.comptimePrint("{s}.bind cannot accept any parameters", .{@typeName(FnParam)}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const State = switch (@typeInfo(bind_fn.return_type.?)) {
|
|
||||||
.error_union => |error_union| error_union.payload,
|
|
||||||
else => bind_fn.return_type.?,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (init_fn.params.len == 0 or init_fn.params[0].type != *State) {
|
|
||||||
const fn_param_name = @typeName(FnParam);
|
|
||||||
|
|
||||||
@compileError(std.fmt.comptimePrint("The first parameter of fn {s}.init must be of type {s} while fn {s}.bind is present", .{
|
|
||||||
fn_param_name,
|
|
||||||
@typeName(*State),
|
|
||||||
fn_param_name,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
uses_bind = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (coral.meta.Unwrapped(init_fn.return_type.?) != FnParam) {
|
|
||||||
const param_type_name = @typeName(FnParam);
|
|
||||||
|
|
||||||
@compileError(std.fmt.comptimePrint("Fn {s}.init must return some variation of {s}", .{
|
|
||||||
param_type_name,
|
|
||||||
param_type_name,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.init_params = getInitParams(init_fn.params[@intFromBool(uses_bind)..]),
|
|
||||||
.is_blocking = usesTrait(FnParam, "blocking"),
|
|
||||||
.uses_bind = uses_bind,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn after(comptime self: *const Self, comptime dependency: *const Self) *const Self {
|
pub fn of(comptime function: anytype) *const Self {
|
||||||
const afters = struct {
|
const Function = @TypeOf(function);
|
||||||
fn on_insertion(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!void {
|
|
||||||
try self.onInsertion(behavior, app, systems);
|
|
||||||
try systems.depend_on_behavior(app, behavior, dependency);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_run(app: *ona.App) void {
|
const function_fn = switch (@typeInfo(Function)) {
|
||||||
self.onRun(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = Self{
|
|
||||||
.label = std.fmt.comptimePrint("({s} after {s})", .{ self.label, dependency.label }),
|
|
||||||
.onInsertion = on_insertion,
|
|
||||||
.onRun = on_run,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return &afters.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn before(comptime self: *const Self, comptime dependant: *const Self) *const Self {
|
|
||||||
const afters = struct {
|
|
||||||
fn on_insertion(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!void {
|
|
||||||
try systems.depend_on_behavior(app, dependant, behavior);
|
|
||||||
try systems.insert(app, behavior);
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = Self{
|
|
||||||
.label = std.fmt.comptimePrint("({s} before {s})", .{ self.label, dependant.label }),
|
|
||||||
.onInsertion = on_insertion,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return &afters.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn of(comptime call: anytype) *const Self {
|
|
||||||
const Call = @TypeOf(call);
|
|
||||||
|
|
||||||
const call_fn = switch (@typeInfo(Call)) {
|
|
||||||
.@"fn" => |@"fn"| @"fn",
|
.@"fn" => |@"fn"| @"fn",
|
||||||
else => @compileError("`call` parameter must be an fn type"),
|
else => @compileError("`function` parameter must be an fn type"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const behaviors = struct {
|
const behavior = struct {
|
||||||
fn onInsertion(behavior: *const Self, allocator: std.mem.Allocator, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error![]coral.Box {
|
const ParamStates = define: {
|
||||||
var states: [call_fn.params.len]coral.Box = undefined;
|
var param_state_types = [_]type{void} ** function_fn.params.len;
|
||||||
comptime var states_written = 0;
|
|
||||||
|
|
||||||
errdefer {
|
for (0..param_state_types.len, function_fn.params) |i, function_param| {
|
||||||
inline for (states[states_written..]) |*state| {
|
const Param = function_param.type orelse {
|
||||||
state.deinit();
|
@compileError("Behavior fns may not have generic parameters");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (coral.meta.hasFn(Param, "bind")) |bind_fn| {
|
||||||
|
param_state_types[i] = coral.meta.UnwrappedError(bind_fn.return_type.?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline for (call_fn.params) |call_param| {
|
const Tuple = std.meta.Tuple(¶m_state_types);
|
||||||
const behavior_param = comptime Param.init(call_param);
|
|
||||||
|
break :define struct {
|
||||||
|
params: Tuple,
|
||||||
|
|
||||||
|
pub fn deinit(self: *@This()) void {
|
||||||
|
inline for (0..self.params.len) |i| {
|
||||||
|
const ParamState = @TypeOf(self.params[i]);
|
||||||
|
|
||||||
|
if (coral.meta.hasFn(ParamState, "deinit")) |deinit_fn| {
|
||||||
|
if (deinit_fn.params.len != 1 or deinit_fn.params[0].type != *ParamState) {
|
||||||
|
const param_state_name = @typeName(ParamState);
|
||||||
|
|
||||||
|
@compileError(std.fmt.comptimePrint("Fn {s}.deinit expects only 1 argument and it must be of type {s}", .{
|
||||||
|
param_state_name,
|
||||||
|
param_state_name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deinit_fn.return_type != void) {
|
||||||
|
const param_state_name = @typeName(ParamState);
|
||||||
|
|
||||||
|
@compileError(std.fmt.comptimePrint("Fn {s}.deinit is expected to return type void", .{
|
||||||
|
param_state_name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.params[i].deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fn apply(local_state: coral.Box, app: *ona.App) void {
|
||||||
|
var param_states = local_state.has(ParamStates).?;
|
||||||
|
|
||||||
|
inline for (0..param_states.params.len) |i| {
|
||||||
|
const ParamState = @TypeOf(param_states.params[i]);
|
||||||
|
|
||||||
|
if (coral.meta.hasFn(ParamState, "apply")) |apply_fn| {
|
||||||
|
if (apply_fn.params.len != 2 or (apply_fn.params[0].type != *ParamState and apply_fn.params[1].type != *ona.App)) {
|
||||||
|
const param_state_name = @typeName(ParamState);
|
||||||
|
|
||||||
|
@compileError(std.fmt.comptimePrint("Fn {s}.apply expects 2 arguments and they must be of types ({s}, {s})", .{
|
||||||
|
param_state_name,
|
||||||
|
@typeInfo(*ona.App),
|
||||||
|
param_state_name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coral.meta.UnwrappedError(apply_fn.return_type.?) != void) {
|
||||||
|
const param_state_name = @typeName(ParamState);
|
||||||
|
|
||||||
|
@compileError(std.fmt.comptimePrint("Fn {s}.apply is expected to return type void or error-wrapped void", .{
|
||||||
|
param_state_name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
coral.expect(ParamState.apply, .{ ¶m_states.params[i], app });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!coral.Box {
|
||||||
|
inline for (function_fn.params) |function_param| {
|
||||||
|
const Param = function_param.type orelse {
|
||||||
|
@compileError("Behavior fns may not have generic parameters");
|
||||||
|
};
|
||||||
|
|
||||||
|
const init_fn = coral.meta.hasFn(Param, "init") orelse {
|
||||||
|
@compileError(std.fmt.comptimePrint("{s} must have a .init fn to be used as a behavior param", .{@typeName(Param)}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const has_bind_fn = coral.meta.hasFn(Param, "bind");
|
||||||
|
|
||||||
|
inline for (init_fn.params[@intFromBool(has_bind_fn != null)..]) |init_param| {
|
||||||
|
const InitParam = coral.meta.UnwrappedOptional(init_param.type orelse {
|
||||||
|
@compileError(std.fmt.comptimePrint("fn {s}.init may not have generic parameters", .{
|
||||||
|
@typeName(Param),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const init_param_pointer = switch (@typeInfo(InitParam)) {
|
||||||
|
.pointer => |pointer| pointer,
|
||||||
|
|
||||||
|
else => @compileError(std.fmt.comptimePrint("Behavior param fn {s}.init expects arguments as pointer types to state only", .{
|
||||||
|
@typeName(Param),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
inline for (behavior_param.init_params) |init_param| {
|
|
||||||
try systems.dependOnType(app, behavior, .{
|
try systems.dependOnType(app, behavior, .{
|
||||||
.id = init_param.type_id,
|
.is_read_only = init_param_pointer.is_const,
|
||||||
.is_read_only = init_param.is_read_only,
|
.id = .of(init_param_pointer.child),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
states[states_written] = try .init(allocator, if (behavior_param.uses_bind) call_param.type.?.bind() else {});
|
|
||||||
states_written += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return allocator.dupe(coral.Box, &states);
|
var param_states: ParamStates = undefined;
|
||||||
|
|
||||||
|
inline for (0..param_states.params.len, function_fn.params) |i, param| {
|
||||||
|
param_states.params[i] = switch (@TypeOf(param_states.params[i])) {
|
||||||
|
void => {},
|
||||||
|
else => coral.expect(param.type.?.bind, .{}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Errdefer deinit the param states.
|
||||||
|
|
||||||
|
return .init(coral.heap.allocator, param_states);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn onRun(app: *ona.App, states: []const coral.Box) void {
|
fn call(local_state: coral.Box, app: *const ona.App) void {
|
||||||
var call_args: std.meta.ArgsTuple(Call) = undefined;
|
var param_states = local_state.has(ParamStates).?;
|
||||||
|
var function_args: std.meta.ArgsTuple(Function) = undefined;
|
||||||
|
|
||||||
std.debug.assert(states.len == call_args.len);
|
inline for (0..function_args.len) |i| {
|
||||||
|
const Param = function_fn.params[i].type orelse {
|
||||||
|
@compileError("Behavior fns may not have generic parameters");
|
||||||
|
};
|
||||||
|
|
||||||
inline for (0..call_args.len) |i| {
|
var init_args: std.meta.ArgsTuple(@TypeOf(Param.init)) = undefined;
|
||||||
const fn_param = call_fn.params[i];
|
const uses_state = @TypeOf(param_states.params[i]) != void;
|
||||||
const param = comptime Param.init(fn_param);
|
|
||||||
var init_args: std.meta.ArgsTuple(@TypeOf(fn_param.type.?.init)) = undefined;
|
|
||||||
|
|
||||||
if (param.uses_bind) {
|
if (uses_state) {
|
||||||
init_args[0] = states[i].has(@TypeOf(init_args[0].*)).?;
|
init_args[0] = ¶m_states.params[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline for (@intFromBool(param.uses_bind)..init_args.len, param.init_params) |arg_index, init_param| {
|
const init_fn = coral.meta.hasFn(Param, "init") orelse {
|
||||||
if (init_param.is_required) {
|
@compileError(std.fmt.comptimePrint("{s} must have a .init fn to be used as a behavior param", .{@typeName(Param)}));
|
||||||
const InitArg = @TypeOf(init_args[arg_index].*);
|
};
|
||||||
|
|
||||||
init_args[arg_index] = app.hasState(InitArg) orelse {
|
inline for (@intFromBool(uses_state)..init_args.len, init_fn.params[@intFromBool(uses_state)..]) |arg_index, init_param| {
|
||||||
@panic(std.fmt.comptimePrint("{s} is a required state but not present in the App", .{@typeName(InitArg)}));
|
const InitParam = init_param.type.?;
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const InitArg = @TypeOf(init_args[arg_index].*);
|
|
||||||
|
|
||||||
init_args[arg_index] = app.hasState(InitArg);
|
switch (@typeInfo(InitParam)) {
|
||||||
|
.optional => |optional| {
|
||||||
|
const child_pointer = switch (@typeInfo(optional.child)) {
|
||||||
|
.pointer => |pointer| pointer,
|
||||||
|
else => @compileError(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
init_args[arg_index] = app.hasState(child_pointer.child);
|
||||||
|
},
|
||||||
|
|
||||||
|
.pointer => |pointer| {
|
||||||
|
init_args[arg_index] = app.hasState(pointer.child) orelse {
|
||||||
|
@panic(std.fmt.comptimePrint("{s} is a required state but not present in the App", .{
|
||||||
|
@typeName(pointer.child),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
@compileError("? or *");
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
call_args[i] = coral.expect(fn_param.type.?.init, init_args);
|
function_args[i] = coral.expect(Param.init, init_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coral.meta.Unwrapped(call_fn.return_type.?) != void) {
|
if (coral.meta.Unwrapped(function_fn.return_type.?) != void) {
|
||||||
@compileError(std.fmt.comptimePrint("parameter `call` must return some variation of void", .{
|
@compileError(std.fmt.comptimePrint("parameter `function` must return some variation of void", .{
|
||||||
@typeName(Call),
|
@typeName(Function),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
coral.expect(call, call_args);
|
coral.expect(function, function_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = Self{
|
const instance = Self{
|
||||||
.label = @typeName(Call),
|
.label = @typeName(Function),
|
||||||
.onInsertion = onInsertion,
|
.apply = apply,
|
||||||
.onRun = onRun,
|
.bind = bind,
|
||||||
|
.call = call,
|
||||||
|
.is_blocking = is_blocking,
|
||||||
|
};
|
||||||
|
|
||||||
.is_blocking = check: {
|
const is_blocking = infer: {
|
||||||
for (call_fn.params) |fn_param| {
|
for (function_fn.params) |function_param| {
|
||||||
const param = Param.init(fn_param);
|
const Param = function_param.type orelse {
|
||||||
|
@compileError("Behavior fns may not have generic parameters");
|
||||||
|
};
|
||||||
|
|
||||||
if (param.is_blocking) {
|
for (getInitParams(Param)) |init_param| {
|
||||||
break :check true;
|
const InitParam = coral.meta.UnwrappedOptional(init_param.type orelse {
|
||||||
|
@compileError(std.fmt.comptimePrint("fn {s}.init may not have generic parameters", .{
|
||||||
|
@typeName(Param),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const init_param_pointer = switch (@typeInfo(InitParam)) {
|
||||||
|
.pointer => |pointer| pointer,
|
||||||
|
|
||||||
|
else => @compileError(std.fmt.comptimePrint("Behavior param fn {s}.init expects arguments as pointer types to state only", .{
|
||||||
|
@typeName(Param),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usesTrait(init_param_pointer.child, "blocking")) {
|
||||||
|
break :infer true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break :check false;
|
break :infer false;
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return &behaviors.instance;
|
return &behavior.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usesTrait(comptime SystemParam: type, name: []const u8) bool {
|
fn getInitParams(comptime Param: type) []const std.builtin.Type.Fn.Param {
|
||||||
if (@hasDecl(SystemParam, "traits")) {
|
const init_fn = coral.meta.hasFn(Param, "init") orelse {
|
||||||
const Traits = @TypeOf(SystemParam.traits);
|
@compileError(std.fmt.comptimePrint("{s} must have a .init fn to be used as a behavior param", .{
|
||||||
|
@typeName(Param),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (coral.meta.UnwrappedError(init_fn.return_type.?) != Param) {
|
||||||
|
const param_name = @typeName(Param);
|
||||||
|
|
||||||
|
@compileError(std.fmt.comptimePrint("{s}.init must return {s} or an error-wrapped {s} to be used as a behavior param", .{
|
||||||
|
param_name,
|
||||||
|
param_name,
|
||||||
|
param_name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return init_fn.params;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usesTrait(comptime Param: type, name: []const u8) bool {
|
||||||
|
if (@hasDecl(Param, "traits")) {
|
||||||
|
const Traits = @TypeOf(Param.traits);
|
||||||
|
|
||||||
const traits_struct = switch (@typeInfo(Traits)) {
|
const traits_struct = switch (@typeInfo(Traits)) {
|
||||||
.@"struct" => |@"struct"| @"struct",
|
.@"struct" => |@"struct"| @"struct",
|
||||||
|
else => @compileError(std.fmt.comptimePrint("{s}.traits must be a struct type", .{@typeName(Param)})),
|
||||||
else => @compileError(std.fmt.comptimePrint("{s}.traits must be a struct type", .{
|
|
||||||
@typeName(SystemParam),
|
|
||||||
})),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!traits_struct.is_tuple) {
|
if (!traits_struct.is_tuple) {
|
||||||
@compileError(std.fmt.comptimePrint("{s}.traits must be a tuple", .{@typeName(SystemParam)}));
|
@compileError(std.fmt.comptimePrint("{s}.traits must be a tuple", .{@typeName(Param)}));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (traits_struct.fields) |field| {
|
for (traits_struct.fields) |field| {
|
||||||
@ -269,7 +287,7 @@ fn usesTrait(comptime SystemParam: type, name: []const u8) bool {
|
|||||||
@compileError("All members of tuple {s}.traits must be enum literals");
|
@compileError("All members of tuple {s}.traits must be enum literals");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std.mem.eql(u8, @tagName(@field(SystemParam.traits, field.name)), name)) {
|
if (std.mem.eql(u8, @tagName(@field(Param.traits, field.name)), name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,17 +18,13 @@ const BehaviorSet = coral.stack.Sequential(*const ona.App.Behavior);
|
|||||||
|
|
||||||
const Edge = struct {
|
const Edge = struct {
|
||||||
dependencies: BehaviorSet = .empty,
|
dependencies: BehaviorSet = .empty,
|
||||||
param_states: []coral.Box = &.{},
|
local_state: coral.Box,
|
||||||
|
|
||||||
pub fn deinit(self: *Edge, allocator: std.mem.Allocator) void {
|
pub fn deinit(self: *Edge, allocator: std.mem.Allocator) void {
|
||||||
for (self.param_states) |*param_state| {
|
self.local_state.deinit();
|
||||||
param_state.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator.free(self.param_states);
|
|
||||||
self.dependencies.deinit(allocator);
|
self.dependencies.deinit(allocator);
|
||||||
|
|
||||||
self.param_states = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,14 +38,14 @@ const Processed = struct {
|
|||||||
|
|
||||||
const Work = struct {
|
const Work = struct {
|
||||||
behavior: *const ona.App.Behavior,
|
behavior: *const ona.App.Behavior,
|
||||||
param_states: []const coral.Box = &.{},
|
local_state: coral.Box,
|
||||||
};
|
};
|
||||||
|
|
||||||
const WorkSet = coral.stack.Sequential(Work);
|
const WorkSet = coral.stack.Sequential(Work);
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub const RunError = error{
|
pub const RunError = std.mem.Allocator.Error || error{
|
||||||
MissingDependency,
|
MissingDependency,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,26 +55,28 @@ pub const TypeDependency = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.processed.deinit(coral.heap.allocator);
|
const allocator = coral.heap.allocator;
|
||||||
self.parallel_work.deinit(coral.heap.allocator);
|
|
||||||
self.parallel_work_ranges.deinit(coral.heap.allocator);
|
self.processed.deinit(allocator);
|
||||||
self.blocking_work.deinit(coral.heap.allocator);
|
self.parallel_work.deinit(allocator);
|
||||||
|
self.parallel_work_ranges.deinit(allocator);
|
||||||
|
self.blocking_work.deinit(allocator);
|
||||||
|
|
||||||
inline for (.{ &self.edges, &self.state_readers, &self.state_writers }) |map| {
|
inline for (.{ &self.edges, &self.state_readers, &self.state_writers }) |map| {
|
||||||
var key_values = map.keyValues();
|
var key_values = map.keyValues();
|
||||||
|
|
||||||
while (key_values.nextValue()) |value| {
|
while (key_values.nextValue()) |value| {
|
||||||
value.deinit(coral.heap.allocator);
|
value.deinit(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
map.deinit(coral.heap.allocator);
|
map.deinit(allocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dependOnBehavior(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
pub fn dependOnBehavior(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||||
try self.insert(app, dependant);
|
try self.insert(app, dependant);
|
||||||
|
|
||||||
const edges = self.edges.get(dependant) orelse (try self.edges.insert(coral.heap.allocator, dependant, .{})).?;
|
const edges = self.edges.get(dependant).?;
|
||||||
|
|
||||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, edges.dependencies.items.slice(), dependency) == null) {
|
if (std.mem.indexOfScalar(*const ona.App.Behavior, edges.dependencies.items.slice(), dependency) == null) {
|
||||||
try edges.dependencies.pushGrow(coral.heap.allocator, dependency);
|
try edges.dependencies.pushGrow(coral.heap.allocator, dependency);
|
||||||
@ -135,17 +133,28 @@ pub const empty = Self{
|
|||||||
.parallel_work_ranges = .empty,
|
.parallel_work_ranges = .empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn insert(self: *Self, app: *ona.App, behavior: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
pub fn applyDeferred(self: *Self, app: *ona.App) void {
|
||||||
self.processed.clear();
|
for (self.parallel_work.items.slice()) |work| {
|
||||||
|
work.behavior.apply(work.local_state, app);
|
||||||
|
}
|
||||||
|
|
||||||
if (try self.edges.insert(coral.heap.allocator, behavior, .{})) |edge| {
|
for (self.blocking_work.items.slice()) |work| {
|
||||||
edge.param_states = try behavior.onInsertion(behavior, coral.heap.allocator, app, self);
|
work.behavior.apply(work.local_state, app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parallelRun(app: *ona.App, works: []const Work) void {
|
pub fn insert(self: *Self, app: *ona.App, behavior: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||||
for (works) |work| {
|
self.processed.clear();
|
||||||
work.behavior.onRun(app, work.param_states);
|
|
||||||
|
if (try self.edges.insert(coral.heap.allocator, behavior, undefined)) |edge| {
|
||||||
|
errdefer {
|
||||||
|
std.debug.assert(self.edges.remove(behavior) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
edge.* = .{
|
||||||
|
.local_state = try behavior.bind(behavior, app, self),
|
||||||
|
.dependencies = .empty,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,12 +173,12 @@ fn process(self: *Self, behavior: *const ona.App.Behavior, edge: Edge) !Processe
|
|||||||
try switch (processed.is_blocking) {
|
try switch (processed.is_blocking) {
|
||||||
true => self.blocking_work.pushGrow(coral.heap.allocator, .{
|
true => self.blocking_work.pushGrow(coral.heap.allocator, .{
|
||||||
.behavior = behavior,
|
.behavior = behavior,
|
||||||
.param_states = edge.param_states,
|
.local_state = edge.local_state,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
false => self.parallel_work.pushGrow(coral.heap.allocator, .{
|
false => self.parallel_work.pushGrow(coral.heap.allocator, .{
|
||||||
.behavior = behavior,
|
.behavior = behavior,
|
||||||
.param_states = edge.param_states,
|
.local_state = edge.local_state,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -177,7 +186,7 @@ fn process(self: *Self, behavior: *const ona.App.Behavior, edge: Edge) !Processe
|
|||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Allocator.Error || RunError)!void {
|
pub fn run(self: *Self, app: *const ona.App, tasks: *coral.asio.TaskQueue) RunError!void {
|
||||||
if (self.processed.isEmpty()) {
|
if (self.processed.isEmpty()) {
|
||||||
errdefer {
|
errdefer {
|
||||||
self.processed.clear();
|
self.processed.clear();
|
||||||
@ -205,19 +214,26 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var parallel_work_offset: usize = 0;
|
{
|
||||||
|
const parallel_work = self.parallel_work.items.slice();
|
||||||
|
var parallel_work_offset: usize = 0;
|
||||||
|
|
||||||
for (self.parallel_work_ranges.items.slice()) |parallel_work_range| {
|
for (self.parallel_work_ranges.items.slice()) |parallel_work_range| {
|
||||||
const work = self.parallel_work.items.slice()[parallel_work_offset .. parallel_work_offset + parallel_work_range];
|
try tasks.execute(coral.asio.CallTask(runWorkGroup).init(.{
|
||||||
|
app,
|
||||||
|
parallel_work[parallel_work_offset .. parallel_work_offset + parallel_work_range],
|
||||||
|
}));
|
||||||
|
|
||||||
try tasks.execute(coral.asio.CallTask(parallelRun).init(.{ app, work }));
|
parallel_work_offset += parallel_work_range;
|
||||||
|
}
|
||||||
parallel_work_offset += parallel_work_range;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.finish();
|
tasks.finish();
|
||||||
|
runWorkGroup(app, self.blocking_work.items.slice());
|
||||||
|
}
|
||||||
|
|
||||||
for (self.blocking_work.items.slice()) |work| {
|
fn runWorkGroup(app: *const ona.App, parallel_work: []const Work) void {
|
||||||
work.behavior.onRun(app, work.param_states);
|
for (parallel_work) |work| {
|
||||||
|
work.behavior.call(work.local_state, app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,17 @@ pub const Display = struct {
|
|||||||
is_hidden: bool,
|
is_hidden: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn queueSynchronize(context: ona.Exclusive(Context), display: ona.Read(Display)) !void {
|
pub fn synchronize(commands: ona.Commands) !void {
|
||||||
context.ptr.shared.swapBuffers(display.ptr.*);
|
const buffer_swap = struct {
|
||||||
|
fn apply(app: *ona.App) void {
|
||||||
|
const context = app.hasState(Context).?;
|
||||||
|
const display = app.hasState(Display).?;
|
||||||
|
|
||||||
|
context.shared.swapBuffers(display.*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try commands.push(buffer_swap.apply);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(app: *ona.App) !void {
|
pub fn setup(app: *ona.App) !void {
|
||||||
@ -66,5 +75,5 @@ pub fn setup(app: *ona.App) !void {
|
|||||||
try app.setState(context);
|
try app.setState(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
try app.on(.render, .of(queueSynchronize));
|
try app.on(.render, .of(synchronize));
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ pub const Queue = struct {
|
|||||||
commands: coral.stack.Sequential(Command) = .empty,
|
commands: coral.stack.Sequential(Command) = .empty,
|
||||||
has_next: ?*Queue = null,
|
has_next: ?*Queue = null,
|
||||||
|
|
||||||
pub fn cancel(self: *Queue) void {
|
pub fn reset(self: *Queue) void {
|
||||||
self.commands.clear();
|
self.commands.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ const Shared = struct {
|
|||||||
|
|
||||||
while (self.swap_buffers[@intFromBool(!self.is_swapped)].dequeue()) |queue| {
|
while (self.swap_buffers[@intFromBool(!self.is_swapped)].dequeue()) |queue| {
|
||||||
defer {
|
defer {
|
||||||
queue.cancel();
|
queue.reset();
|
||||||
|
|
||||||
queue.has_next = self.has_pooled_queue.load(.acquire);
|
queue.has_next = self.has_pooled_queue.load(.acquire);
|
||||||
|
|
||||||
|
@ -158,6 +158,70 @@ fn Channel(comptime Message: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const Commands = struct {
|
||||||
|
pending: *Buffer,
|
||||||
|
|
||||||
|
const Action = coral.Callable(void, &.{*App});
|
||||||
|
|
||||||
|
const Buffer = struct {
|
||||||
|
arena: std.heap.ArenaAllocator,
|
||||||
|
actions: coral.stack.Sequential(Action),
|
||||||
|
|
||||||
|
pub fn apply(self: *Buffer, app: *App) void {
|
||||||
|
for (self.actions.items.slice()) |action| {
|
||||||
|
action.call(.{app});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(self: *Buffer) void {
|
||||||
|
if (!self.arena.reset(.retain_capacity)) {
|
||||||
|
std.log.warn("Failed to retain command buffer arena capacity", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.actions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Buffer) void {
|
||||||
|
self.actions.deinit(coral.heap.allocator);
|
||||||
|
self.arena.deinit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn bind() Buffer {
|
||||||
|
return .{
|
||||||
|
.arena = .init(coral.heap.allocator),
|
||||||
|
.actions = .empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(buffer: *Buffer) Commands {
|
||||||
|
return .{
|
||||||
|
.pending = buffer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(self: Commands, comptime command: anytype) std.mem.Allocator.Error!void {
|
||||||
|
const Command = @TypeOf(command);
|
||||||
|
|
||||||
|
switch (Command) {
|
||||||
|
fn (*App) void => {
|
||||||
|
try self.pending.actions.pushGrow(coral.heap.allocator, .initFn(command));
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
const arena_allocator = self.pending.arena.allocator();
|
||||||
|
const context = try arena_allocator.create(Command);
|
||||||
|
|
||||||
|
context.* = .{};
|
||||||
|
|
||||||
|
try self.pending.actions.pushGrow(coral.heap.allocator, .initRef(command));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub fn Exclusive(comptime State: type) type {
|
pub fn Exclusive(comptime State: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
ptr: *State,
|
ptr: *State,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user