Generic pool & handle implementation for Zig.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Chris Heyes f99ee2483b
[BREAKING] Upgrade to Zig 0.17.0 (#11)
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.
2026-10-23 01:32:51 +01:00
.github/workflows upgrade setup-zig 2025-07-20 22:46:33 +01:00
src [BREAKING] Upgrade to Zig 0.17.0 (#11) 2026-10-23 01:32:51 +01:00
.gitignore files from monorepo @ 613d847104a072fc1d4442f58d5f50b3f9c3f27c 2024-11-03 21:55:06 +00:00
build.zig - Fixup build script deprecations (#3) 2025-07-20 10:51:39 +01:00
build.zig.zon [BREAKING] Upgrade to Zig 0.17.0 (#11) 2026-10-23 01:32:51 +01:00
LICENSE files from monorepo @ 613d847104a072fc1d4442f58d5f50b3f9c3f27c 2024-11-03 21:55:06 +00:00
README.md Update README.md 2024-11-03 21:55:44 +00:00

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;
}