|
|
|
@ -1,24 +1,18 @@
|
|
|
|
|
const std = @import("std");
|
|
|
|
|
const ErasedPtr = @import("../ecs/utils.zig").ErasedPtr;
|
|
|
|
|
|
|
|
|
|
/// Simple cache for resources of a given type. TLoader should be a struct that implements a single
|
|
|
|
|
/// method: load(args: var) *T. If any resource has a deinit method it will be called when clear
|
|
|
|
|
/// or remove is called.
|
|
|
|
|
/// Simple cache for resources of a given type. If any resource has a deinit method it will be called when clear
|
|
|
|
|
/// or remove is called. Implementing a "loader" which is passed to "load" is a struct with one method:
|
|
|
|
|
/// - load(self: @This()) *T.
|
|
|
|
|
pub fn Cache(comptime T: type) type {
|
|
|
|
|
return struct {
|
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
|
|
safe_deinit: fn (*@This()) void,
|
|
|
|
|
resources: std.AutoHashMap(u16, *T),
|
|
|
|
|
loader: ErasedPtr,
|
|
|
|
|
resources: std.AutoHashMap(u32, *T),
|
|
|
|
|
allocator: ?*std.mem.Allocator = null,
|
|
|
|
|
|
|
|
|
|
pub fn initPtr(allocator: *std.mem.Allocator, comptime LoaderT: type) *@This() {
|
|
|
|
|
const ptr = if (@sizeOf(LoaderT) > 0)
|
|
|
|
|
ErasedPtr.init(allocator.create(LoaderT) catch unreachable)
|
|
|
|
|
else
|
|
|
|
|
ErasedPtr.init(LoaderT);
|
|
|
|
|
|
|
|
|
|
pub fn initPtr(allocator: *std.mem.Allocator) *@This() {
|
|
|
|
|
var cache = allocator.create(@This()) catch unreachable;
|
|
|
|
|
cache.safe_deinit = struct {
|
|
|
|
|
fn deinit(self: *Self) void {
|
|
|
|
@ -27,18 +21,12 @@ pub fn Cache(comptime T: type) type {
|
|
|
|
|
self.allocator.?.destroy(self);
|
|
|
|
|
}
|
|
|
|
|
}.deinit;
|
|
|
|
|
cache.loader = ptr;
|
|
|
|
|
cache.resources = std.AutoHashMap(u16, *T).init(allocator);
|
|
|
|
|
cache.resources = std.AutoHashMap(u32, *T).init(allocator);
|
|
|
|
|
cache.allocator = allocator;
|
|
|
|
|
return cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn init(allocator: *std.mem.Allocator, comptime LoaderT: type) @This() {
|
|
|
|
|
const ptr = if (@sizeOf(LoaderT) > 0)
|
|
|
|
|
ErasedPtr.init(std.testing.allocator.create(LoaderT) catch unreachable)
|
|
|
|
|
else
|
|
|
|
|
ErasedPtr.init(LoaderT);
|
|
|
|
|
|
|
|
|
|
pub fn init(allocator: *std.mem.Allocator) @This() {
|
|
|
|
|
return .{
|
|
|
|
|
.safe_deinit = struct {
|
|
|
|
|
fn deinit(self: *Self) void {
|
|
|
|
@ -46,8 +34,7 @@ pub fn Cache(comptime T: type) type {
|
|
|
|
|
self.resources.deinit();
|
|
|
|
|
}
|
|
|
|
|
}.deinit,
|
|
|
|
|
.loader = ptr,
|
|
|
|
|
.resources = std.AutoHashMap(u16, *T).init(allocator),
|
|
|
|
|
.resources = std.AutoHashMap(u32, *T).init(allocator),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -55,21 +42,21 @@ pub fn Cache(comptime T: type) type {
|
|
|
|
|
self.safe_deinit(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn load(self: *@This(), id: u16, comptime LoaderT: type, args: LoaderT.LoadArgs) *T {
|
|
|
|
|
pub fn load(self: *@This(), id: u32, comptime loader: var) @typeInfo(@TypeOf(@field(loader, "load"))).BoundFn.return_type.? {
|
|
|
|
|
if (self.resources.getValue(id)) |resource| {
|
|
|
|
|
return resource;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var resource = self.loader.as(LoaderT).load(args);
|
|
|
|
|
var resource = loader.load();
|
|
|
|
|
_ = self.resources.put(id, resource) catch unreachable;
|
|
|
|
|
return resource;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn contains(self: *@This(), id: u16) bool {
|
|
|
|
|
pub fn contains(self: *@This(), id: u32) bool {
|
|
|
|
|
return self.resources.contains(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn remove(self: *@This(), id: u16) void {
|
|
|
|
|
pub fn remove(self: *@This(), id: u32) void {
|
|
|
|
|
if (self.resources.remove(id)) |kv| {
|
|
|
|
|
if (@hasDecl(T, "deinit")) {
|
|
|
|
|
@call(.{ .modifier = .always_inline }, @field(kv.value, "deinit"), .{});
|
|
|
|
@ -95,6 +82,8 @@ pub fn Cache(comptime T: type) type {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test "cache" {
|
|
|
|
|
const utils = @import("../ecs/utils.zig");
|
|
|
|
|
|
|
|
|
|
const Thing = struct {
|
|
|
|
|
fart: i32,
|
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
|
|
@ -102,21 +91,20 @@ test "cache" {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const ThingLoader = struct {
|
|
|
|
|
pub const LoadArgs = struct {};
|
|
|
|
|
pub fn load(self: @This(), args: var) *Thing {
|
|
|
|
|
const ThingLoadArgs = struct {
|
|
|
|
|
pub fn load(self: @This()) *Thing {
|
|
|
|
|
return std.testing.allocator.create(Thing) catch unreachable;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var cache = Cache(Thing).init(std.testing.allocator, ThingLoader);
|
|
|
|
|
var cache = Cache(Thing).init(std.testing.allocator);
|
|
|
|
|
defer cache.deinit();
|
|
|
|
|
|
|
|
|
|
var thing = cache.load(6, ThingLoader, ThingLoader.LoadArgs{});
|
|
|
|
|
var thing2 = cache.load(2, ThingLoader, ThingLoader.LoadArgs{});
|
|
|
|
|
var thing = cache.load(utils.hashString("my/id"), ThingLoadArgs{});
|
|
|
|
|
var thing2 = cache.load(utils.hashString("another/id"), ThingLoadArgs{});
|
|
|
|
|
std.testing.expectEqual(cache.size(), 2);
|
|
|
|
|
|
|
|
|
|
cache.remove(2);
|
|
|
|
|
cache.remove(utils.hashString("my/id"));
|
|
|
|
|
std.testing.expectEqual(cache.size(), 1);
|
|
|
|
|
|
|
|
|
|
cache.clear();
|
|
|
|
|