- Zig 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
NOTE: consumers that rely on the deinit magic need to ensure that their `deinit` fns are marked public. If not, they will silently fail to be called. This is due to a change in how `@hasDecl` works and there's nothing we can do about it sorry. IMHO this feature is not a good idea, is antithetical to Zig and I would advise against writing code in this way. I may make the argument that the feature should be deleted separately to landing these changes. Doing this highlighted some obvious simplifications that could potentially result in noticably better compile time perf for consumers that use pools heavily (i.e. zgpu). Which makes me think that there are probably more simplifications that could be done, but that's not in scope here. |
||
| .github/workflows | ||
| src | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| LICENSE | ||
| README.md | ||
zpool
Generic pool & handle implementation for Zig. Based on Andre Weissflog's "Handles Are The Better Pointers".
Exposing API resources using pools and handles is a common way to avoid exposing implementation details to calling code and providing some insulation against stale references in data structures maintained by the caller.
When the caller is provided a handle instead of an opaque pointer, the API implementation is free to move resources around, replace them, and even discard them.
Pool(index_bits: u8, cycle_bits: u8, TResource: type, TColumns: type)
Handle(index_bits: u8, cycle_bits: u8, TResource: type)
The generic Pool type has configurable bit distribution for the
Handle's index/cycle fields, and supports multiple columns of data to
be indexed by a handle, using std.MultiArrayList to store all of the pool
data in a single memory allocation. The TResource parameter ensures the pool
and handle types can be distinct types even when other parameters are the same.
Getting started
Example build.zig:
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{ ... });
const zpool = b.dependency("zpool", .{});
exe.root_module.addImport("zpool", zpool.module("root"));
}
Now in your code you may import and use zpool:
const Pool = @import("zpool").Pool;
const ImagePtr = graphics.Image;
const ImageInfo = graphics.ImageInfo;
pub const ImagePool = Pool(16, 16, ImagePtr, struct {
ptr: ImagePtr,
info: ImageInfo,
});
pub const ImageHandle = ImagePool.Handle;
var imagePool = ImagePool.initMaxCapacity(allocator);
defer pool.deinit();
pub fn acquireImage(info: ImageInfo) !ImageHandle {
const handle : ImageHandle = try imagePool.add(.{
.ptr = graphics.createImage(info),
.info = info,
});
return handle;
}
pub fn drawImage(handle: ImageHandle) !void {
// get the stored ImagePtr
const ptr : ImagePtr = try imagePool.getColumn(handle, .ptr);
graphics.drawImage(ptr);
}
pub fn resizeImage(handle: ImageHandle, width: u16, height: u16) !void {
// get a pointer to the stored ImageInfo
const info : *ImageInfo = try imagePool.getColumnPtr(handle, .info);
const old_width = info.width;
const old_height = info.height;
const old_pixels = // allocate memory to store old pixels
// get the stored ImagePtr
const ptr = try imagePool.getColumn(handle, .ptr);
graphics.readPixels(ptr, old_pixels);
const new_pixels = // allocate memory to store new pixels
super_eagle.resizeImage(
old_width, old_height, old_pixels,
new_width, new_height, new_pixels);
graphics.writePixels(ptr, new_width, new_height, new_pixels);
// update the stored ImageInfo
info.width = new_width;
info.height = new_height;
}