113 lines
3.7 KiB
Zig
Raw Normal View History

2020-06-04 19:19:34 -07:00
const std = @import("std");
2020-06-05 14:17:25 -07:00
const ErasedPtr = @import("../ecs/utils.zig").ErasedPtr;
2020-06-05 14:15:17 -07:00
2020-06-05 19:15:21 -07:00
/// 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.
2020-06-05 14:15:17 -07:00
pub fn Cache(comptime T: type) type {
2020-06-04 19:19:34 -07:00
return struct {
2020-06-05 14:15:17 -07:00
const Self = @This();
safe_deinit: fn (*@This()) void,
2020-06-05 19:15:21 -07:00
resources: std.AutoHashMap(u32, *T),
2020-06-05 14:15:17 -07:00
allocator: ?*std.mem.Allocator = null,
2020-06-05 19:15:21 -07:00
pub fn initPtr(allocator: *std.mem.Allocator) *@This() {
2020-06-05 14:15:17 -07:00
var cache = allocator.create(@This()) catch unreachable;
cache.safe_deinit = struct {
fn deinit(self: *Self) void {
self.clear();
self.resources.deinit();
self.allocator.?.destroy(self);
}
}.deinit;
2020-06-05 19:15:21 -07:00
cache.resources = std.AutoHashMap(u32, *T).init(allocator);
2020-06-05 14:15:17 -07:00
cache.allocator = allocator;
return cache;
}
2020-06-05 19:15:21 -07:00
pub fn init(allocator: *std.mem.Allocator) @This() {
2020-06-04 19:19:34 -07:00
return .{
2020-06-05 14:15:17 -07:00
.safe_deinit = struct {
fn deinit(self: *Self) void {
self.clear();
self.resources.deinit();
}
}.deinit,
2020-06-05 19:15:21 -07:00
.resources = std.AutoHashMap(u32, *T).init(allocator),
2020-06-04 19:19:34 -07:00
};
}
2020-06-04 23:26:34 -07:00
pub fn deinit(self: *@This()) void {
2020-06-05 14:15:17 -07:00
self.safe_deinit(self);
2020-06-04 19:19:34 -07:00
}
2020-06-05 19:15:21 -07:00
pub fn load(self: *@This(), id: u32, comptime loader: var) @typeInfo(@TypeOf(@field(loader, "load"))).BoundFn.return_type.? {
2020-06-04 19:19:34 -07:00
if (self.resources.getValue(id)) |resource| {
return resource;
}
2020-06-05 19:15:21 -07:00
var resource = loader.load();
2020-06-04 19:19:34 -07:00
_ = self.resources.put(id, resource) catch unreachable;
return resource;
}
2020-06-05 19:15:21 -07:00
pub fn contains(self: *@This(), id: u32) bool {
2020-06-04 19:19:34 -07:00
return self.resources.contains(id);
}
2020-06-05 19:15:21 -07:00
pub fn remove(self: *@This(), id: u32) void {
2020-06-04 19:19:34 -07:00
if (self.resources.remove(id)) |kv| {
if (@hasDecl(T, "deinit")) {
@call(.{ .modifier = .always_inline }, @field(kv.value, "deinit"), .{});
}
}
}
pub fn clear(self: *@This()) void {
// optionally deinit any resources that have a deinit method
if (@hasDecl(T, "deinit")) {
var iter = self.resources.iterator();
while (iter.next()) |kv| {
@call(.{ .modifier = .always_inline }, @field(kv.value, "deinit"), .{});
}
}
self.resources.clear();
}
pub fn size(self: @This()) usize {
return self.resources.size;
}
};
}
test "cache" {
2020-06-05 19:15:21 -07:00
const utils = @import("../ecs/utils.zig");
2020-06-04 19:19:34 -07:00
const Thing = struct {
fart: i32,
pub fn deinit(self: *@This()) void {
std.testing.allocator.destroy(self);
}
};
2020-06-05 19:15:21 -07:00
const ThingLoadArgs = struct {
pub fn load(self: @This()) *Thing {
2020-06-04 19:19:34 -07:00
return std.testing.allocator.create(Thing) catch unreachable;
}
};
2020-06-05 19:15:21 -07:00
var cache = Cache(Thing).init(std.testing.allocator);
2020-06-04 19:19:34 -07:00
defer cache.deinit();
2020-06-05 19:15:21 -07:00
var thing = cache.load(utils.hashString("my/id"), ThingLoadArgs{});
var thing2 = cache.load(utils.hashString("another/id"), ThingLoadArgs{});
2020-06-04 19:19:34 -07:00
std.testing.expectEqual(cache.size(), 2);
2020-06-05 19:15:21 -07:00
cache.remove(utils.hashString("my/id"));
2020-06-04 19:19:34 -07:00
std.testing.expectEqual(cache.size(), 1);
cache.clear();
std.testing.expectEqual(cache.size(), 0);
2020-06-05 14:15:17 -07:00
}