ona/src/coral/Box.zig

90 lines
2.4 KiB
Zig
Raw Normal View History

const coral = @import("./coral.zig");
const std = @import("std");
2025-08-05 18:07:09 +01:00
destroy: *const fn (*anyopaque) void,
erased: *anyopaque,
fn Layout(comptime Type: type) type {
2025-08-05 18:07:09 +01:00
return switch (Type) {
void => struct {
fn destroy(_: *anyopaque) void {}
},
2025-08-05 18:07:09 +01:00
else => struct {
allocator: std.mem.Allocator,
value: Type,
2025-08-05 18:07:09 +01:00
const TypeLayout = @This();
2025-08-05 18:07:09 +01:00
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),
})),
};
2025-08-05 18:07:09 +01:00
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 {
2025-08-05 18:07:09 +01:00
self.destroy(self.erased);
self.* = undefined;
}
2025-08-05 18:07:09 +01:00
pub fn has(self: Self, comptime Value: type) ?*Value {
const ValueLayout = Layout(Value);
2025-08-05 18:07:09 +01:00
if (self.destroy == ValueLayout.destroy) {
const layout: *ValueLayout = @ptrCast(@alignCast(self.erased));
return &layout.value;
}
return null;
}
2025-08-05 18:07:09 +01:00
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);
}
2025-08-05 18:07:09 +01:00
if (@hasField(ValueLayout, "allocator")) {
value_layout.allocator = allocator;
}
if (@hasField(ValueLayout, "value")) {
value_layout.value = value;
}
return .{
.erased = @ptrCast(value_layout),
2025-08-05 18:07:09 +01:00
.destroy = ValueLayout.destroy,
};
}