examples/gkurve: initial commit for resizable_label
This commit is contained in:
parent
ade26b24b1
commit
98138dd2fa
10 changed files with 4899 additions and 20 deletions
67
examples/gkurve/data_structures/bit_array_list.zig
Normal file
67
examples/gkurve/data_structures/bit_array_list.zig
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
const std = @import("std");
|
||||
|
||||
// std.DynamicBitSet doesn't behave like std.ArrayList, it will realloc on every resize.
|
||||
// For now provide a bitset api and use std.ArrayList(bool) as the implementation.
|
||||
pub const BitArrayList = struct {
|
||||
const Self = @This();
|
||||
|
||||
buf: std.ArrayList(bool),
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
return .{
|
||||
.buf = std.ArrayList(bool).init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.buf.deinit();
|
||||
}
|
||||
|
||||
pub fn clearRetainingCapacity(self: *Self) void {
|
||||
self.buf.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub fn appendUnset(self: *Self) !void {
|
||||
try self.buf.append(false);
|
||||
}
|
||||
|
||||
pub fn appendSet(self: *Self) !void {
|
||||
try self.buf.append(true);
|
||||
}
|
||||
|
||||
pub fn isSet(self: Self, idx: usize) bool {
|
||||
return self.buf.items[idx];
|
||||
}
|
||||
|
||||
pub fn set(self: *Self, idx: usize) void {
|
||||
self.buf.items[idx] = true;
|
||||
}
|
||||
|
||||
pub fn unset(self: *Self, idx: usize) void {
|
||||
self.buf.items[idx] = false;
|
||||
}
|
||||
|
||||
pub fn setRange(self: *Self, start: usize, end: usize) void {
|
||||
std.mem.set(bool, self.buf.items[start..end], true);
|
||||
}
|
||||
|
||||
pub fn unsetRange(self: *Self, start: usize, end: usize) void {
|
||||
std.mem.set(bool, self.buf.items[start..end], false);
|
||||
}
|
||||
|
||||
pub fn resize(self: *Self, size: usize) !void {
|
||||
try self.buf.resize(size);
|
||||
}
|
||||
|
||||
pub fn resizeFillNew(self: *Self, size: usize, comptime fill: bool) !void {
|
||||
const start = self.buf.items.len;
|
||||
try self.resize(size);
|
||||
if (self.buf.items.len > start) {
|
||||
if (fill) {
|
||||
self.setRange(start, self.buf.items.len);
|
||||
} else {
|
||||
self.unsetRange(start, self.buf.items.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
780
examples/gkurve/data_structures/compact.zig
Normal file
780
examples/gkurve/data_structures/compact.zig
Normal file
|
|
@ -0,0 +1,780 @@
|
|||
const std = @import("std");
|
||||
const t = std.testing;
|
||||
const BitArrayList = @import("bit_array_list.zig").BitArrayList;
|
||||
const log = std.log.scoped(.compact);
|
||||
|
||||
/// Useful for keeping elements closer together in memory when you're using a bunch of insert/delete,
|
||||
/// while keeping realloc to a minimum and preserving the element's initial insert index.
|
||||
/// Backed by std.ArrayList.
|
||||
/// Item ids are reused once removed.
|
||||
/// Items are assigned an id and have O(1) access time by id.
|
||||
/// TODO: Iterating can be just as fast as a dense array if CompactIdGenerator kept a sorted list of freed id ranges.
|
||||
/// Although that also means delete ops would need to be O(logn).
|
||||
pub fn CompactUnorderedList(comptime Id: type, comptime T: type) type {
|
||||
if (@typeInfo(Id).Int.signedness != .unsigned) {
|
||||
@compileError("Unsigned id type required.");
|
||||
}
|
||||
return struct {
|
||||
id_gen: CompactIdGenerator(Id),
|
||||
|
||||
// TODO: Rename to buf.
|
||||
data: std.ArrayList(T),
|
||||
|
||||
// Keep track of whether an item exists at id in order to perform iteration.
|
||||
// TODO: Rename to exists.
|
||||
// TODO: Maybe the user should provide this if it's important. It would also simplify the api and remove optional return types. It also means iteration won't be possible.
|
||||
data_exists: BitArrayList,
|
||||
|
||||
const Self = @This();
|
||||
const Iterator = struct {
|
||||
// The current id should reflect the id of the value returned from next or nextPtr.
|
||||
cur_id: Id,
|
||||
list: *const Self,
|
||||
|
||||
fn init(list: *const Self) @This() {
|
||||
return .{
|
||||
.cur_id = std.math.maxInt(Id),
|
||||
.list = list,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn reset(self: *@This()) void {
|
||||
self.idx = std.math.maxInt(Id);
|
||||
}
|
||||
|
||||
pub fn nextPtr(self: *@This()) ?*T {
|
||||
self.cur_id +%= 1;
|
||||
while (true) {
|
||||
if (self.cur_id < self.list.data.items.len) {
|
||||
if (!self.list.data_exists.isSet(self.cur_id)) {
|
||||
self.cur_id += 1;
|
||||
continue;
|
||||
} else {
|
||||
return &self.list.data.items[self.cur_id];
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(self: *@This()) ?T {
|
||||
self.cur_id +%= 1;
|
||||
while (true) {
|
||||
if (self.cur_id < self.list.data.items.len) {
|
||||
if (!self.list.data_exists.isSet(self.cur_id)) {
|
||||
self.cur_id += 1;
|
||||
continue;
|
||||
} else {
|
||||
return self.list.data.items[self.cur_id];
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) @This() {
|
||||
const new = @This(){
|
||||
.id_gen = CompactIdGenerator(Id).init(alloc, 0),
|
||||
.data = std.ArrayList(T).init(alloc),
|
||||
.data_exists = BitArrayList.init(alloc),
|
||||
};
|
||||
return new;
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.id_gen.deinit();
|
||||
self.data.deinit();
|
||||
self.data_exists.deinit();
|
||||
}
|
||||
|
||||
pub fn iterator(self: *const Self) Iterator {
|
||||
return Iterator.init(self);
|
||||
}
|
||||
|
||||
// Returns the id of the item.
|
||||
pub fn add(self: *Self, item: T) !Id {
|
||||
const new_id = self.id_gen.getNextId();
|
||||
errdefer self.id_gen.deleteId(new_id);
|
||||
|
||||
if (new_id >= self.data.items.len) {
|
||||
try self.data.resize(new_id + 1);
|
||||
try self.data_exists.resize(new_id + 1);
|
||||
}
|
||||
self.data.items[new_id] = item;
|
||||
self.data_exists.set(new_id);
|
||||
return new_id;
|
||||
}
|
||||
|
||||
pub fn set(self: *Self, id: Id, item: T) void {
|
||||
self.data.items[id] = item;
|
||||
}
|
||||
|
||||
pub fn remove(self: *Self, id: Id) void {
|
||||
self.data_exists.unset(id);
|
||||
self.id_gen.deleteId(id);
|
||||
}
|
||||
|
||||
pub fn clearRetainingCapacity(self: *Self) void {
|
||||
self.data_exists.clearRetainingCapacity();
|
||||
self.id_gen.clearRetainingCapacity();
|
||||
self.data.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub fn get(self: Self, id: Id) ?T {
|
||||
if (self.has(id)) {
|
||||
return self.data.items[id];
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getNoCheck(self: Self, id: Id) T {
|
||||
return self.data.items[id];
|
||||
}
|
||||
|
||||
pub fn getPtr(self: *const Self, id: Id) ?*T {
|
||||
if (self.has(id)) {
|
||||
return &self.data.items[id];
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getPtrNoCheck(self: Self, id: Id) *T {
|
||||
return &self.data.items[id];
|
||||
}
|
||||
|
||||
pub fn has(self: Self, id: Id) bool {
|
||||
return self.data_exists.isSet(id);
|
||||
}
|
||||
|
||||
pub fn size(self: Self) usize {
|
||||
return self.data.items.len - self.id_gen.next_ids.count;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "CompactUnorderedList" {
|
||||
{
|
||||
// General test.
|
||||
var arr = CompactUnorderedList(u32, u8).init(t.allocator);
|
||||
defer arr.deinit();
|
||||
|
||||
_ = try arr.add(1);
|
||||
const id = try arr.add(2);
|
||||
_ = try arr.add(3);
|
||||
arr.remove(id);
|
||||
// Test adding to a removed slot.
|
||||
_ = try arr.add(4);
|
||||
const id2 = try arr.add(5);
|
||||
// Test iterator skips removed slot.
|
||||
arr.remove(id2);
|
||||
|
||||
var iter = arr.iterator();
|
||||
try t.expectEqual(iter.next(), 1);
|
||||
try t.expectEqual(iter.next(), 4);
|
||||
try t.expectEqual(iter.next(), 3);
|
||||
try t.expectEqual(iter.next(), null);
|
||||
try t.expectEqual(arr.size(), 3);
|
||||
}
|
||||
{
|
||||
// Empty test.
|
||||
var arr = CompactUnorderedList(u32, u8).init(t.allocator);
|
||||
defer arr.deinit();
|
||||
var iter = arr.iterator();
|
||||
try t.expectEqual(iter.next(), null);
|
||||
try t.expectEqual(arr.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffer is a CompactUnorderedList.
|
||||
pub fn CompactSinglyLinkedList(comptime Id: type, comptime T: type) type {
|
||||
const Null = CompactNull(Id);
|
||||
const Node = CompactSinglyLinkedListNode(Id, T);
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
first: Id,
|
||||
nodes: CompactUnorderedList(Id, Node),
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
return .{
|
||||
.first = Null,
|
||||
.nodes = CompactUnorderedList(Id, Node).init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.nodes.deinit();
|
||||
}
|
||||
|
||||
pub fn insertAfter(self: *Self, id: Id, data: T) !Id {
|
||||
if (self.nodes.has(id)) {
|
||||
const new = try self.nodes.add(.{
|
||||
.next = self.nodes.getNoCheck(id).next,
|
||||
.data = data,
|
||||
});
|
||||
self.nodes.getPtrNoCheck(id).next = new;
|
||||
return new;
|
||||
} else return error.NoElement;
|
||||
}
|
||||
|
||||
pub fn removeNext(self: *Self, id: Id) !bool {
|
||||
if (self.nodes.has(id)) {
|
||||
const at = self.nodes.getPtrNoCheck(id);
|
||||
if (at.next != Null) {
|
||||
const next = at.next;
|
||||
at.next = self.nodes.getNoCheck(next).next;
|
||||
self.nodes.remove(next);
|
||||
return true;
|
||||
} else return false;
|
||||
} else return error.NoElement;
|
||||
}
|
||||
|
||||
pub fn getNode(self: *const Self, id: Id) ?Node {
|
||||
return self.nodes.get(id);
|
||||
}
|
||||
|
||||
pub fn getNodeAssumeExists(self: *const Self, id: Id) Node {
|
||||
return self.nodes.getNoCheck(id);
|
||||
}
|
||||
|
||||
pub fn get(self: *const Self, id: Id) ?T {
|
||||
if (self.nodes.has(id)) {
|
||||
return self.nodes.getNoCheck(id).data;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getNoCheck(self: *const Self, id: Id) T {
|
||||
return self.nodes.getNoCheck(id).data;
|
||||
}
|
||||
|
||||
pub fn getAt(self: *const Self, idx: usize) Id {
|
||||
var i: u32 = 0;
|
||||
var cur = self.first.?;
|
||||
while (i != idx) : (i += 1) {
|
||||
cur = self.getNext(cur).?;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
pub fn getFirst(self: *const Self) Id {
|
||||
return self.first;
|
||||
}
|
||||
|
||||
pub fn getNext(self: Self, id: Id) ?Id {
|
||||
if (self.nodes.has(id)) {
|
||||
return self.nodes.getNoCheck(id).next;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn prepend(self: *Self, data: T) !Id {
|
||||
const node = Node{
|
||||
.next = self.first,
|
||||
.data = data,
|
||||
};
|
||||
self.first = try self.nodes.add(node);
|
||||
return self.first;
|
||||
}
|
||||
|
||||
pub fn removeFirst(self: *Self) bool {
|
||||
if (self.first != Null) {
|
||||
const next = self.getNodeAssumeExists(self.first).next;
|
||||
self.nodes.remove(self.first);
|
||||
self.first = next;
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "CompactSinglyLinkedList" {
|
||||
const Null = CompactNull(u32);
|
||||
{
|
||||
// General test.
|
||||
var list = CompactSinglyLinkedList(u32, u8).init(t.allocator);
|
||||
defer list.deinit();
|
||||
|
||||
const first = try list.prepend(1);
|
||||
var last = first;
|
||||
last = try list.insertAfter(last, 2);
|
||||
last = try list.insertAfter(last, 3);
|
||||
// Test remove next.
|
||||
_ = try list.removeNext(first);
|
||||
// Test remove first.
|
||||
_ = list.removeFirst();
|
||||
|
||||
var id = list.getFirst();
|
||||
try t.expectEqual(list.get(id), 3);
|
||||
id = list.getNext(id).?;
|
||||
try t.expectEqual(id, Null);
|
||||
}
|
||||
{
|
||||
// Empty test.
|
||||
var list = CompactSinglyLinkedList(u32, u8).init(t.allocator);
|
||||
defer list.deinit();
|
||||
try t.expectEqual(list.getFirst(), Null);
|
||||
}
|
||||
}
|
||||
|
||||
/// Id should be an unsigned integer type.
|
||||
/// Max value of Id is used to indicate null. (An optional would increase the struct size.)
|
||||
pub fn CompactSinglyLinkedListNode(comptime Id: type, comptime T: type) type {
|
||||
return struct {
|
||||
next: Id,
|
||||
data: T,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn CompactNull(comptime Id: type) Id {
|
||||
return comptime std.math.maxInt(Id);
|
||||
}
|
||||
|
||||
/// Stores multiple linked lists together in memory.
|
||||
pub fn CompactManySinglyLinkedList(comptime ListId: type, comptime Index: type, comptime T: type) type {
|
||||
const Node = CompactSinglyLinkedListNode(Index, T);
|
||||
const Null = CompactNull(Index);
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
const List = struct {
|
||||
head: ?Index,
|
||||
};
|
||||
|
||||
nodes: CompactUnorderedList(Index, Node),
|
||||
lists: CompactUnorderedList(ListId, List),
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
return .{
|
||||
.nodes = CompactUnorderedList(Index, Node).init(alloc),
|
||||
.lists = CompactUnorderedList(ListId, List).init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.nodes.deinit();
|
||||
self.lists.deinit();
|
||||
}
|
||||
|
||||
// Returns detached item.
|
||||
pub fn detachAfter(self: *Self, id: Index) !Index {
|
||||
if (self.nodes.has(id)) {
|
||||
const item = self.getNodePtrAssumeExists(id);
|
||||
const detached = item.next;
|
||||
item.next = Null;
|
||||
return detached;
|
||||
} else return error.NoElement;
|
||||
}
|
||||
|
||||
pub fn insertAfter(self: *Self, id: Index, data: T) !Index {
|
||||
if (self.nodes.has(id)) {
|
||||
const new = try self.nodes.add(.{
|
||||
.next = self.nodes.getNoCheck(id).next,
|
||||
.data = data,
|
||||
});
|
||||
self.nodes.getPtrNoCheck(id).next = new;
|
||||
return new;
|
||||
} else return error.NoElement;
|
||||
}
|
||||
|
||||
pub fn setDetachedToEnd(self: *Self, id: Index, detached_id: Index) void {
|
||||
const item = self.nodes.getPtr(id).?;
|
||||
item.next = detached_id;
|
||||
}
|
||||
|
||||
pub fn addListWithDetachedHead(self: *Self, id: Index) !ListId {
|
||||
return self.lists.add(.{ .head = id });
|
||||
}
|
||||
|
||||
pub fn addListWithHead(self: *Self, data: T) !ListId {
|
||||
const item_id = try self.addDetachedItem(data);
|
||||
return self.addListWithDetachedHead(item_id);
|
||||
}
|
||||
|
||||
pub fn addEmptyList(self: *Self) !ListId {
|
||||
return self.lists.add(.{ .head = Null });
|
||||
}
|
||||
|
||||
pub fn addDetachedItem(self: *Self, data: T) !Index {
|
||||
return try self.nodes.add(.{
|
||||
.next = Null,
|
||||
.data = data,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn prepend(self: *Self, list_id: ListId, data: T) !Index {
|
||||
const list = self.getList(list_id);
|
||||
const item = Node{
|
||||
.next = list.first,
|
||||
.data = data,
|
||||
};
|
||||
list.first = try self.nodes.add(item);
|
||||
return list.first.?;
|
||||
}
|
||||
|
||||
pub fn removeFirst(self: *Self, list_id: ListId) bool {
|
||||
const list = self.getList(list_id);
|
||||
if (list.first == null) {
|
||||
return false;
|
||||
} else {
|
||||
const next = self.getNext(list.first.?);
|
||||
self.nodes.remove(list.first.?);
|
||||
list.first = next;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn removeNext(self: *Self, id: Index) !bool {
|
||||
if (self.nodes.has(id)) {
|
||||
const at = self.nodes.getPtrNoCheck(id);
|
||||
if (at.next != Null) {
|
||||
const next = at.next;
|
||||
at.next = self.nodes.getNoCheck(next).next;
|
||||
self.nodes.remove(next);
|
||||
return true;
|
||||
} else return false;
|
||||
} else return error.NoElement;
|
||||
}
|
||||
|
||||
pub fn removeDetached(self: *Self, id: Index) void {
|
||||
self.nodes.remove(id);
|
||||
}
|
||||
|
||||
pub fn getListPtr(self: *const Self, id: ListId) *List {
|
||||
return self.lists.getPtr(id);
|
||||
}
|
||||
|
||||
pub fn getListHead(self: *const Self, id: ListId) ?Index {
|
||||
if (self.lists.has(id)) {
|
||||
return self.lists.getNoCheck(id).head;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn findInList(self: Self, list_id: ListId, ctx: anytype, pred: fn (ctx: @TypeOf(ctx), buf: Self, item_id: Index) bool) ?Index {
|
||||
var id = self.getListHead(list_id) orelse return null;
|
||||
while (id != Null) {
|
||||
if (pred(ctx, self, id)) {
|
||||
return id;
|
||||
}
|
||||
id = self.getNextIdNoCheck(id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn has(self: Self, id: Index) bool {
|
||||
return self.nodes.has(id);
|
||||
}
|
||||
|
||||
pub fn getNode(self: Self, id: Index) ?Node {
|
||||
return self.nodes.get(id);
|
||||
}
|
||||
|
||||
pub fn getNodeAssumeExists(self: Self, id: Index) Node {
|
||||
return self.nodes.getNoCheck(id);
|
||||
}
|
||||
|
||||
pub fn getNodePtr(self: Self, id: Index) ?*Node {
|
||||
return self.nodes.getPtr(id);
|
||||
}
|
||||
|
||||
pub fn getNodePtrAssumeExists(self: Self, id: Index) *Node {
|
||||
return self.nodes.getPtrNoCheck(id);
|
||||
}
|
||||
|
||||
pub fn get(self: Self, id: Index) ?T {
|
||||
if (self.nodes.has(id)) {
|
||||
return self.nodes.getNoCheck(id).data;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getNoCheck(self: Self, id: Index) T {
|
||||
return self.nodes.getNoCheck(id).data;
|
||||
}
|
||||
|
||||
pub fn getIdAt(self: Self, list_id: ListId, idx: usize) Index {
|
||||
var i: u32 = 0;
|
||||
var cur: Index = self.getListHead(list_id).?;
|
||||
while (i != idx) : (i += 1) {
|
||||
cur = self.getNextId(cur).?;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
pub fn getPtr(self: Self, id: Index) ?*T {
|
||||
if (self.nodes.has(id)) {
|
||||
return &self.nodes.getPtrNoCheck(id).data;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getPtrNoCheck(self: Self, id: Index) *T {
|
||||
return &self.nodes.getPtrNoCheck(id).data;
|
||||
}
|
||||
|
||||
pub fn getNextId(self: Self, id: Index) ?Index {
|
||||
if (self.nodes.get(id)) |node| {
|
||||
return node.next;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getNextIdNoCheck(self: Self, id: Index) Index {
|
||||
return self.nodes.getNoCheck(id).next;
|
||||
}
|
||||
|
||||
pub fn getNextNode(self: Self, id: Index) ?Node {
|
||||
if (self.getNext(id)) |next| {
|
||||
return self.getNode(next);
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getNextData(self: *const Self, id: Index) ?T {
|
||||
if (self.getNext(id)) |next| {
|
||||
return self.get(next);
|
||||
} else return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "CompactManySinglyLinkedList" {
|
||||
const Null = CompactNull(u32);
|
||||
var lists = CompactManySinglyLinkedList(u32, u32, u32).init(t.allocator);
|
||||
defer lists.deinit();
|
||||
|
||||
const list_id = try lists.addListWithHead(10);
|
||||
const head = lists.getListHead(list_id).?;
|
||||
|
||||
// Test detachAfter.
|
||||
const after = try lists.insertAfter(head, 20);
|
||||
try t.expectEqual(lists.getNextIdNoCheck(head), after);
|
||||
try t.expectEqual(lists.detachAfter(head), after);
|
||||
try t.expectEqual(lists.getNextIdNoCheck(head), Null);
|
||||
}
|
||||
|
||||
/// Reuses deleted ids.
|
||||
/// Uses a fifo id buffer to get the next id if not empty, otherwise it uses the next id counter.
|
||||
pub fn CompactIdGenerator(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
start_id: T,
|
||||
next_default_id: T,
|
||||
next_ids: std.fifo.LinearFifo(T, .Dynamic),
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator, start_id: T) Self {
|
||||
return .{
|
||||
.start_id = start_id,
|
||||
.next_default_id = start_id,
|
||||
.next_ids = std.fifo.LinearFifo(T, .Dynamic).init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn peekNextId(self: Self) T {
|
||||
if (self.next_ids.readableLength() == 0) {
|
||||
return self.next_default_id;
|
||||
} else {
|
||||
return self.next_ids.peekItem(0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getNextId(self: *Self) T {
|
||||
if (self.next_ids.readableLength() == 0) {
|
||||
defer self.next_default_id += 1;
|
||||
return self.next_default_id;
|
||||
} else {
|
||||
return self.next_ids.readItem().?;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clearRetainingCapacity(self: *Self) void {
|
||||
self.next_default_id = self.start_id;
|
||||
self.next_ids.head = 0;
|
||||
self.next_ids.count = 0;
|
||||
}
|
||||
|
||||
pub fn deleteId(self: *Self, id: T) void {
|
||||
self.next_ids.writeItem(id) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.next_ids.deinit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "CompactIdGenerator" {
|
||||
var gen = CompactIdGenerator(u16).init(t.allocator, 1);
|
||||
defer gen.deinit();
|
||||
try t.expectEqual(gen.getNextId(), 1);
|
||||
try t.expectEqual(gen.getNextId(), 2);
|
||||
gen.deleteId(1);
|
||||
try t.expectEqual(gen.getNextId(), 1);
|
||||
try t.expectEqual(gen.getNextId(), 3);
|
||||
}
|
||||
|
||||
/// Holds linked lists in a compact buffer. Does not keep track of list heads.
|
||||
/// This might replace CompactManySinglyLinkedList.
|
||||
pub fn CompactSinglyLinkedListBuffer(comptime Id: type, comptime T: type) type {
|
||||
const Null = comptime CompactNull(Id);
|
||||
const OptId = Id;
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
pub const Node = CompactSinglyLinkedListNode(Id, T);
|
||||
|
||||
nodes: CompactUnorderedList(Id, Node),
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
return .{
|
||||
.nodes = CompactUnorderedList(Id, Node).init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.nodes.deinit();
|
||||
}
|
||||
|
||||
pub fn clearRetainingCapacity(self: *Self) void {
|
||||
self.nodes.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub fn getNode(self: Self, idx: Id) ?Node {
|
||||
return self.nodes.get(idx);
|
||||
}
|
||||
|
||||
pub fn getNodeNoCheck(self: Self, idx: Id) Node {
|
||||
return self.nodes.getNoCheck(idx);
|
||||
}
|
||||
|
||||
pub fn getNodePtrNoCheck(self: Self, idx: Id) *Node {
|
||||
return self.nodes.getPtrNoCheck(idx);
|
||||
}
|
||||
|
||||
pub fn iterator(self: Self) CompactUnorderedList(Id, Node).Iterator {
|
||||
return self.nodes.iterator();
|
||||
}
|
||||
|
||||
pub fn iterFirstNoCheck(self: Self) Id {
|
||||
var iter = self.nodes.iterator();
|
||||
_ = iter.next();
|
||||
return iter.cur_id;
|
||||
}
|
||||
|
||||
pub fn iterFirstValueNoCheck(self: Self) T {
|
||||
var iter = self.nodes.iterator();
|
||||
return iter.next().?.data;
|
||||
}
|
||||
|
||||
pub fn size(self: Self) usize {
|
||||
return self.nodes.size();
|
||||
}
|
||||
|
||||
pub fn getLast(self: Self, id: Id) ?Id {
|
||||
if (id == Null) {
|
||||
return null;
|
||||
}
|
||||
if (self.nodes.has(id)) {
|
||||
var cur = id;
|
||||
while (cur != Null) {
|
||||
const next = self.getNextNoCheck(cur);
|
||||
if (next == Null) {
|
||||
return cur;
|
||||
}
|
||||
cur = next;
|
||||
}
|
||||
unreachable;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn get(self: Self, id: Id) ?T {
|
||||
if (self.nodes.has(id)) {
|
||||
return self.nodes.getNoCheck(id).data;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getNoCheck(self: Self, idx: Id) T {
|
||||
return self.nodes.getNoCheck(idx).data;
|
||||
}
|
||||
|
||||
pub fn getPtrNoCheck(self: Self, idx: Id) *T {
|
||||
return &self.nodes.getPtrNoCheck(idx).data;
|
||||
}
|
||||
|
||||
pub fn getNextNoCheck(self: Self, id: Id) OptId {
|
||||
return self.nodes.getNoCheck(id).next;
|
||||
}
|
||||
|
||||
pub fn getNext(self: Self, id: Id) ?OptId {
|
||||
if (self.nodes.has(id)) {
|
||||
return self.nodes.getNoCheck(id).next;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
/// Adds a new head node.
|
||||
pub fn add(self: *Self, data: T) !Id {
|
||||
return try self.nodes.add(.{
|
||||
.next = Null,
|
||||
.data = data,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn insertBeforeHead(self: *Self, head_id: Id, data: T) !Id {
|
||||
if (self.nodes.has(head_id)) {
|
||||
return try self.nodes.add(.{
|
||||
.next = head_id,
|
||||
.data = data,
|
||||
});
|
||||
} else return error.NoElement;
|
||||
}
|
||||
|
||||
pub fn insertBeforeHeadNoCheck(self: *Self, head_id: Id, data: T) !Id {
|
||||
return try self.nodes.add(.{
|
||||
.next = head_id,
|
||||
.data = data,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn insertAfter(self: *Self, id: Id, data: T) !Id {
|
||||
if (self.nodes.has(id)) {
|
||||
const new = try self.nodes.add(.{
|
||||
.next = self.nodes.getNoCheck(id).next,
|
||||
.data = data,
|
||||
});
|
||||
self.nodes.getPtrNoCheck(id).next = new;
|
||||
return new;
|
||||
} else return error.NoElement;
|
||||
}
|
||||
|
||||
pub fn removeAfter(self: *Self, id: Id) !void {
|
||||
if (self.nodes.has(id)) {
|
||||
const next = self.getNextNoCheck(id);
|
||||
if (next != Null) {
|
||||
const next_next = self.getNextNoCheck(next);
|
||||
self.nodes.getNoCheck(id).next = next_next;
|
||||
self.nodes.remove(next);
|
||||
}
|
||||
} else return error.NoElement;
|
||||
}
|
||||
|
||||
pub fn removeAssumeNoPrev(self: *Self, id: Id) !void {
|
||||
if (self.nodes.has(id)) {
|
||||
self.nodes.remove(id);
|
||||
} else return error.NoElement;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "CompactSinglyLinkedListBuffer" {
|
||||
var buf = CompactSinglyLinkedListBuffer(u32, u32).init(t.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
const head = try buf.add(1);
|
||||
try t.expectEqual(buf.get(head).?, 1);
|
||||
try t.expectEqual(buf.getNoCheck(head), 1);
|
||||
try t.expectEqual(buf.getNode(head).?.data, 1);
|
||||
try t.expectEqual(buf.getNodeNoCheck(head).data, 1);
|
||||
|
||||
const second = try buf.insertAfter(head, 2);
|
||||
try t.expectEqual(buf.getNodeNoCheck(head).next, second);
|
||||
try t.expectEqual(buf.getNoCheck(second), 2);
|
||||
|
||||
try buf.removeAssumeNoPrev(head);
|
||||
try t.expectEqual(buf.get(head), null);
|
||||
}
|
||||
1390
examples/gkurve/data_structures/rb_tree.zig
Normal file
1390
examples/gkurve/data_structures/rb_tree.zig
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue