2020-05-31 21:28:29 -07:00
|
|
|
const std = @import("std");
|
|
|
|
const warn = std.debug.warn;
|
2020-06-11 22:28:29 -07:00
|
|
|
const utils = @import("utils.zig");
|
2020-06-10 13:10:56 -07:00
|
|
|
const ReverseSliceIterator = @import("utils.zig").ReverseSliceIterator;
|
2020-05-31 21:28:29 -07:00
|
|
|
|
|
|
|
// TODO: fix entity_mask. it should come from EntityTraitsDefinition.
|
2020-06-04 19:19:29 -07:00
|
|
|
pub fn SparseSet(comptime SparseT: type) type {
|
2020-05-31 21:28:29 -07:00
|
|
|
return struct {
|
|
|
|
const Self = @This();
|
2020-06-12 23:37:24 -07:00
|
|
|
const page_size: usize = 32768;
|
|
|
|
const entity_per_page = page_size / @sizeOf(SparseT);
|
2020-05-31 21:28:29 -07:00
|
|
|
|
2020-06-12 23:37:24 -07:00
|
|
|
sparse: std.ArrayList(?[]SparseT),
|
2020-05-31 21:28:29 -07:00
|
|
|
dense: std.ArrayList(SparseT),
|
|
|
|
entity_mask: SparseT,
|
2020-06-02 19:55:19 -07:00
|
|
|
allocator: ?*std.mem.Allocator,
|
2020-05-31 21:28:29 -07:00
|
|
|
|
2020-06-02 19:55:19 -07:00
|
|
|
pub fn initPtr(allocator: *std.mem.Allocator) *Self {
|
2020-05-31 21:28:29 -07:00
|
|
|
var set = allocator.create(Self) catch unreachable;
|
2020-06-12 23:37:24 -07:00
|
|
|
set.sparse = std.ArrayList(?[]SparseT).init(allocator);
|
2020-05-31 21:28:29 -07:00
|
|
|
set.dense = std.ArrayList(SparseT).init(allocator);
|
|
|
|
set.entity_mask = std.math.maxInt(SparseT);
|
|
|
|
set.allocator = allocator;
|
|
|
|
return set;
|
|
|
|
}
|
|
|
|
|
2020-06-02 19:55:19 -07:00
|
|
|
pub fn init(allocator: *std.mem.Allocator) Self {
|
|
|
|
return Self{
|
2020-06-12 23:37:24 -07:00
|
|
|
.sparse = std.ArrayList(?[]SparseT).init(allocator),
|
2020-06-02 19:55:19 -07:00
|
|
|
.dense = std.ArrayList(SparseT).init(allocator),
|
|
|
|
.entity_mask = std.math.maxInt(SparseT),
|
|
|
|
.allocator = null,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-05-31 21:28:29 -07:00
|
|
|
pub fn deinit(self: *Self) void {
|
2020-06-12 23:37:24 -07:00
|
|
|
self.sparse.expandToCapacity();
|
2021-06-29 22:43:23 -06:00
|
|
|
for (self.sparse.items) |array| {
|
2020-06-12 23:37:24 -07:00
|
|
|
if (array) |arr| {
|
|
|
|
self.sparse.allocator.free(arr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-31 21:28:29 -07:00
|
|
|
self.dense.deinit();
|
|
|
|
self.sparse.deinit();
|
2020-06-02 19:55:19 -07:00
|
|
|
|
2020-06-12 23:37:24 -07:00
|
|
|
if (self.allocator) |allocator| {
|
2020-06-02 19:55:19 -07:00
|
|
|
allocator.destroy(self);
|
2020-06-12 23:37:24 -07:00
|
|
|
}
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-11 22:28:29 -07:00
|
|
|
pub fn page(self: Self, sparse: SparseT) usize {
|
2020-06-12 23:37:24 -07:00
|
|
|
return (sparse & self.entity_mask) / entity_per_page;
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
2021-06-29 22:43:23 -06:00
|
|
|
fn offset(_: Self, sparse: SparseT) usize {
|
2020-06-12 23:37:24 -07:00
|
|
|
return sparse & (entity_per_page - 1);
|
2020-06-03 20:13:16 -07:00
|
|
|
}
|
|
|
|
|
2020-06-04 19:19:29 -07:00
|
|
|
fn assure(self: *Self, pos: usize) []SparseT {
|
2020-06-12 23:37:24 -07:00
|
|
|
if (pos >= self.sparse.items.len) {
|
2020-06-16 13:19:41 -07:00
|
|
|
const start_pos = self.sparse.items.len;
|
2020-06-12 23:37:24 -07:00
|
|
|
self.sparse.resize(pos + 1) catch unreachable;
|
2020-05-31 21:28:29 -07:00
|
|
|
self.sparse.expandToCapacity();
|
2020-06-16 13:19:41 -07:00
|
|
|
std.mem.set(?[]SparseT, self.sparse.items[start_pos..], null);
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-12 23:37:24 -07:00
|
|
|
if (self.sparse.items[pos]) |arr| {
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
var new_page = self.sparse.allocator.alloc(SparseT, entity_per_page) catch unreachable;
|
|
|
|
std.mem.set(SparseT, new_page, std.math.maxInt(SparseT));
|
|
|
|
self.sparse.items[pos] = new_page;
|
|
|
|
|
|
|
|
return self.sparse.items[pos].?;
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-03 15:01:16 -07:00
|
|
|
/// Increases the capacity of a sparse sets index array
|
2020-05-31 21:28:29 -07:00
|
|
|
pub fn reserve(self: *Self, cap: usize) void {
|
2020-06-03 15:01:16 -07:00
|
|
|
self.sparse.resize(cap) catch unreachable;
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-03 15:01:16 -07:00
|
|
|
/// Returns the number of dense elements that a sparse set has currently allocated space for
|
2020-05-31 21:28:29 -07:00
|
|
|
pub fn capacity(self: *Self) usize {
|
|
|
|
return self.dense.capacity;
|
|
|
|
}
|
|
|
|
|
2020-06-03 15:01:16 -07:00
|
|
|
/// Returns the number of dense elements in a sparse set
|
|
|
|
pub fn len(self: Self) usize {
|
2020-05-31 21:28:29 -07:00
|
|
|
return self.dense.items.len;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn empty(self: *Self) bool {
|
|
|
|
return self.dense.items.len == 0;
|
|
|
|
}
|
|
|
|
|
2020-06-09 10:21:42 -07:00
|
|
|
pub fn data(self: Self) []const SparseT {
|
|
|
|
return self.dense.items;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dataPtr(self: Self) *const []SparseT {
|
2020-05-31 21:28:29 -07:00
|
|
|
return &self.dense.items;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn contains(self: Self, sparse: SparseT) bool {
|
|
|
|
const curr = self.page(sparse);
|
2020-06-12 23:37:24 -07:00
|
|
|
return curr < self.sparse.items.len and self.sparse.items[curr] != null and self.sparse.items[curr].?[self.offset(sparse)] != std.math.maxInt(SparseT);
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the position of an entity in a sparse set
|
2020-06-04 19:19:29 -07:00
|
|
|
pub fn index(self: Self, sparse: SparseT) SparseT {
|
2020-05-31 21:28:29 -07:00
|
|
|
std.debug.assert(self.contains(sparse));
|
2020-06-12 23:37:24 -07:00
|
|
|
return self.sparse.items[self.page(sparse)].?[self.offset(sparse)];
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Assigns an entity to a sparse set
|
|
|
|
pub fn add(self: *Self, sparse: SparseT) void {
|
|
|
|
std.debug.assert(!self.contains(sparse));
|
|
|
|
|
|
|
|
// assure(page(entt))[offset(entt)] = packed.size()
|
2020-06-04 19:19:29 -07:00
|
|
|
self.assure(self.page(sparse))[self.offset(sparse)] = @intCast(SparseT, self.dense.items.len);
|
2020-05-31 21:28:29 -07:00
|
|
|
_ = self.dense.append(sparse) catch unreachable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Removes an entity from a sparse set
|
|
|
|
pub fn remove(self: *Self, sparse: SparseT) void {
|
|
|
|
std.debug.assert(self.contains(sparse));
|
|
|
|
|
|
|
|
const curr = self.page(sparse);
|
|
|
|
const pos = self.offset(sparse);
|
|
|
|
const last_dense = self.dense.items[self.dense.items.len - 1];
|
|
|
|
|
2020-06-12 23:37:24 -07:00
|
|
|
self.dense.items[self.sparse.items[curr].?[pos]] = last_dense;
|
|
|
|
self.sparse.items[self.page(last_dense)].?[self.offset(last_dense)] = self.sparse.items[curr].?[pos];
|
|
|
|
self.sparse.items[curr].?[pos] = std.math.maxInt(SparseT);
|
2020-05-31 21:28:29 -07:00
|
|
|
|
|
|
|
_ = self.dense.pop();
|
|
|
|
}
|
|
|
|
|
2020-06-03 20:13:16 -07:00
|
|
|
/// Swaps two entities in the internal packed and sparse arrays
|
2020-06-12 23:37:24 -07:00
|
|
|
pub fn swap(self: *Self, lhs: SparseT, rhs: SparseT) void {
|
|
|
|
var from = &self.sparse.items[self.page(lhs)].?[self.offset(lhs)];
|
|
|
|
var to = &self.sparse.items[self.page(rhs)].?[self.offset(rhs)];
|
2020-05-31 21:28:29 -07:00
|
|
|
|
|
|
|
std.mem.swap(SparseT, &self.dense.items[from.*], &self.dense.items[to.*]);
|
2020-06-04 19:19:29 -07:00
|
|
|
std.mem.swap(SparseT, from, to);
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Sort elements according to the given comparison function
|
2020-07-20 09:53:15 -07:00
|
|
|
pub fn sort(self: *Self, context: anytype, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool) void {
|
2020-06-12 18:15:47 -07:00
|
|
|
std.sort.insertionSort(SparseT, self.dense.items, context, lessThan);
|
2020-06-11 22:28:29 -07:00
|
|
|
|
2021-06-29 22:43:23 -06:00
|
|
|
for (self.dense.items) |_, i| {
|
2020-06-12 23:37:24 -07:00
|
|
|
const item = @intCast(SparseT, i);
|
|
|
|
self.sparse.items[self.page(self.dense.items[self.page(item)])].?[self.offset(self.dense.items[self.page(item)])] = @intCast(SparseT, i);
|
2020-06-11 22:28:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 14:53:14 -07:00
|
|
|
/// Sort elements according to the given comparison function. Use this when a data array needs to stay in sync with the SparseSet
|
|
|
|
/// by passing in a "swap_context" that contains a "swap" method with a sig of fn(ctx,SparseT,SparseT)void
|
2020-07-20 09:53:15 -07:00
|
|
|
pub fn arrange(self: *Self, length: usize, context: anytype, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool, swap_context: anytype) void {
|
2020-06-12 23:37:24 -07:00
|
|
|
std.sort.insertionSort(SparseT, self.dense.items[0..length], context, lessThan);
|
2020-06-12 20:05:55 -07:00
|
|
|
|
2021-06-29 22:43:23 -06:00
|
|
|
for (self.dense.items[0..length]) |_, pos| {
|
2020-06-12 20:05:55 -07:00
|
|
|
var curr = @intCast(SparseT, pos);
|
|
|
|
var next = self.index(self.dense.items[curr]);
|
|
|
|
|
|
|
|
while (curr != next) {
|
|
|
|
swap_context.swap(self.dense.items[curr], self.dense.items[next]);
|
2020-06-12 23:37:24 -07:00
|
|
|
self.sparse.items[self.page(self.dense.items[curr])].?[self.offset(self.dense.items[curr])] = curr;
|
2020-06-12 20:05:55 -07:00
|
|
|
|
|
|
|
curr = next;
|
|
|
|
next = self.index(self.dense.items[curr]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-03 20:13:16 -07:00
|
|
|
/// Sort entities according to their order in another sparse set. Other is the master in this case.
|
2020-05-31 21:28:29 -07:00
|
|
|
pub fn respect(self: *Self, other: *Self) void {
|
2020-06-04 19:19:29 -07:00
|
|
|
var pos = @as(SparseT, 0);
|
|
|
|
var i = @as(SparseT, 0);
|
2020-06-03 20:13:16 -07:00
|
|
|
while (i < other.dense.items.len) : (i += 1) {
|
|
|
|
if (self.contains(other.dense.items[i])) {
|
|
|
|
if (other.dense.items[i] != self.dense.items[pos]) {
|
|
|
|
self.swap(self.dense.items[pos], other.dense.items[i]);
|
|
|
|
}
|
|
|
|
pos += 1;
|
|
|
|
}
|
|
|
|
}
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear(self: *Self) void {
|
2020-06-12 23:41:49 -07:00
|
|
|
self.sparse.expandToCapacity();
|
|
|
|
for (self.sparse.items) |array, i| {
|
|
|
|
if (array) |arr| {
|
|
|
|
self.sparse.allocator.free(arr);
|
|
|
|
self.sparse.items[i] = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-31 21:28:29 -07:00
|
|
|
self.sparse.items.len = 0;
|
|
|
|
self.dense.items.len = 0;
|
|
|
|
}
|
2020-06-10 13:10:56 -07:00
|
|
|
|
|
|
|
pub fn reverseIterator(self: *Self) ReverseSliceIterator(SparseT) {
|
|
|
|
return ReverseSliceIterator(SparseT).init(self.dense.items);
|
|
|
|
}
|
2020-05-31 21:28:29 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-06-03 20:13:16 -07:00
|
|
|
fn printSet(set: *SparseSet(u32, u8)) void {
|
|
|
|
std.debug.warn("\nsparse -----\n", .{});
|
|
|
|
for (set.sparse.items) |sparse| {
|
|
|
|
std.debug.warn("{}\t", .{sparse});
|
|
|
|
}
|
|
|
|
|
|
|
|
std.debug.warn("\ndense -----\n", .{});
|
|
|
|
for (set.dense.items) |dense| {
|
|
|
|
std.debug.warn("{}\t", .{dense});
|
|
|
|
}
|
|
|
|
std.debug.warn("\n\n", .{});
|
|
|
|
}
|
|
|
|
|
2020-05-31 21:28:29 -07:00
|
|
|
test "add/remove/clear" {
|
2020-06-04 19:19:29 -07:00
|
|
|
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
2020-05-31 21:28:29 -07:00
|
|
|
defer set.deinit();
|
|
|
|
|
|
|
|
set.add(4);
|
|
|
|
set.add(3);
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(set.len(), 2);
|
|
|
|
try std.testing.expectEqual(set.index(4), 0);
|
|
|
|
try std.testing.expectEqual(set.index(3), 1);
|
2020-05-31 21:28:29 -07:00
|
|
|
|
|
|
|
set.remove(4);
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(set.len(), 1);
|
2020-05-31 21:28:29 -07:00
|
|
|
|
|
|
|
set.clear();
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(set.len(), 0);
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
test "grow" {
|
2020-06-04 19:19:29 -07:00
|
|
|
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
2020-05-31 21:28:29 -07:00
|
|
|
defer set.deinit();
|
|
|
|
|
|
|
|
var i = @as(usize, std.math.maxInt(u8));
|
|
|
|
while (i > 0) : (i -= 1) {
|
|
|
|
set.add(@intCast(u32, i));
|
|
|
|
}
|
|
|
|
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(set.len(), std.math.maxInt(u8));
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
test "swap" {
|
2020-06-04 19:19:29 -07:00
|
|
|
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
2020-05-31 21:28:29 -07:00
|
|
|
defer set.deinit();
|
|
|
|
|
|
|
|
set.add(4);
|
|
|
|
set.add(3);
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(set.index(4), 0);
|
|
|
|
try std.testing.expectEqual(set.index(3), 1);
|
2020-05-31 21:28:29 -07:00
|
|
|
|
|
|
|
set.swap(4, 3);
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(set.index(3), 0);
|
|
|
|
try std.testing.expectEqual(set.index(4), 1);
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
test "data() synced" {
|
2020-06-04 19:19:29 -07:00
|
|
|
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
2020-05-31 21:28:29 -07:00
|
|
|
defer set.deinit();
|
|
|
|
|
|
|
|
set.add(0);
|
|
|
|
set.add(1);
|
|
|
|
set.add(2);
|
|
|
|
set.add(3);
|
|
|
|
|
|
|
|
var data = set.data();
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(data[1], 1);
|
|
|
|
try std.testing.expectEqual(set.len(), data.len);
|
2020-05-31 21:28:29 -07:00
|
|
|
|
|
|
|
set.remove(0);
|
|
|
|
set.remove(1);
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(set.len(), set.data().len);
|
2020-05-31 21:28:29 -07:00
|
|
|
}
|
2020-06-03 20:13:16 -07:00
|
|
|
|
2020-06-10 13:10:56 -07:00
|
|
|
test "iterate" {
|
|
|
|
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
|
|
|
defer set.deinit();
|
|
|
|
|
|
|
|
set.add(0);
|
|
|
|
set.add(1);
|
|
|
|
set.add(2);
|
|
|
|
set.add(3);
|
|
|
|
|
2020-06-11 22:28:29 -07:00
|
|
|
var i: u32 = @intCast(u32, set.len()) - 1;
|
2020-06-10 13:10:56 -07:00
|
|
|
var iter = set.reverseIterator();
|
|
|
|
while (iter.next()) |entity| {
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(i, entity);
|
2020-06-10 13:10:56 -07:00
|
|
|
if (i > 0) i -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
test "respect 1" {
|
2020-06-04 19:19:29 -07:00
|
|
|
var set1 = SparseSet(u32).initPtr(std.testing.allocator);
|
2020-06-03 20:13:16 -07:00
|
|
|
defer set1.deinit();
|
|
|
|
|
2020-06-04 19:19:29 -07:00
|
|
|
var set2 = SparseSet(u32).initPtr(std.testing.allocator);
|
2020-06-03 20:13:16 -07:00
|
|
|
defer set2.deinit();
|
|
|
|
|
|
|
|
set1.add(3);
|
|
|
|
set1.add(4);
|
|
|
|
set1.add(5);
|
|
|
|
set1.add(6);
|
|
|
|
set1.add(7);
|
|
|
|
|
|
|
|
set2.add(8);
|
|
|
|
set2.add(6);
|
|
|
|
set2.add(4);
|
|
|
|
|
|
|
|
set1.respect(set2);
|
|
|
|
|
2021-06-29 22:26:23 -06:00
|
|
|
try std.testing.expectEqual(set1.dense.items[0], set2.dense.items[1]);
|
|
|
|
try std.testing.expectEqual(set1.dense.items[1], set2.dense.items[2]);
|
2020-06-03 20:13:16 -07:00
|
|
|
}
|
|
|
|
|
2020-06-11 22:28:29 -07:00
|
|
|
const desc_u32 = std.sort.desc(u32);
|
|
|
|
|
2020-06-10 13:10:56 -07:00
|
|
|
test "respect 2" {
|
2020-06-04 19:19:29 -07:00
|
|
|
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
2020-06-03 20:13:16 -07:00
|
|
|
defer set.deinit();
|
|
|
|
|
|
|
|
set.add(5);
|
|
|
|
set.add(2);
|
|
|
|
set.add(4);
|
|
|
|
set.add(1);
|
|
|
|
set.add(3);
|
|
|
|
|
2020-06-12 18:15:47 -07:00
|
|
|
set.sort({}, desc_u32);
|
2020-06-03 20:13:16 -07:00
|
|
|
|
|
|
|
for (set.dense.items) |item, i| {
|
|
|
|
if (i < set.dense.items.len - 1) {
|
|
|
|
std.debug.assert(item > set.dense.items[i + 1]);
|
|
|
|
}
|
|
|
|
}
|
2020-06-11 22:28:29 -07:00
|
|
|
}
|