mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 03:06:55 -04:00
Add a SparseSet implementation (#1414)
This commit is contained in:
parent
a29bb3c3b9
commit
2f48161712
160
src/utils.zig
160
src/utils.zig
@ -1907,6 +1907,166 @@ test "read/write mixed" {
|
||||
try std.testing.expect(reader.remaining.len == 0);
|
||||
}
|
||||
|
||||
pub fn DenseId(comptime IdType: type) type {
|
||||
std.debug.assert(@typeInfo(IdType) == .int);
|
||||
std.debug.assert(@typeInfo(IdType).int.signedness == .unsigned);
|
||||
|
||||
return enum(IdType) {
|
||||
noValue = std.math.maxInt(IdType),
|
||||
_,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn SparseSet(comptime T: type, comptime IdType: type) type { // MARK: SparseSet
|
||||
std.debug.assert(@intFromEnum(IdType.noValue) == std.math.maxInt(@typeInfo(IdType).@"enum".tag_type));
|
||||
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
dense: main.ListUnmanaged(T) = .{},
|
||||
denseToSparseIndex: main.ListUnmanaged(IdType) = .{},
|
||||
sparseToDenseIndex: main.ListUnmanaged(IdType) = .{},
|
||||
|
||||
pub fn deinit(self: *Self, allocator: NeverFailingAllocator) void {
|
||||
self.dense.deinit(allocator);
|
||||
self.denseToSparseIndex.deinit(allocator);
|
||||
self.sparseToDenseIndex.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn contains(self: *Self, id: IdType) bool {
|
||||
return @intFromEnum(id) < self.sparseToDenseIndex.items.len and self.sparseToDenseIndex.items[@intFromEnum(id)] != .noValue;
|
||||
}
|
||||
|
||||
pub fn set(self: *Self, allocator: NeverFailingAllocator, id: IdType, value: T) void {
|
||||
std.debug.assert(id != .noValue);
|
||||
|
||||
const denseId: IdType = @enumFromInt(self.dense.items.len);
|
||||
|
||||
if(@intFromEnum(id) >= self.sparseToDenseIndex.items.len) {
|
||||
self.sparseToDenseIndex.appendNTimes(allocator, .noValue, @intFromEnum(id) - self.sparseToDenseIndex.items.len + 1);
|
||||
}
|
||||
|
||||
std.debug.assert(self.sparseToDenseIndex.items[@intFromEnum(id)] == .noValue);
|
||||
|
||||
self.sparseToDenseIndex.items[@intFromEnum(id)] = denseId;
|
||||
self.dense.append(allocator, value);
|
||||
self.denseToSparseIndex.append(allocator, id);
|
||||
}
|
||||
|
||||
pub fn remove(self: *Self, id: IdType) !void {
|
||||
if(!self.contains(id)) return error.ElementNotFound;
|
||||
|
||||
const denseId = @intFromEnum(self.sparseToDenseIndex.items[@intFromEnum(id)]);
|
||||
self.sparseToDenseIndex.items[@intFromEnum(id)] = .noValue;
|
||||
|
||||
_ = self.dense.swapRemove(denseId);
|
||||
_ = self.denseToSparseIndex.swapRemove(denseId);
|
||||
|
||||
if(denseId != self.dense.items.len) {
|
||||
self.sparseToDenseIndex.items[@intFromEnum(self.denseToSparseIndex.items[denseId])] = @enumFromInt(denseId);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self: *Self, id: IdType) ?*T {
|
||||
if(@intFromEnum(id) >= self.sparseToDenseIndex.items.len) return null;
|
||||
const index = self.sparseToDenseIndex.items[@intFromEnum(id)];
|
||||
if(index == .noValue) return null;
|
||||
return &self.dense.items[@intFromEnum(index)];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "SparseSet/set at zero" {
|
||||
const IdType = DenseId(u32);
|
||||
var set: SparseSet(u32, IdType) = .{};
|
||||
defer set.deinit(main.heap.testingAllocator);
|
||||
|
||||
const index: IdType = @enumFromInt(0);
|
||||
|
||||
set.set(main.heap.testingAllocator, index, 5);
|
||||
try std.testing.expectEqual(set.get(index).?.*, 5);
|
||||
}
|
||||
|
||||
test "SparseSet/set at 100" {
|
||||
const IdType = DenseId(u32);
|
||||
var set: SparseSet(u32, IdType) = .{};
|
||||
defer set.deinit(main.heap.testingAllocator);
|
||||
|
||||
const index: IdType = @enumFromInt(100);
|
||||
|
||||
set.set(main.heap.testingAllocator, index, 5);
|
||||
try std.testing.expectEqual(set.get(index).?.*, 5);
|
||||
}
|
||||
|
||||
test "SparseSet/remove first" {
|
||||
const IdType = DenseId(u32);
|
||||
var set: SparseSet(u32, IdType) = .{};
|
||||
defer set.deinit(main.heap.testingAllocator);
|
||||
|
||||
const expectSecond: u32 = 100;
|
||||
|
||||
const firstId: IdType = @enumFromInt(0);
|
||||
const secondId: IdType = @enumFromInt(1);
|
||||
|
||||
set.set(main.heap.testingAllocator, firstId, 5);
|
||||
set.set(main.heap.testingAllocator, secondId, expectSecond);
|
||||
|
||||
try set.remove(firstId);
|
||||
|
||||
try std.testing.expectEqual(set.get(secondId).?.*, expectSecond);
|
||||
}
|
||||
|
||||
test "SparseSet/remove last" {
|
||||
const IdType = DenseId(u32);
|
||||
var set: SparseSet(u32, IdType) = .{};
|
||||
defer set.deinit(main.heap.testingAllocator);
|
||||
|
||||
set.set(main.heap.testingAllocator, @enumFromInt(0), 5);
|
||||
|
||||
try set.remove(@enumFromInt(0));
|
||||
}
|
||||
|
||||
test "SparseSet/remove entry that doesn't exist" {
|
||||
const IdType = DenseId(u32);
|
||||
var set: SparseSet(u32, IdType) = .{};
|
||||
defer set.deinit(main.heap.testingAllocator);
|
||||
|
||||
try std.testing.expectError(error.ElementNotFound, set.remove(@enumFromInt(0)));
|
||||
}
|
||||
|
||||
test "SparseSet/remove entry twice" {
|
||||
const IdType = DenseId(u32);
|
||||
var set: SparseSet(u32, IdType) = .{};
|
||||
defer set.deinit(main.heap.testingAllocator);
|
||||
|
||||
set.set(main.heap.testingAllocator, @enumFromInt(0), 5);
|
||||
|
||||
try set.remove(@enumFromInt(0));
|
||||
try std.testing.expectError(error.ElementNotFound, set.remove(@enumFromInt(0)));
|
||||
}
|
||||
|
||||
test "SparseSet/reusing" {
|
||||
const IdType = DenseId(u32);
|
||||
var set: SparseSet(u32, IdType) = .{};
|
||||
defer set.deinit(main.heap.testingAllocator);
|
||||
|
||||
const expectSecond = 100;
|
||||
const expectNew = 10;
|
||||
|
||||
const firstId: IdType = @enumFromInt(0);
|
||||
const secondId: IdType = @enumFromInt(1);
|
||||
|
||||
set.set(main.heap.testingAllocator, firstId, 5);
|
||||
set.set(main.heap.testingAllocator, secondId, expectSecond);
|
||||
|
||||
try set.remove(firstId);
|
||||
|
||||
set.set(main.heap.testingAllocator, firstId, expectNew);
|
||||
|
||||
try std.testing.expectEqual(set.get(secondId).?.*, expectSecond);
|
||||
try std.testing.expectEqual(set.get(firstId).?.*, expectNew);
|
||||
}
|
||||
|
||||
// MARK: functionPtrCast()
|
||||
fn CastFunctionSelfToAnyopaqueType(Fn: type) type {
|
||||
var typeInfo = @typeInfo(Fn);
|
||||
|
@ -3,6 +3,9 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const main = @import("main");
|
||||
|
||||
var testingErrorHandlingAllocator = ErrorHandlingAllocator.init(std.testing.allocator);
|
||||
pub const testingAllocator = testingErrorHandlingAllocator.allocator();
|
||||
|
||||
/// Allows for stack-like allocations in a fast and safe way.
|
||||
/// It is safe in the sense that a regular allocator will be used when the buffer is full.
|
||||
pub const StackAllocator = struct { // MARK: StackAllocator
|
||||
|
Loading…
x
Reference in New Issue
Block a user