mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-05 12:17:53 -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);
|
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()
|
// MARK: functionPtrCast()
|
||||||
fn CastFunctionSelfToAnyopaqueType(Fn: type) type {
|
fn CastFunctionSelfToAnyopaqueType(Fn: type) type {
|
||||||
var typeInfo = @typeInfo(Fn);
|
var typeInfo = @typeInfo(Fn);
|
||||||
|
@ -3,6 +3,9 @@ const Allocator = std.mem.Allocator;
|
|||||||
|
|
||||||
const main = @import("main");
|
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.
|
/// 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.
|
/// It is safe in the sense that a regular allocator will be used when the buffer is full.
|
||||||
pub const StackAllocator = struct { // MARK: StackAllocator
|
pub const StackAllocator = struct { // MARK: StackAllocator
|
||||||
|
Loading…
x
Reference in New Issue
Block a user