clean up linked lists

This commit is contained in:
Matthew 2025-12-12 18:53:53 +11:00
parent 91e83eea1d
commit e5f91cae6d
4 changed files with 147 additions and 232 deletions

69
alloc.d
View File

@ -22,22 +22,23 @@ struct Scratch
struct ArenaPool struct ArenaPool
{ {
u8* mem; u8* mem;
u64 pos; u64 pos;
u64 length; u64 length;
ArenaPool* next;
} }
struct Arena struct Arena
{ {
SLList!(ArenaPool) pools; ArenaPool* first, last;
u64 def_size; u64 def_size;
} }
struct TempArena struct TempArena
{ {
Arena* arena; Arena* arena;
Node!(ArenaPool)* start_node; u64 start_pos;
u64 start_pos; ArenaPool* start_pool;
} }
T* T*
@ -160,13 +161,13 @@ BeginTempArena(Arena* arena)
arena: arena, arena: arena,
}; };
auto n = arena.pools.first; auto n = arena.first;
for(;;) for(;;)
{ {
if(n.next == null) if(n.next == null)
{ {
t.start_node = n; t.start_pool = n;
t.start_pos = n.value.pos; t.start_pos = n.pos;
break; break;
} }
@ -180,16 +181,16 @@ void
End(TempArena* t) End(TempArena* t)
{ {
bool resetting = false; bool resetting = false;
for(auto n = t.arena.pools.first; n != null; n = n.next) for(auto n = t.arena.first; n != null; n = n.next)
{ {
if(t.start_node == n) if(t.start_pool == n)
{ {
n.value.pos = t.start_pos; n.pos = t.start_pos;
resetting = true; resetting = true;
} }
else if(resetting) else if(resetting)
{ {
n.value.pos = 0; n.pos = 0;
} }
} }
} }
@ -233,17 +234,17 @@ Alloc(T)(TempArena* t)
void void
AddArenaPool(Arena* arena, u64 size) AddArenaPool(Arena* arena, u64 size)
{ {
u8* mem = Alloc!(u8)(size + Node!(ArenaPool).sizeof).ptr; u8* mem = Alloc!(u8)(size + ArenaPool.sizeof).ptr;
Node!(ArenaPool)* node = cast(Node!(ArenaPool)*)mem; ArenaPool* node = cast(ArenaPool*)mem;
node.value.mem = (cast(u8*)mem) + Node!(ArenaPool).sizeof; node.mem = (cast(u8*)mem) + ArenaPool.sizeof;
node.value.pos = 0; node.pos = 0;
node.value.length = size; node.length = size;
assert(node.value.mem != null, "Unable to allocate memory for arena"); assert(node.mem != null, "Unable to allocate memory for arena");
SLLPushFront(&arena.pools, node, null); SLLPushFront(arena, node, null);
} }
T[] T[]
@ -308,7 +309,7 @@ AllocAlign(Arena* arena, u64 size, u64 alignment)
void* ptr = null; void* ptr = null;
uintptr mem_pos, current, offset; uintptr mem_pos, current, offset;
Node!(ArenaPool)* node = arena.pools.first; ArenaPool* node = arena.first;
while (true) while (true)
{ {
if(node == null) if(node == null)
@ -319,14 +320,14 @@ AllocAlign(Arena* arena, u64 size, u64 alignment)
} }
AddArenaPool(arena, Max(size, arena.def_size)); AddArenaPool(arena, Max(size, arena.def_size));
node = arena.pools.first; node = arena.first;
} }
mem_pos = cast(uintptr)node.value.mem; mem_pos = cast(uintptr)node.mem;
current = mem_pos + node.value.pos; current = mem_pos + node.pos;
offset = AlignPow2(current, alignment) - mem_pos; offset = AlignPow2(current, alignment) - mem_pos;
if(offset+size <= node.value.length) if(offset+size <= node.length)
{ {
break; break;
} }
@ -334,8 +335,8 @@ AllocAlign(Arena* arena, u64 size, u64 alignment)
node = node.next; node = node.next;
} }
ptr = &node.value.mem[offset]; ptr = &node.mem[offset];
node.value.pos = offset+size; node.pos = offset+size;
return ptr; return ptr;
}; };
@ -343,10 +344,10 @@ AllocAlign(Arena* arena, u64 size, u64 alignment)
void void
Reset(Arena* arena) Reset(Arena* arena)
{ {
Node!(ArenaPool)* node = arena.pools.first; ArenaPool* node = arena.first;
while (node != null) while (node != null)
{ {
node.value.pos = 0; node.pos = 0;
node = node.next; node = node.next;
} }
} }
@ -354,11 +355,13 @@ Reset(Arena* arena)
void void
Free(Arena* arena) Free(Arena* arena)
{ {
Node!(ArenaPool)* node = arena.pools.first; ArenaPool* node = arena.first;
ArenaPool* next;
while (node != null) while (node != null)
{ {
next = node.next;
Free(node); Free(node);
node = node.next; node = next;
} }
} }

View File

@ -153,13 +153,14 @@ alias MD = Modifier;
struct InputEvent struct InputEvent
{ {
Input key; Input key;
Modifier md; Modifier md;
bool pressed; bool pressed;
i32 x; i32 x;
i32 y; i32 y;
i32 rel_x; i32 rel_x;
i32 rel_y; i32 rel_y;
InputEvent* next, prev;
} }
enum ClipboardMode enum ClipboardMode
@ -175,8 +176,8 @@ alias CBM = ClipboardMode;
struct Inputs struct Inputs
{ {
DLList!(InputEvent) list; InputEvent* first, last;
Arena arena; Arena arena;
} }
pragma(inline) bool pragma(inline) bool
@ -287,20 +288,21 @@ Unlock(TicketMut* mut)
return atomicFetchAdd!(MemoryOrder.rel, u64)(mut.next_ticket, 1); return atomicFetchAdd!(MemoryOrder.rel, u64)(mut.next_ticket, 1);
} }
__gshared const DNode!(SysMessage) g_sys_message; __gshared const SysMessage g_sys_message;
__gshared DNode!(SysMessage)* g_NIL_MSG; __gshared SysMessage* g_NIL_MSG;
struct SysMessage struct SysMessage
{ {
SysMessageType type; SysMessageType type;
SysMessage* next, prev;
} }
struct MessageQueue struct MessageQueue
{ {
DLList!(SysMessage) list; TicketMut mut;
DLList!(SysMessage) free_list; Arena arena;
TicketMut mut; SysMessage* first, last;
Arena arena; LinkedList!(SysMessage) free_list;
} }
MessageQueue MessageQueue
@ -308,37 +310,35 @@ CreateMessageQueue()
{ {
if(g_NIL_MSG == null) if(g_NIL_MSG == null)
{ {
g_NIL_MSG = cast(DNode!(SysMessage)*)&g_sys_message; g_NIL_MSG = cast(SysMessage*)&g_sys_message;
} }
MessageQueue queue = { MessageQueue queue = {
list: { first: g_NIL_MSG,
first: g_NIL_MSG, last: g_NIL_MSG,
last: g_NIL_MSG, mut: CreateTicketMut(),
}, arena: CreateArena(MB(1)),
free_list: { free_list: {
first: g_NIL_MSG, first: g_NIL_MSG,
last: g_NIL_MSG, last: g_NIL_MSG,
}, },
mut: CreateTicketMut(),
arena: CreateArena(MB(1)),
}; };
return queue; return queue;
} }
pragma(inline) bool pragma(inline) bool
Nil(DNode!(SysMessage)* msg) Nil(SysMessage* msg)
{ {
return msg == null || msg == g_NIL_MSG; return msg == null || msg == g_NIL_MSG;
} }
DNode!(SysMessage)* SysMessage*
Pop(MessageQueue* queue) Pop(MessageQueue* queue)
{ {
Lock(&queue.mut); Lock(&queue.mut);
DNode!(SysMessage)* node = DLLPop(&queue.list, g_NIL_MSG); SysMessage* node = DLLPop(queue, g_NIL_MSG);
Unlock(&queue.mut); Unlock(&queue.mut);
@ -350,26 +350,26 @@ Push(PlatformWindow* w, SysMessageType msg)
{ {
Lock(&w.msg_queue.mut); Lock(&w.msg_queue.mut);
DNode!(SysMessage)* node = g_NIL_MSG; SysMessage* node = g_NIL_MSG;
if(Nil(w.msg_queue.free_list.first)) if(Nil(w.msg_queue.free_list.first))
{ {
node = Alloc!(DNode!(SysMessage))(&w.msg_queue.arena); node = Alloc!(SysMessage)(&w.msg_queue.arena);
} }
else else
{ {
node = DLLPop(&w.msg_queue.free_list, g_NIL_MSG); node = DLLPop(&w.msg_queue.free_list, g_NIL_MSG);
} }
node.value.type = msg; node.type = msg;
DLLPush(&w.msg_queue.list, node, g_NIL_MSG); DLLPush(&w.msg_queue, node, g_NIL_MSG);
Unlock(&w.msg_queue.mut); Unlock(&w.msg_queue.mut);
} }
void void
PushFree(PlatformWindow* w, DNode!(SysMessage)* node) PushFree(PlatformWindow* w, SysMessage* node)
{ {
DLLPush(&w.msg_queue.list, node, g_NIL_MSG); DLLPush(&w.msg_queue, node, g_NIL_MSG);
} }
version(linux) version(linux)
@ -548,35 +548,35 @@ Kill()
void void
ResetInputs(Inputs* inputs) ResetInputs(Inputs* inputs)
{ {
inputs.list.first = inputs.list.last = null; inputs.first = inputs.last = null;
Reset(&inputs.arena); Reset(&inputs.arena);
} }
void void
Push(Inputs* inputs, Input input, i32 x, i32 y, bool pressed, Modifier md) Push(Inputs* inputs, Input input, i32 x, i32 y, bool pressed, Modifier md)
{ {
DNode!(InputEvent)* node = Alloc!(DNode!(InputEvent))(&inputs.arena); InputEvent* node = Alloc!(InputEvent)(&inputs.arena);
node.value.key = input; node.key = input;
node.value.pressed = pressed; node.pressed = pressed;
node.value.x = x; node.x = x;
node.value.y = y; node.y = y;
node.value.md = md; node.md = md;
DLLPushFront(&inputs.list, node, null); DLLPushFront(inputs, node, null);
} }
void void
PushMotion(Inputs* inputs, i32 rel_x, i32 rel_y, i32 x, i32 y) PushMotion(Inputs* inputs, i32 rel_x, i32 rel_y, i32 x, i32 y)
{ {
DNode!(InputEvent)* node = Alloc!(DNode!(InputEvent))(&inputs.arena); InputEvent* node = Alloc!(InputEvent)(&inputs.arena);
node.value.key = Input.MouseMotion; node.key = Input.MouseMotion;
node.value.rel_x = rel_x; node.rel_x = rel_x;
node.value.rel_y = rel_y; node.rel_y = rel_y;
node.value.x = x; node.x = x;
node.value.y = y; node.y = y;
node.value.pressed = false; node.pressed = false;
DLLPushFront(&inputs.list, node, null); DLLPushFront(inputs, node, null);
} }
struct PlatformWindow struct PlatformWindow
@ -1156,7 +1156,7 @@ HandleEvents(void* window_ptr)
{ {
PlatformWindow* w = cast(PlatformWindow*)window_ptr; PlatformWindow* w = cast(PlatformWindow*)window_ptr;
DNode!(SysMessage)* sys_msg = g_NIL_MSG; SysMessage* sys_msg = g_NIL_MSG;
XEvent e; XEvent e;
bool ignore_mouse_events = false; bool ignore_mouse_events = false;
@ -1173,9 +1173,7 @@ HandleEvents(void* window_ptr)
if(!Nil(sys_msg)) if(!Nil(sys_msg))
{ {
SysMessage msg = sys_msg.value; switch (sys_msg.type)
switch (msg.type)
{ {
case SMT.Quit: case SMT.Quit:
{ {

View File

@ -4,7 +4,7 @@ name="Test_Runner"
/bin/bash ./build.sh build /bin/bash ./build.sh build
ldc2 platform.d aliases.d math.d util.d alloc.d assets.d external/xxhash/xxhash.d build/libcglm.a -d-version=DLIB_TEST -Xcc=-mno-sse -P-I/usr/include/freetype2 -L-lfreetype --main --unittest -g --of=$name ldc2 platform.d aliases.d math.d util.d alloc.d assets.d external/xxhash/xxhash.d build/libcglm.a -d-version=DLIB_TEST -Xcc=-mno-sse -P-I/usr/include/freetype2 -L-lfreetype --main --unittest -g --of=$name -verrors=8
rm $name.o rm $name.o
./$name ./$name

206
util.d
View File

@ -205,17 +205,21 @@ BitEq(u64 l, u64 r)
return (l & r) == r; return (l & r) == r;
} }
struct DNode(T) struct ListBox(T, bool doubly_linked)
{ {
DNode!(T)* next; alias U = ListBox!(T, doubly_linked);
DNode!(T)* prev;
T value; U* next;
static if(doubly_linked)
{
U* prev;
}
T value;
} }
struct DLList(T) struct LinkedList(T)
{ {
DNode!(T)* first; T* first, last;
DNode!(T)* last;
} }
void void
@ -320,100 +324,38 @@ DLLPush(T, U)(T* list, U* node, U* nil)
} }
void void
DLLInsert(T, U)(T* list, U* node, U* prev, U* nil) DLLInsert(T, U)(T* list, U* node, U* prev, U* nil) if(hasMember!(T, "last") && hasMember!(U, "prev"))
{ {
node.next = node.prev = nil;
if(CheckNil(nil, list.first) && CheckNil(nil, list.last)) if(CheckNil(nil, list.first) && CheckNil(nil, list.last))
{ {
assert(CheckNil(nil, prev));
list.first = list.last = node; list.first = list.last = node;
node.next = node.prev = nil;
} }
else if(list.first == prev && list.last == prev) else if(!CheckNil(nil, prev))
{ {
list.last = node; node.next = list.first;
node.prev = prev; list.first.prev = node;
prev.next = node; list.first = node;
node.prev = nil;
} }
else if(list.last == prev) else if(list.last == prev)
{ {
prev.next = node; list.last.next = node;
node.prev = prev; node.prev = list.last;
list.last = node; list.last = node;
node.next = nil;
} }
else if(!CheckNil(nil, prev) && CheckNil(nil, prev.next))
{}
else else
{ {
node.next = prev.next; node.prev = prev;
node.prev = prev; node.next = prev.next;
prev.next = node; node.next.prev = node;
prev.next = node;
} }
} }
struct Stack(T)
{
Node!(T)* top;
Node!(T)* free;
Node!(T)* nil;
}
void
SPush(T)(Arena* arena, Stack!(T)* stack, T value)
{
Node!(T)* node;
if(!CheckNil(stack.nil, stack.free))
{
node = stack.free;
stack.free = node.next;
}
else
{
node = Alloc!(Node!(T))(arena);
}
node.value = value;
if(CheckNil(stack.nil, stack.top))
{
stack.top = node;
node.next = stack.nil;
}
else
{
node.next = stack.top;
stack.top = node;
}
}
T
SPop(T)(Stack!(T)* stack)
{
T result;
if(!CheckNil(stack.nil, stack.top))
{
result = stack.top.value;
Node!(T)* free_node = stack.top;
stack.top = free_node.next;
free_node.next = stack.free;
stack.free = free_node;
}
return result;
}
struct Node(T)
{
Node!(T)* next;
T value;
}
struct SLList(T)
{
Node!(T)* first;
Node!(T)* last;
}
pragma(inline) bool pragma(inline) bool
CheckNil(T)(T* nil, T* node) CheckNil(T)(T* nil, T* node)
{ {
@ -494,8 +436,9 @@ SLLPush(T, U)(T* list, U* node, U* nil)
struct KVPair(K, V) struct KVPair(K, V)
{ {
K key; K key;
V value; V value;
KVPair!(K, V)* next;
} }
struct Result(V) struct Result(V)
@ -508,12 +451,12 @@ struct HashTable(K, V)
{ {
alias P = KVPair!(K, V); alias P = KVPair!(K, V);
SLList!(P) free_lists; LinkedList!(P) free_lists;
SLList!(P)[] lists; LinkedList!(P)[] lists;
Node!(P)* nil; P* nil;
Arena arena; Arena arena;
u64 node_count; u64 node_count;
u64 list_count; u64 list_count;
void opIndexAssign(V value, K key) void opIndexAssign(V value, K key)
{ {
@ -539,8 +482,8 @@ HashTable!(K, V)
CreateHashTable(K, V)(u64 size) CreateHashTable(K, V)(u64 size)
{ {
Arena arena = CreateArena(MB(4)); Arena arena = CreateArena(MB(4));
auto nil = Alloc!(Node!(KVPair!(K, V)))(&arena); auto nil = Alloc!(KVPair!(K, V))(&arena);
auto lists = Alloc!(SLList!(KVPair!(K, V)))(&arena, size); auto lists = Alloc!(LinkedList!(KVPair!(K, V)))(&arena, size);
HashTable!(K, V) table = { HashTable!(K, V) table = {
arena: arena, arena: arena,
@ -572,13 +515,12 @@ Clear(K, V)(HashTable!(K, V)* ht)
} }
} }
pragma(inline) Node!(KVPair!(K, V))* pragma(inline) KVPair!(K, V)*
Push(K, V)(HashTable!(K, V)* ht, K key, V value) Push(K, V)(HashTable!(K, V)* ht, K key, V value)
{ {
alias P = KVPair!(K, V); alias P = KVPair!(K, V);
alias N = Node!(P);
N* node = ht.nil; P* node = ht.nil;
if(!CheckNil(ht.nil, ht.free_lists.first)) if(!CheckNil(ht.nil, ht.free_lists.first))
{ {
@ -586,12 +528,12 @@ Push(K, V)(HashTable!(K, V)* ht, K key, V value)
} }
else else
{ {
node = Alloc!(N)(&ht.arena); node = Alloc!(P)(&ht.arena);
} }
node.next = ht.nil; node.next = ht.nil;
node.value.key = key; node.key = key;
node.value.value = value; node.value = value;
SLLPush(GetList(ht, key), node, ht.nil); SLLPush(GetList(ht, key), node, ht.nil);
@ -608,9 +550,9 @@ Search(K, V)(HashTable!(K, V)* ht, K key)
auto list = GetList(ht, key); auto list = GetList(ht, key);
for(auto node = list.first; !CheckNil(ht.nil, node); node = node.next) for(auto node = list.first; !CheckNil(ht.nil, node); node = node.next)
{ {
if(node.value.key == key) if(node.key == key)
{ {
result = &node.value; result = node;
break; break;
} }
} }
@ -618,7 +560,7 @@ Search(K, V)(HashTable!(K, V)* ht, K key)
return result; return result;
} }
pragma(inline) SLList!(KVPair!(K, V))* pragma(inline) LinkedList!(KVPair!(K, V))*
GetList(K, V)(HashTable!(K, V)* ht, K key) GetList(K, V)(HashTable!(K, V)* ht, K key)
{ {
u64 hash = Hash(&key); u64 hash = Hash(&key);
@ -1000,8 +942,9 @@ Embed(string file_name)
version(DLIB_TEST) unittest version(DLIB_TEST) unittest
{ {
{ // Singly Linked List { // Singly Linked List
SLList!(u32) list; alias LT = ListBox!(u32, false);
Node!(u32)[5] nodes; LinkedList!(LT) list;
LT[5] nodes;
foreach(u32 i, n; nodes) foreach(u32 i, n; nodes)
{ {
nodes[i].value = i; nodes[i].value = i;
@ -1014,15 +957,15 @@ version(DLIB_TEST) unittest
SLLRemove(&list, &nodes[1], &nodes[0], null); SLLRemove(&list, &nodes[1], &nodes[0], null);
SLLRemove(&list, &nodes[3], &nodes[2], null); SLLRemove(&list, &nodes[3], &nodes[2], null);
Node!(u32)* n = list.first; LT* n = list.first;
assert(list.first != null && list.last != null); assert(list.first != null && list.last != null);
assert(n != null); assert(n != null);
assert(n.next != null); assert(n.next != null);
void TestSLList(SLList!(u32)* list, u32[] result) void TestSLList(LinkedList!(LT)* list, u32[] result)
{ {
Node!(u32)* n = list.first; LT* n = list.first;
foreach(i, v; result) foreach(i, v; result)
{ {
assert(n != null); assert(n != null);
@ -1057,9 +1000,11 @@ version(DLIB_TEST) unittest
} }
{ // Doubly Linked List { // Doubly Linked List
void TestDLList(DLList!(u32)* list, u32[] result) alias LT = ListBox!(u32, true);
void TestDLList(T)(T* list, u32[] result)
{ {
DNode!(u32)* n = list.first; LT* n = list.first;
foreach(i, v; result) foreach(i, v; result)
{ {
assert(n != null); assert(n != null);
@ -1100,8 +1045,8 @@ version(DLIB_TEST) unittest
} }
} }
DLList!(u32) list; LinkedList!(LT) list;
DNode!(u32)[5] nodes; LT[5] nodes;
foreach(u32 i, n; nodes) foreach(u32 i, n; nodes)
{ {
nodes[i].value = i; nodes[i].value = i;
@ -1223,37 +1168,6 @@ version(DLIB_TEST) unittest
assert(v3 > 0); assert(v3 > 0);
} }
{ // Stack
Stack!(u32) stack;
Arena arena = CreateArena(MB(1));
u32 v1 = 1;
u32 v2 = 2;
u32 v3 = 3;
Node!(u32) nil;
stack.nil = &nil;
SPush(&arena, &stack, v1);
SPush(&arena, &stack, v2);
SPush(&arena, &stack, v3);
u32 count = 3;
for (auto n = stack.top; !CheckNil(null, n); n = n.next, count -= 1)
{
assert(n.value == count);
}
count = 3;
for (u32 n = SPop(&stack); n != 0; n = SPop(&stack), count -= 1)
{
assert(n == count);
}
assert(stack.top == &nil);
}
{ // Casts { // Casts
u8[] arr = CastStr!(u8)("Test"); u8[] arr = CastStr!(u8)("Test");
char[] char_arr = ['a', 'b']; char[] char_arr = ['a', 'b'];