ona/src/coral/Box.zig
kayomn d2a246fce0
Some checks failed
continuous-integration/drone Build is failing
Add system-parameterised local states
2025-08-05 18:07:09 +01:00

90 lines
2.4 KiB
Zig

const coral = @import("./coral.zig");
const std = @import("std");
destroy: *const fn (*anyopaque) void,
erased: *anyopaque,
fn Layout(comptime Type: type) type {
return switch (Type) {
void => struct {
fn destroy(_: *anyopaque) void {}
},
else => struct {
allocator: std.mem.Allocator,
value: Type,
const TypeLayout = @This();
fn destroy(erased: *anyopaque) void {
const layout: *TypeLayout = @ptrCast(@alignCast(erased));
if (@hasDecl(Type, "deinit")) {
const deinit_fn = switch (@typeInfo(@TypeOf(Type.deinit))) {
.@"fn" => |@"fn"| @"fn",
else => @compileError(std.fmt.comptimePrint("Declaration `{s}.deinit` must be an fn type to be boxable", .{
@typeName(Type),
})),
};
if (deinit_fn.params.len != 1 or deinit_fn.params[0].type != *Type) {
@compileError(std.fmt.comptimePrint("Fn `{s}.deinit` is only permitted 1 parameter to be boxable and it must be of type {s}", .{
@typeName(Type),
@typeName(*Type),
}));
}
layout.value.deinit();
}
layout.allocator.destroy(layout);
}
},
};
}
const Self = @This();
pub fn deinit(self: *Self) void {
self.destroy(self.erased);
self.* = undefined;
}
pub fn has(self: Self, comptime Value: type) ?*Value {
const ValueLayout = Layout(Value);
if (self.destroy == ValueLayout.destroy) {
const layout: *ValueLayout = @ptrCast(@alignCast(self.erased));
return &layout.value;
}
return null;
}
pub fn init(allocator: std.mem.Allocator, value: anytype) std.mem.Allocator.Error!Self {
const Value = @TypeOf(value);
const ValueLayout = Layout(Value);
const value_layout = try allocator.create(ValueLayout);
errdefer {
allocator.destroy(value_layout);
}
if (@hasField(ValueLayout, "allocator")) {
value_layout.allocator = allocator;
}
if (@hasField(ValueLayout, "value")) {
value_layout.value = value;
}
return .{
.erased = @ptrCast(value_layout),
.destroy = ValueLayout.destroy,
};
}