From cdfe9cb66014f1e2dc17e65900e3034500d1f43a Mon Sep 17 00:00:00 2001 From: Matthew Date: Fri, 29 Aug 2025 07:49:26 +1000 Subject: [PATCH] add doubly linked list, used linked list for platform inputs, add tests for linked lists + memcpy --- assets.d | 2 +- math.d | 54 ++++---- platform.d | 77 ++++++----- util.d | 381 ++++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 422 insertions(+), 92 deletions(-) diff --git a/assets.d b/assets.d index 300e3d1..ffcc33c 100644 --- a/assets.d +++ b/assets.d @@ -212,7 +212,7 @@ OpenAssetPack() } } -pragma(inline): void +pragma(inline) void CheckAssetPack() { if (!Asset_Pack_Opened) diff --git a/math.d b/math.d index a17f4e8..ce2adad 100644 --- a/math.d +++ b/math.d @@ -678,7 +678,7 @@ Mat4MulASM(Mat4 l, Mat4 r) return result; } -pragma(inline): Mat4 +pragma(inline) Mat4 Mat4Identity() { return Mat4( @@ -689,7 +689,7 @@ Mat4Identity() ); } -pragma(inline): void +pragma(inline) void Mat4Identity(Mat4* mat) { MatZero(mat); @@ -700,7 +700,7 @@ Mat4Identity(Mat4* mat) (*mat)[3, 3] = 1.0; } - pragma(inline): Mat3 + pragma(inline) Mat3 Mat3Identity() { return Mat3( @@ -710,7 +710,7 @@ Mat3Identity() ); } - pragma(inline): Mat2 + pragma(inline) Mat2 Mat2Identity() { return Mat2( @@ -719,7 +719,7 @@ Mat2Identity() ); } -pragma(inline): Quat +pragma(inline) Quat QuatFromAxis(f32 angle, Vec3 axis) { Quat q; @@ -727,39 +727,39 @@ QuatFromAxis(f32 angle, Vec3 axis) return q; } -pragma(inline): f32 +pragma(inline) f32 Dot(Vec2* l, Vec2* r) { return l.x * r.x + l.y * r.y; } -pragma(inline): f32 +pragma(inline) f32 Dot(Vec3* l, Vec3* r) { return l.x * r.x + l.y * r.y + l.z * r.z; } -pragma(inline): f32 +pragma(inline) f32 Dot(Vec4* l, Vec4* r) { // TODO: SIMD this return l.x * r.x + l.y * r.y + l.z * r.z + l.w * r.w; } -pragma(inline): f32 +pragma(inline) f32 Norm(Vec3* v) { return sqrtf(Dot(v, v)); } -pragma(inline): f32 +pragma(inline) f32 Norm(Vec4* v) { // TODO: SIMD this return sqrtf(Dot(v, v)); } -pragma(inline): void +pragma(inline) void Normalize(T)(T* vec) if (is(T: Vec2) || is(T: Vec3) || is(T: Vec4)) { f32 length = Norm(vec); @@ -774,14 +774,14 @@ Normalize(T)(T* vec) if (is(T: Vec2) || is(T: Vec3) || is(T: Vec4)) } } -pragma(inline): T +pragma(inline) T Normalize(T)(T vec) if (is(T: Vec2) || is(T: Vec3) || is(T: Vec4)) { Normalize(&vec); return vec; } -pragma(inline): Quat +pragma(inline) Quat Normalize(Quat q) { f32 dot = Norm(&q.vec); @@ -796,7 +796,7 @@ Normalize(Quat q) return q; } -pragma(inline): Mat4 +pragma(inline) Mat4 Perspective(f32 fov, f32 aspect, f32 near, f32 far) { Mat4 res; @@ -822,7 +822,7 @@ Ortho(f32 left, f32 bottom, f32 right, f32 top, f32 near, f32 far) return mat; } -pragma(inline): Vec3 +pragma(inline) Vec3 Rotate(Quat q, Vec3 vec) { Quat p = Normalize(q); @@ -840,7 +840,7 @@ Rotate(Quat q, Vec3 vec) return v1 + v2; } -pragma(inline): Mat4 +pragma(inline) Mat4 LookAt(Vec3 eye, Vec3 center, Vec3 up) { Mat4 result; @@ -849,13 +849,13 @@ LookAt(Vec3 eye, Vec3 center, Vec3 up) return result; } -pragma(inline): Mat4 +pragma(inline) Mat4 Look(Vec3 eye, Vec3 dir, Vec3 up) { return LookAt(eye, eye + dir, up); } -pragma(inline): Vec3 +pragma(inline) Vec3 Cross(Vec3 a, Vec3 b) { Vec3 c; @@ -863,7 +863,7 @@ Cross(Vec3 a, Vec3 b) return c; } -pragma(inline): Vec3 +pragma(inline) Vec3 CrossN(Vec3 a, Vec3 b) { Vec3 c; @@ -872,20 +872,20 @@ CrossN(Vec3 a, Vec3 b) return c; } -pragma(inline): void +pragma(inline) void CrossN(Vec3 a, Vec3 b, Vec3* dst) { Cross(a, b, dst); glm_vec3_normalize(dst.v.ptr); } -pragma(inline): void +pragma(inline) void Cross(Vec3 a, Vec3 b, Vec3* dst) { glm_vec3_cross(a.v.ptr, b.v.ptr, dst.v.ptr); } -pragma(inline): void +pragma(inline) void MatZero(Mat4* mat) { auto v = &mat.vec; @@ -900,13 +900,13 @@ MatZero(Mat4* mat) } } -pragma(inline): void +pragma(inline) void Translate(Mat4* mat, Vec3 vec) { glm_translate(mat.glm_mat.ptr, vec.v.ptr); } -pragma(inline): Mat4 +pragma(inline) Mat4 Inverse(Mat4 mat) { Mat4 res; @@ -915,19 +915,19 @@ Inverse(Mat4 mat) return res; } -pragma(inline): f32 +pragma(inline) f32 Mix(f32 x, f32 y, f32 a) { return x * (1 - a) + y * a; } -pragma(inline): f32 +pragma(inline) f32 InverseLerp(f32 v, f32 min, f32 max) { return (v - min) / (max - min); } -pragma(inline): f32 +pragma(inline) f32 Remap(f32 v, f32 in_min, f32 in_max, f32 out_min, f32 out_max) { f32 t = InverseLerp(v, in_min, in_max); diff --git a/platform.d b/platform.d index 528fcc5..786165b 100644 --- a/platform.d +++ b/platform.d @@ -1,10 +1,13 @@ module dlib.platform; import dlib.aliases; +import dlib.alloc : Reset; import dlib.alloc; import dlib.util; import includes; + +import std.typecons; import std.stdio; import core.memory; import core.thread.osthread; @@ -73,8 +76,38 @@ struct InputEvent struct Inputs { - InputEvent[10] events; - u32 count; + DLList!(InputEvent) list; + Arena arena; +} + +void +Reset(Inputs* inputs) +{ + inputs.list.first = inputs.list.last; + Reset(&inputs.arena); +} + +void +Push(Inputs* inputs, Input input, bool pressed) +{ + DNode!(InputEvent)* node = Alloc!(DNode!(InputEvent))(&inputs.arena); + node.value.key = input; + node.value.pressed = pressed; + + PushFront(&inputs.list, node, null); +} + +void +Push(Inputs* inputs, i32 rel_x, i32 rel_y, u16 x, u16 y) +{ + DNode!(InputEvent)* node = Alloc!(DNode!(InputEvent))(&inputs.arena); + node.value.key = Input.MouseMotion; + node.value.rel_x = rel_x; + node.value.rel_y = rel_y; + node.value.x = x; + node.value.y = y; + + PushFront(&inputs.list, node, null); } struct PlatformWindow @@ -116,6 +149,9 @@ CreateWindow(string name, u16 width, u16 height) PlatformWindow window = { w: width, h: height, + inputs: { + arena: CreateArena(MB(1)), + }, }; assert(width > 0 && height > 0, "CreateWindow error: width and height must be above 0"); @@ -309,11 +345,11 @@ FlushEvents(PlatformWindow* window) void HandleEvents(PlatformWindow* window) { - window.inputs.count = 0; - xcb_generic_event_t* e; bool ignore_mouse_events = false; + + Inputs* inputs = &window.inputs; do { @@ -339,12 +375,6 @@ HandleEvents(PlatformWindow* window) case XCB_KEY_RELEASE: case XCB_KEY_PRESS: { - // TODO: definitely definitely need to rework this whole thing - if (window.inputs.count == window.inputs.events.length) - { - continue; - } - xcb_key_press_event_t* keyboard_event = cast(xcb_key_press_event_t*)e; bool pressed = e.response_type == XCB_KEY_PRESS; @@ -354,25 +384,12 @@ HandleEvents(PlatformWindow* window) if (input != KBI.None) { - window.inputs.events[window.inputs.count].key = input; - window.inputs.events[window.inputs.count].pressed = pressed; - window.inputs.count += 1; - - if (input == KBI.F2) - { - LockCursor(window); - } - else if (input == KBI.F3) - { - UnlockCursor(window); - } + Push(inputs, input, pressed); } } break; case XCB_BUTTON_PRESS: case XCB_BUTTON_RELEASE: { - if (window.inputs.count == window.inputs.events.length) continue; - xcb_button_press_event_t* mouse_event = cast(xcb_button_press_event_t*)e; bool pressed = e.response_type == XCB_BUTTON_PRESS; Input input = Input.None; @@ -387,15 +404,12 @@ HandleEvents(PlatformWindow* window) if (input != Input.None) { - window.inputs.events[window.inputs.count].key = input; - window.inputs.events[window.inputs.count].pressed = pressed; - window.inputs.count += 1; + Push(inputs, input, pressed); } } break; case XCB_MOTION_NOTIFY: { if (ignore_mouse_events) continue; - if (window.inputs.count == window.inputs.events.length) continue; xcb_motion_notify_event_t* move_event = cast(xcb_motion_notify_event_t*)e; @@ -412,12 +426,7 @@ HandleEvents(PlatformWindow* window) if (x > 0 || y > 0) { - window.inputs.events[window.inputs.count].key = Input.MouseMotion; - window.inputs.events[window.inputs.count].x = move_event.event_x; - window.inputs.events[window.inputs.count].y = move_event.event_y; - window.inputs.events[window.inputs.count].rel_x = window.mouse_prev_x - x; - window.inputs.events[window.inputs.count].rel_y = window.mouse_prev_y - y; - window.inputs.count += 1; + Push(inputs, window.mouse_prev_x-x, window.mouse_prev_y-y, x, y); } window.mouse_prev_x = x; diff --git a/util.d b/util.d index 774e10c..7defc92 100644 --- a/util.d +++ b/util.d @@ -56,7 +56,7 @@ GB(u64 v) return MB(v) * 1024; }; -pragma(inline): void +pragma(inline) void ConvertColor(Vec4 *dst, u32 src) { if (src == 0) @@ -70,7 +70,7 @@ ConvertColor(Vec4 *dst, u32 src) } } -pragma(inline): void +pragma(inline) void Convert(Vec4* dst, u32 src) { dst.r = cast(f32)((src >> 0) & 0xFF) / 255.0; @@ -85,6 +85,123 @@ BitEq(u64 l, u64 r) return (l & r) == r; } +struct DNode(T) +{ + DNode!(T)* next; + DNode!(T)* prev; + T value; +} + +struct DLList(T) +{ + DNode!(T)* first; + DNode!(T)* last; +} + +pragma(inline) bool +CheckNil(T)(DNode!(T)* nil, DNode!(T)* node) +{ + return node == null || node == nil; +} + +void +ConcatInPlace(T)(DLList!(T)* list, DLList!(T)* to_concat) +{ + if (to_concat.first) + { + if (list.first) + { + list.last.next = to_concat.first; + list.last = to_concat.last; + } + else + { + list.first = to_concat.first; + list.last = to_concat.last; + } + + memset(to_concat, 0, DLList!(T).sizeof); + } +} + +DNode!(T)* +Pop(T)(DLList!(T)* list, DNode!(T)* nil) +{ + DNode!(T)* node = list.first; + + if (list.first == list.last) + { + list.first = list.last = nil; + } + else + { + list.first = list.first.next; + list.first.prev = nil; + } + + return node; +} + +void +Remove(T)(DLList!(T)* list, DNode!(T)* node, DNode!(T)* nil) +{ + if (list.first == list.last) + { + list.first = list.last = nil; + } + else if (list.first == node) + { + list.first = node.next; + list.first.prev = nil; + } + else if (list.last == node) + { + node.prev.next = nil; + list.last = node.prev; + } + else + { + node.next.prev = node.prev; + node.prev.next = node.next; + } + + node.prev = node.next = nil; +} + +void +PushFront(T)(DLList!(T)* list, DNode!(T)* node, DNode!(T)* nil) +{ + if (CheckNil(nil, list.first)) + { + list.first = list.last = node; + node.prev = node.next = nil; + } + else + { + node.next = list.first; + node.prev = nil; + list.first.prev = node; + list.first = node; + } +} + +void +Push(T)(DLList!(T)* list, DNode!(T)* node, DNode!(T)* nil) +{ + if (CheckNil(nil, list.first)) + { + list.first = list.last = node; + node.prev = node.next = nil; + } + else + { + list.last.next = node; + node.prev = list.last; + list.last = node; + node.next = nil; + } +} + struct Node(T) { Node!(T)* next; @@ -97,13 +214,13 @@ struct SLList(T) Node!(T)* last; } -pragma(inline): bool +pragma(inline) bool CheckNil(T)(Node!(T)* nil, Node!(T)* node) { return node == null || node == nil; } -pragma(inline): void +void ConcatInPlace(T)(SLList!(T)* list, SLList!(T)* to_concat) { if (to_concat.first) @@ -123,7 +240,7 @@ ConcatInPlace(T)(SLList!(T)* list, SLList!(T)* to_concat) } } -pragma(inline): Node!(T)* +pragma(inline) Node!(T)* Pop(T)(SLList!(T)*list, Node!(T)* nil) { Node!(T)* node = list.first; @@ -140,11 +257,9 @@ Pop(T)(SLList!(T)*list, Node!(T)* nil) return node; } -pragma(inline): void -Remove(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* prev, Node!(T)* nil) +pragma(inline) void +Remove(T)(SLList!(T)* list, Node!(T)* node, Node!(T)* prev, Node!(T)* nil) { - node.next = nil; - if (list.first == list.last) { list.first = list.last = nil; @@ -162,10 +277,12 @@ Remove(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* prev, Node!(T)* nil) { prev.next = node.next; } + + node.next = nil; } -pragma(inline): void -PushFront(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* nil) +pragma(inline) void +PushFront(T)(SLList!(T)* list, Node!(T)* node, Node!(T)* nil) { if (CheckNil(nil, list.first)) { @@ -179,8 +296,8 @@ PushFront(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* nil) } } -pragma(inline): void -Push(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* nil) +pragma(inline) void +Push(T)(SLList!(T)* list, Node!(T)* node, Node!(T)* nil) { if (CheckNil(nil, list.first)) { @@ -266,7 +383,7 @@ CreateHashTable(K, V)(u64 size) return table; } -pragma(inline): void +pragma(inline) void Clear(K, V)(HashTable!(K, V)* ht) { table.count = 0; @@ -276,7 +393,7 @@ Clear(K, V)(HashTable!(K, V)* ht) } } -pragma(inline): Node!(KVPair!(K, V))* +pragma(inline) Node!(KVPair!(K, V))* Push(K, V)(HashTable!(K, V)* ht, K key, V value) { alias P = KVPair!(K, V); @@ -304,7 +421,7 @@ Push(K, V)(HashTable!(K, V)* ht, K key, V value) return node; } -pragma(inline): KVPair!(K, V)* +pragma(inline) KVPair!(K, V)* Search(K, V)(HashTable!(K, V)* ht, K key) { KVPair!(K, V)* result = null; @@ -322,7 +439,7 @@ Search(K, V)(HashTable!(K, V)* ht, K key) return result; } -pragma(inline): SLList!(KVPair!(K, V))* +pragma(inline) SLList!(KVPair!(K, V))* GetList(K, V)(HashTable!(K, V)* ht, K key) { u64 hash = Hash(&key); @@ -330,7 +447,7 @@ GetList(K, V)(HashTable!(K, V)* ht, K key) return ht.lists.ptr + index; } -pragma(inline): Result!(V) +pragma(inline) Result!(V) Delete(K, V)(HashTable!(K, V)* ht, K key) { Result!(V) result = { ok: false }; @@ -359,25 +476,25 @@ Delete(K, V)(HashTable!(K, V)* ht, K key) const u64 HASH_SEED = 5995; -pragma(inline): u64 +pragma(inline) u64 Hash(T)(T[] value) { return xxh3_64bits_withSeed(value.ptr, (T.sizeof * value.length) / u8.sizeof, HASH_SEED); } -pragma(inline): u64 +pragma(inline) u64 Hash(T)(T* value) { return xxh3_64bits_withSeed(value, T.sizeof / u8.sizeof, HASH_SEED); } -pragma(inline): u64 +pragma(inline) u64 Hash(string str) { return xxh3_64bits_withSeed(str.ptr, str.length, HASH_SEED); } -pragma(inline): u64 +pragma(inline) u64 RDTSC() { union u64_split @@ -404,7 +521,7 @@ RDTSC() return val.full; } -pragma(inline): u64 +pragma(inline) u64 OSTimeFreq() { version (linux) @@ -415,7 +532,7 @@ OSTimeFreq() return freq; } -pragma(inline): u64 +pragma(inline) u64 OSTime() { version(linux) @@ -474,7 +591,7 @@ CreateTimer(u64 fps) return timer; } -pragma(inline): bool +pragma(inline) bool CheckTimer(IntervalTimer* t) { bool result = false; @@ -529,7 +646,7 @@ CreateTimer() return timer; } -pragma(inline): f32 +pragma(inline) f32 DeltaTime(Timer* t) { u64 time = RDTSC(); @@ -576,7 +693,7 @@ MemCpy(void* dst_p, void* src_p, u64 length) u64 remaining = length; if (remaining >= 64) { - for(u64 i = 0; i < length; i += 64) + for(u64 i = 0; i + 64 < length; i += 64) { asm { @@ -602,7 +719,7 @@ MemCpy(void* dst_p, void* src_p, u64 length) if (remaining >= 32) { - for(u64 i = remaining; i < length; i += 32) + for(u64 i = length - remaining; i + 32 < length; i += 32) { asm { @@ -622,9 +739,9 @@ MemCpy(void* dst_p, void* src_p, u64 length) } } - for(u64 i = remaining; i < length; i += 1) + if (remaining > 0) { - dst[i] = src[i]; + dst[length-remaining .. length] = src[length-remaining .. length]; } } @@ -634,3 +751,207 @@ Embed(string file_name) import std.file; return cast(u8[])read(file_name); } + +unittest +{ + { // Singly Linked List + SLList!(u32) list; + Node!(u32)[5] nodes; + foreach(u32 i, n; nodes) + { + nodes[i].value = i; + Push(&list, &nodes[i], null); + } + + u32 count = 0; + u32[3] res1 = [0, 2, 4]; + + Remove(&list, &nodes[1], &nodes[0], null); + Remove(&list, &nodes[3], &nodes[2], null); + + Node!(u32)* n = list.first; + + assert(list.first != null && list.last != null); + assert(n != null); + assert(n.next != null); + + void TestSLList(SLList!(u32)* list, u32[] result) + { + Node!(u32)* n = list.first; + foreach(i, v; result) + { + assert(n != null); + assert(v == n.value); + + if (i == result.length-1) + { + assert(n.next == null); + assert(n == list.last); + } + + n = n.next; + } + } + + TestSLList(&list, res1); + + count = 0; + u32[5] res2 = [3, 0, 2, 4, 1]; + + PushFront(&list, &nodes[3], null); + Push(&list, &nodes[1], null); + + TestSLList(&list, res2); + + count = 0; + + Remove(&list, &nodes[3], null, null); + Remove(&list, &nodes[1], &nodes[4], null); + + TestSLList(&list, res1); + } + + { // Doubly Linked List + void TestDLList(DLList!(u32)* list, u32[] result) + { + DNode!(u32)* n = list.first; + foreach(i, v; result) + { + assert(n != null); + assert(v == n.value); + + if (i > 0) + { + assert(n.prev != null); + } + + if (i == result.length-1) + { + assert(n.next == null); + assert(n == list.last); + } + + n = n.next; + } + + n = list.last; + foreach_reverse(i, v; result) + { + assert(n != null); + assert(v == n.value); + + if (i == result.length-1) + { + assert(n.next == null); + } + + if (i == 0) + { + assert(n.prev == null); + assert(n == list.first); + } + + n = n.prev; + } + } + + DLList!(u32) list; + DNode!(u32)[5] nodes; + foreach(u32 i, n; nodes) + { + nodes[i].value = i; + Push(&list, &nodes[i], null); + } + + assert(list.first != null && list.last != null); + + TestDLList(&list, [0, 1, 2, 3, 4]); + + u32 count = 0; + u32[3] res1 = [0, 2, 4]; + + Remove(&list, &nodes[1], null); + Remove(&list, &nodes[3], null); + + TestDLList(&list, res1); + + count = 0; + u32[5] res2 = [3, 0, 2, 4, 1]; + + PushFront(&list, &nodes[3], null); + Push(&list, &nodes[1], null); + + TestDLList(&list, res2); + + Remove(&list, &nodes[3], null); + Remove(&list, &nodes[1], null); + + TestDLList(&list, res1); + } + + { // MemCpy + import std.conv; + + u8[777] bytes; + u8[777] test_bytes; + + bytes[0 .. 123] = 123; + bytes[123 .. 333] = 133; + bytes[333 .. 655] = 155; + bytes[655 .. $] = 199; + + test_bytes[0 .. $] = bytes[0 .. $]; + + assert(test_bytes == bytes); + + test_bytes[0 .. $] = 0; + + MemCpy(test_bytes.ptr, bytes.ptr, 777); + + assert(test_bytes == bytes); + + test_bytes[0 .. $] = 0; + + MemCpy(test_bytes.ptr+100, bytes.ptr, 32); + + u32 count = 0; + foreach(i, v; test_bytes[100 .. 132]) + { + if (v != bytes[count]) + { + Logf("Failed %d %d %d", i, v, bytes[count]); + assert(false); + } + + count += 1; + } + + assert(test_bytes[100 .. 132] == bytes[0 .. 32]); + + test_bytes[0 .. $] = 0; + + MemCpy(test_bytes.ptr, bytes.ptr, 33); + + assert(test_bytes[0 .. 33] == bytes[0 .. 33]); + + test_bytes[0 .. $] = 0; + + MemCpy(test_bytes.ptr, bytes.ptr, 65); + + assert(test_bytes[0 .. 65] == bytes[0 .. 65]); + + test_bytes[0 .. $] = 0; + + MemCpy(test_bytes.ptr, bytes.ptr, 96); + + foreach(i, v; test_bytes[0 .. 96]) + { + if (v != bytes[i]) + { + assert(false); + } + } + + assert(test_bytes[0 .. 96] == bytes[0 .. 96]); + } +}