Project template
This commit is contained in:
21
vendor/zpool/LICENSE
vendored
Normal file
21
vendor/zpool/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 zig-gamedev contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
96
vendor/zpool/README.md
vendored
Normal file
96
vendor/zpool/README.md
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
# [zpool](https://github.com/zig-gamedev/zpool)
|
||||
|
||||
Generic pool & handle implementation for Zig. Based on [Andre Weissflog's "Handles Are The Better Pointers"](https://floooh.github.io/2018/06/17/handles-vs-pointers.html).
|
||||
|
||||
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.
|
||||
|
||||
```zig
|
||||
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`:
|
||||
|
||||
```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`:
|
||||
|
||||
```zig
|
||||
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;
|
||||
```
|
||||
|
||||
```zig
|
||||
var imagePool = ImagePool.initMaxCapacity(allocator);
|
||||
defer pool.deinit();
|
||||
```
|
||||
|
||||
```zig
|
||||
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;
|
||||
}
|
||||
|
||||
```
|
||||
22
vendor/zpool/build.zig
vendored
Normal file
22
vendor/zpool/build.zig
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
_ = b.addModule("root", .{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
});
|
||||
|
||||
const test_step = b.step("test", "Run zpool tests");
|
||||
|
||||
const tests = b.addTest(.{
|
||||
.name = "zpool-tests",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
b.installArtifact(tests);
|
||||
|
||||
test_step.dependOn(&b.addRunArtifact(tests).step);
|
||||
}
|
||||
11
vendor/zpool/build.zig.zon
vendored
Normal file
11
vendor/zpool/build.zig.zon
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.{
|
||||
.name = "zpool",
|
||||
.version = "0.11.0-dev",
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
"README.md",
|
||||
"LICENSE",
|
||||
},
|
||||
}
|
||||
174
vendor/zpool/src/embedded_ring_queue.zig
vendored
Normal file
174
vendor/zpool/src/embedded_ring_queue.zig
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn EmbeddedRingQueue(comptime TElement: type) type {
|
||||
const assert = std.debug.assert;
|
||||
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
pub const Error = error{
|
||||
Empty,
|
||||
Full,
|
||||
};
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
pub const Element = TElement;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
head: usize = 0,
|
||||
tail: usize = 0,
|
||||
storage: []Element = &.{},
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
pub fn init(buffer: []Element) Self {
|
||||
return .{ .storage = buffer };
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
pub fn capacity(self: Self) usize {
|
||||
return self.storage.len;
|
||||
}
|
||||
|
||||
pub fn len(self: Self) usize {
|
||||
return self.tail -% self.head;
|
||||
}
|
||||
|
||||
pub fn empty(self: Self) bool {
|
||||
return self.len() == 0;
|
||||
}
|
||||
|
||||
pub fn full(self: Self) bool {
|
||||
return self.len() == self.capacity();
|
||||
}
|
||||
|
||||
pub fn clear(self: *Self) void {
|
||||
self.head = 0;
|
||||
self.tail = 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
pub fn enqueue(self: *Self, value: Element) Error!void {
|
||||
if (self.enqueueIfNotFull(value)) {
|
||||
return;
|
||||
}
|
||||
return Error.Full;
|
||||
}
|
||||
|
||||
pub fn dequeue(self: *Self) Error!Element {
|
||||
var value: Element = undefined;
|
||||
if (self.dequeueIfNotEmpty(&value)) {
|
||||
return value;
|
||||
}
|
||||
return Error.Empty;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
pub fn enqueueIfNotFull(self: *Self, value: Element) bool {
|
||||
if (self.full()) {
|
||||
return false;
|
||||
}
|
||||
self.enqueueUnchecked(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn dequeueIfNotEmpty(self: *Self, value: *Element) bool {
|
||||
if (self.empty()) {
|
||||
return false;
|
||||
}
|
||||
self.dequeueUnchecked(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
pub fn enqueueAssumeNotFull(self: *Self, value: Element) void {
|
||||
assert(!self.full());
|
||||
self.enqueueUnchecked(value);
|
||||
}
|
||||
|
||||
pub fn dequeueAssumeNotEmpty(self: *Self) Element {
|
||||
assert(!self.empty());
|
||||
var value: Element = undefined;
|
||||
self.dequeueUnchecked(&value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
pub fn enqueueUnchecked(self: *Self, value: Element) void {
|
||||
const tail_index = self.tail % self.storage.len;
|
||||
self.storage[tail_index] = value;
|
||||
self.tail +%= 1;
|
||||
}
|
||||
|
||||
pub fn dequeueUnchecked(self: *Self, value: *Element) void {
|
||||
const head_index = self.head % self.storage.len;
|
||||
value.* = self.storage[head_index];
|
||||
self.head +%= 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
test "EmbeddedRingQueue basics" {
|
||||
var buffer: [16]usize = undefined;
|
||||
var queue = EmbeddedRingQueue(usize).init(buffer[0..]);
|
||||
|
||||
try expectEqual(buffer.len, queue.capacity());
|
||||
try expectEqual(@as(usize, 0), queue.len());
|
||||
try expectEqual(true, queue.empty());
|
||||
try expectEqual(false, queue.full());
|
||||
|
||||
for (buffer, 0..) |_, i| {
|
||||
try expectEqual(i, queue.len());
|
||||
try queue.enqueue(i);
|
||||
try expectEqual(i, buffer[i]);
|
||||
}
|
||||
|
||||
try expectEqual(buffer.len, queue.capacity());
|
||||
try expectEqual(buffer.len, queue.len());
|
||||
try expectEqual(false, queue.empty());
|
||||
try expectEqual(true, queue.full());
|
||||
|
||||
for (buffer, 0..) |_, i| {
|
||||
try expectEqual(buffer.len - i, queue.len());
|
||||
const j = try queue.dequeue();
|
||||
try expectEqual(i, j);
|
||||
}
|
||||
|
||||
try expectEqual(buffer.len, queue.capacity());
|
||||
try expectEqual(@as(usize, 0), queue.len());
|
||||
try expectEqual(true, queue.empty());
|
||||
try expectEqual(false, queue.full());
|
||||
|
||||
for (buffer, 0..) |_, i| {
|
||||
try expectEqual(i, queue.len());
|
||||
try queue.enqueue(i);
|
||||
try expectEqual(i, buffer[i]);
|
||||
}
|
||||
|
||||
try expectEqual(buffer.len, queue.capacity());
|
||||
try expectEqual(buffer.len, queue.len());
|
||||
try expectEqual(false, queue.empty());
|
||||
try expectEqual(true, queue.full());
|
||||
|
||||
queue.clear();
|
||||
|
||||
try expectEqual(buffer.len, queue.capacity());
|
||||
try expectEqual(@as(usize, 0), queue.len());
|
||||
try expectEqual(true, queue.empty());
|
||||
try expectEqual(false, queue.full());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
278
vendor/zpool/src/handle.zig
vendored
Normal file
278
vendor/zpool/src/handle.zig
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
const std = @import("std");
|
||||
|
||||
/// Returns a struct consisting of an array `index` and a semi-unique `cycle`,
|
||||
/// which exists to distinguish handles with the same array `index`.
|
||||
///
|
||||
/// The `cycle` value is only unique within the incremental period of an
|
||||
/// unsigned integer with `cycle_bits`, so a larger number of `cycle_bits`
|
||||
/// provides a larger scope of identifiable conflicts between handles for the
|
||||
/// same `index`.
|
||||
///
|
||||
/// `Handle` is generic because while the `{ index, cycle }` pattern is widely
|
||||
/// applicable, a good distribution of bits between `index` and `cycle` and the
|
||||
/// overall size of a handle are highly dependent on the lifecycle of the
|
||||
/// resource being identified by a handle and the systems consuming handles.
|
||||
///
|
||||
/// Reasonable values for `index_bits` depend on the maximum number of
|
||||
/// uniquely identifiable resources your API will to identify with handles.
|
||||
/// Generally this is directly tied to the length of the array(s) in which
|
||||
/// you will store data to be referenced by a handle's `index`.
|
||||
///
|
||||
/// Reasonable values for `cycle_bits` depend on the frequency with which your
|
||||
/// API expects to be issuing handles, and how many cycles of your application
|
||||
/// are likely to elapse before an expired handle will likely no longer be
|
||||
/// retained by the API caller's data structures.
|
||||
///
|
||||
/// For example, a `Handle(16, 16)` may be sufficient for a GPU resource like
|
||||
/// a texture or buffer, where 64k instances of that resource is a reasonable
|
||||
/// upper bound.
|
||||
///
|
||||
/// A `Handle(22, 10)` may be more appropriate to identify an entity in a
|
||||
/// system where we can safely assume that 4 million entities, is a lot, and
|
||||
/// that API callers can discover and discard expired entity handles within
|
||||
/// 1024 frames of an entity being destroyed and its handle's `index` being
|
||||
/// reissued for use by a distinct entity.
|
||||
///
|
||||
/// `TResource` identifies type of resource referenced by a handle, and
|
||||
/// provides a type-safe distinction between two otherwise equivalently
|
||||
/// configured `Handle` types, such as:
|
||||
/// * `const BufferHandle = Handle(16, 16, Buffer);`
|
||||
/// * `const TextureHandle = Handle(16, 16, Texture);`
|
||||
///
|
||||
/// The total size of a handle will always be the size of an addressable
|
||||
/// unsigned integer of type `u8`, `u16`, `u32`, `u64`, `u128`, or `u256`.
|
||||
pub fn Handle(
|
||||
comptime index_bits: u8,
|
||||
comptime cycle_bits: u8,
|
||||
comptime TResource: type,
|
||||
) type {
|
||||
if (index_bits == 0) @compileError("index_bits must be greater than 0");
|
||||
if (cycle_bits == 0) @compileError("cycle_bits must be greater than 0");
|
||||
|
||||
const id_bits: u16 = @as(u16, index_bits) + @as(u16, cycle_bits);
|
||||
const Id = switch (id_bits) {
|
||||
8 => u8,
|
||||
16 => u16,
|
||||
32 => u32,
|
||||
64 => u64,
|
||||
128 => u128,
|
||||
256 => u256,
|
||||
else => @compileError("index_bits + cycle_bits must sum to exactly " ++
|
||||
"8, 16, 32, 64, 128, or 256 bits"),
|
||||
};
|
||||
|
||||
const field_bits = @max(index_bits, cycle_bits);
|
||||
|
||||
const utils = @import("utils.zig");
|
||||
const UInt = utils.UInt;
|
||||
const AddressableUInt = utils.AddressableUInt;
|
||||
|
||||
return extern struct {
|
||||
const Self = @This();
|
||||
|
||||
const HandleType = Self;
|
||||
const IndexType = UInt(index_bits);
|
||||
const CycleType = UInt(cycle_bits);
|
||||
const HandleUnion = extern union {
|
||||
id: Id,
|
||||
bits: packed struct {
|
||||
cycle: CycleType, // least significant bits
|
||||
index: IndexType, // most significant bits
|
||||
},
|
||||
};
|
||||
|
||||
pub const Resource = TResource;
|
||||
|
||||
pub const AddressableCycle = AddressableUInt(field_bits);
|
||||
pub const AddressableIndex = AddressableUInt(field_bits);
|
||||
|
||||
pub const max_cycle = ~@as(CycleType, 0);
|
||||
pub const max_index = ~@as(IndexType, 0);
|
||||
pub const max_count = @as(Id, max_index - 1) + 2;
|
||||
|
||||
id: Id = 0,
|
||||
|
||||
pub const nil = Self{ .id = 0 };
|
||||
|
||||
pub fn init(i: IndexType, c: CycleType) Self {
|
||||
const u = HandleUnion{ .bits = .{
|
||||
.cycle = c,
|
||||
.index = i,
|
||||
} };
|
||||
return .{ .id = u.id };
|
||||
}
|
||||
|
||||
pub fn cycle(self: Self) CycleType {
|
||||
const u = HandleUnion{ .id = self.id };
|
||||
return u.bits.cycle;
|
||||
}
|
||||
|
||||
pub fn index(self: Self) IndexType {
|
||||
const u = HandleUnion{ .id = self.id };
|
||||
return u.bits.index;
|
||||
}
|
||||
|
||||
/// Unpacks the `index` and `cycle` bit fields that comprise
|
||||
/// `Handle.id` into an `AddressableHandle`, which stores
|
||||
/// the `index` and `cycle` values in pointer-addressable fields.
|
||||
pub fn addressable(self: Self) AddressableHandle {
|
||||
const u = HandleUnion{ .id = self.id };
|
||||
return .{
|
||||
.cycle = u.bits.cycle,
|
||||
.index = u.bits.index,
|
||||
};
|
||||
}
|
||||
|
||||
/// When you want to directly access the `index` and `cycle` of a
|
||||
/// handle, first convert it to an `AddressableHandle` by calling
|
||||
/// `Handle.addressable()`.
|
||||
/// An `AddressableHandle` can be converted back into a "compact"
|
||||
/// `Handle` by calling `AddressableHandle.compact()`.
|
||||
pub const AddressableHandle = struct {
|
||||
cycle: AddressableCycle = 0,
|
||||
index: AddressableIndex = 0,
|
||||
|
||||
/// Returns the corresponding `Handle`
|
||||
pub fn handle(self: AddressableHandle) HandleType {
|
||||
const u = HandleUnion{ .bits = .{
|
||||
.cycle = @as(CycleType, @intCast(self.cycle)),
|
||||
.index = @as(IndexType, @intCast(self.index)),
|
||||
} };
|
||||
return .{ .id = u.id };
|
||||
}
|
||||
};
|
||||
|
||||
pub fn format(
|
||||
self: Self,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = fmt;
|
||||
_ = options;
|
||||
const n = @typeName(Resource);
|
||||
const a = self.addressable();
|
||||
return writer.print("{s}[{}#{}]", .{ n, a.index, a.cycle });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
test "Handle sizes and alignments" {
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
|
||||
{
|
||||
const H = Handle(4, 4, void);
|
||||
try expectEqual(@sizeOf(u8), @sizeOf(H));
|
||||
try expectEqual(@alignOf(u8), @alignOf(H));
|
||||
try expectEqual(4, @bitSizeOf(H.IndexType));
|
||||
try expectEqual(4, @bitSizeOf(H.CycleType));
|
||||
try expectEqual(8, @bitSizeOf(H.AddressableIndex));
|
||||
try expectEqual(8, @bitSizeOf(H.AddressableCycle));
|
||||
|
||||
const A = H.AddressableHandle;
|
||||
try expectEqual(@sizeOf(u16), @sizeOf(A));
|
||||
try expectEqual(@alignOf(u8), @alignOf(A));
|
||||
}
|
||||
|
||||
{
|
||||
const H = Handle(6, 2, void);
|
||||
try expectEqual(@sizeOf(u8), @sizeOf(H));
|
||||
try expectEqual(@alignOf(u8), @alignOf(H));
|
||||
try expectEqual(6, @bitSizeOf(H.IndexType));
|
||||
try expectEqual(2, @bitSizeOf(H.CycleType));
|
||||
try expectEqual(8, @bitSizeOf(H.AddressableIndex));
|
||||
try expectEqual(8, @bitSizeOf(H.AddressableCycle));
|
||||
|
||||
const A = H.AddressableHandle;
|
||||
try expectEqual(@sizeOf(u16), @sizeOf(A));
|
||||
try expectEqual(@alignOf(u8), @alignOf(A));
|
||||
}
|
||||
|
||||
{
|
||||
const H = Handle(8, 8, void);
|
||||
try expectEqual(@sizeOf(u16), @sizeOf(H));
|
||||
try expectEqual(@alignOf(u16), @alignOf(H));
|
||||
try expectEqual(8, @bitSizeOf(H.IndexType));
|
||||
try expectEqual(8, @bitSizeOf(H.CycleType));
|
||||
try expectEqual(8, @bitSizeOf(H.AddressableIndex));
|
||||
try expectEqual(8, @bitSizeOf(H.AddressableCycle));
|
||||
|
||||
const A = H.AddressableHandle;
|
||||
try expectEqual(@sizeOf(u16), @sizeOf(A));
|
||||
try expectEqual(@alignOf(u8), @alignOf(A));
|
||||
}
|
||||
|
||||
{
|
||||
const H = Handle(12, 4, void);
|
||||
try expectEqual(@sizeOf(u16), @sizeOf(H));
|
||||
try expectEqual(@alignOf(u16), @alignOf(H));
|
||||
try expectEqual(12, @bitSizeOf(H.IndexType));
|
||||
try expectEqual(4, @bitSizeOf(H.CycleType));
|
||||
try expectEqual(16, @bitSizeOf(H.AddressableIndex));
|
||||
try expectEqual(16, @bitSizeOf(H.AddressableCycle));
|
||||
|
||||
const A = H.AddressableHandle;
|
||||
try expectEqual(@sizeOf(u32), @sizeOf(A));
|
||||
try expectEqual(@alignOf(u16), @alignOf(A));
|
||||
}
|
||||
|
||||
{
|
||||
const H = Handle(16, 16, void);
|
||||
try expectEqual(@sizeOf(u32), @sizeOf(H));
|
||||
try expectEqual(@alignOf(u32), @alignOf(H));
|
||||
try expectEqual(16, @bitSizeOf(H.IndexType));
|
||||
try expectEqual(16, @bitSizeOf(H.CycleType));
|
||||
try expectEqual(16, @bitSizeOf(H.AddressableIndex));
|
||||
try expectEqual(16, @bitSizeOf(H.AddressableCycle));
|
||||
|
||||
const A = H.AddressableHandle;
|
||||
try expectEqual(@sizeOf(u32), @sizeOf(A));
|
||||
try expectEqual(@alignOf(u16), @alignOf(A));
|
||||
}
|
||||
|
||||
{
|
||||
const H = Handle(22, 10, void);
|
||||
try expectEqual(@sizeOf(u32), @sizeOf(H));
|
||||
try expectEqual(@alignOf(u32), @alignOf(H));
|
||||
try expectEqual(22, @bitSizeOf(H.IndexType));
|
||||
try expectEqual(10, @bitSizeOf(H.CycleType));
|
||||
try expectEqual(32, @bitSizeOf(H.AddressableIndex));
|
||||
try expectEqual(32, @bitSizeOf(H.AddressableCycle));
|
||||
|
||||
const A = H.AddressableHandle;
|
||||
try expectEqual(@sizeOf(u64), @sizeOf(A));
|
||||
try expectEqual(@alignOf(u32), @alignOf(A));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
test "Handle sort order" {
|
||||
const expect = std.testing.expect;
|
||||
|
||||
const handle = Handle(4, 4, void).init;
|
||||
const a = handle(0, 3);
|
||||
const b = handle(1, 1);
|
||||
|
||||
// id order is consistent with index order, even when cycle order is not
|
||||
try expect(a.id < b.id);
|
||||
try expect(a.index() < b.index());
|
||||
try expect(a.cycle() > b.cycle());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
test "Handle.format()" {
|
||||
const bufPrint = std.fmt.bufPrint;
|
||||
const expectEqualStrings = std.testing.expectEqualStrings;
|
||||
|
||||
const Foo = struct {};
|
||||
const H = Handle(12, 4, Foo);
|
||||
const h = H.init(0, 1);
|
||||
|
||||
var buffer = [_]u8{0} ** 128;
|
||||
const s = try bufPrint(buffer[0..], "{}", .{h});
|
||||
try expectEqualStrings("handle.test.Handle.format().Foo[0#1]", s);
|
||||
}
|
||||
8
vendor/zpool/src/main.zig
vendored
Normal file
8
vendor/zpool/src/main.zig
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
pub const Handle = @import("handle.zig").Handle;
|
||||
pub const Pool = @import("pool.zig").Pool;
|
||||
|
||||
// ensure transitive closure of test coverage
|
||||
comptime {
|
||||
_ = Handle;
|
||||
_ = Pool;
|
||||
}
|
||||
1430
vendor/zpool/src/pool.zig
vendored
Normal file
1430
vendor/zpool/src/pool.zig
vendored
Normal file
File diff suppressed because it is too large
Load Diff
106
vendor/zpool/src/utils.zig
vendored
Normal file
106
vendor/zpool/src/utils.zig
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
const std = @import("std");
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
pub fn asTypeId(comptime typeInfo: std.builtin.Type) std.builtin.TypeId {
|
||||
return @as(std.builtin.TypeId, typeInfo);
|
||||
}
|
||||
|
||||
pub fn typeIdOf(comptime T: type) std.builtin.TypeId {
|
||||
return asTypeId(@typeInfo(T));
|
||||
}
|
||||
|
||||
pub fn isStruct(comptime T: type) bool {
|
||||
return typeIdOf(T) == std.builtin.TypeId.@"struct";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/// UInt(bits) returns an unsigned integer type of the requested bit width.
|
||||
pub fn UInt(comptime bits: u8) type {
|
||||
const unsigned = std.builtin.Signedness.unsigned;
|
||||
return @Type(.{ .int = .{ .signedness = unsigned, .bits = bits } });
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/// Returns an unsigned integer type with ***at least*** `min_bits`,
|
||||
/// that is also large enough to be addressable by a normal pointer.
|
||||
/// The returned type will always be one of the following:
|
||||
/// * `u8`
|
||||
/// * `u16`
|
||||
/// * `u32`
|
||||
/// * `u64`
|
||||
/// * `u128`
|
||||
/// * `u256`
|
||||
pub fn AddressableUInt(comptime min_bits: u8) type {
|
||||
return switch (min_bits) {
|
||||
0...8 => u8,
|
||||
9...16 => u16,
|
||||
17...32 => u32,
|
||||
33...64 => u64,
|
||||
65...128 => u128,
|
||||
129...255 => u256,
|
||||
};
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/// Given: `Struct = struct { foo: u32, bar: u64 }`
|
||||
/// Returns: `StructOfSlices = struct { foo: []u32, bar: []u64 }`
|
||||
pub fn StructOfSlices(comptime Struct: type) type {
|
||||
const StructField = std.builtin.Type.StructField;
|
||||
|
||||
// same number of fields in the new struct
|
||||
const struct_fields = @typeInfo(Struct).@"struct".fields;
|
||||
|
||||
comptime var struct_of_slices_fields: []const StructField = &.{};
|
||||
inline for (struct_fields) |struct_field| {
|
||||
// u32 -> []u32
|
||||
const element_type = struct_field.type;
|
||||
|
||||
const slice_type_info = std.builtin.Type{
|
||||
.pointer = .{
|
||||
.child = element_type,
|
||||
.alignment = @alignOf(element_type),
|
||||
.size = .slice,
|
||||
.is_const = false,
|
||||
.is_volatile = false,
|
||||
.address_space = .generic,
|
||||
.is_allowzero = false,
|
||||
.sentinel_ptr = null,
|
||||
},
|
||||
};
|
||||
|
||||
const FieldType = @Type(slice_type_info);
|
||||
|
||||
// Struct.foo: u32 -> StructOfSlices.foo : []u32
|
||||
const slice_field = std.builtin.Type.StructField{
|
||||
.name = struct_field.name,
|
||||
.type = FieldType,
|
||||
.default_value_ptr = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(FieldType),
|
||||
};
|
||||
|
||||
// Struct.foo: u32 -> StructOfSlices.foo : []u32
|
||||
struct_of_slices_fields = struct_of_slices_fields ++ [1]StructField{slice_field};
|
||||
}
|
||||
|
||||
return @Type(.{ .@"struct" = .{
|
||||
.layout = .auto,
|
||||
.fields = struct_of_slices_fields,
|
||||
.decls = &.{},
|
||||
.is_tuple = false,
|
||||
} });
|
||||
}
|
||||
|
||||
test "StructOfSlices" {
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
|
||||
const Struct = struct { a: u16, b: u16, c: u16 };
|
||||
try expectEqual(@sizeOf(u16) * 3, @sizeOf(Struct));
|
||||
|
||||
const SOS = StructOfSlices(Struct);
|
||||
try expectEqual(@sizeOf([]u16) * 3, @sizeOf(SOS));
|
||||
}
|
||||
Reference in New Issue
Block a user