From 95719e7a36b76341445acdeb66e23c11be40f3a0 Mon Sep 17 00:00:00 2001 From: Synthasmagoria Date: Fri, 17 Jul 2026 10:06:37 +0200 Subject: [PATCH] Initial commit --- list.odin | 139 +++++++++++++++++++++++++++++++++++++++++++++++ tests/tests.odin | 79 +++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 list.odin create mode 100644 tests/tests.odin diff --git a/list.odin b/list.odin new file mode 100644 index 0000000..11d7d2a --- /dev/null +++ b/list.odin @@ -0,0 +1,139 @@ +package list + +IntrusiveList :: struct { + head: ^IntrusiveListNode +} + +IntrusiveListNode :: struct { + prev, next: ^IntrusiveListNode +} + +IntrusiveListIterator :: struct($T: typeid) { + curr: ^IntrusiveListNode, + offset: uintptr, +} + +intrusive_list_push_front :: proc "contextless" (list: ^IntrusiveList, node: ^IntrusiveListNode) { + if list.head == nil { + list.head = node + node.next, node.prev = node, node + } else { + tail := list.head.prev + list.head.prev = node + tail.next = node + node.next, node.prev = list.head, tail + list.head = node + } +} + +intrusive_list_push_back :: proc (list: ^IntrusiveList, node: ^IntrusiveListNode) { + if list.head == nil { + list.head = node + node.next, node.prev = node, node + } else { + node.next, node.prev = list.head, list.head.prev + list.head.prev.next = node + list.head.prev = node + } +} + +intrusive_list_pop_front :: proc "contextless" (list: ^IntrusiveList) -> ^IntrusiveListNode { + head := list.head + if head == nil { + return head + } else if intrusive_list_node_is_only(list^, head) { + list.head = nil + return head + } + head.prev.next = head.next + head.next.prev = head.prev + list.head = head.next + return head +} + +intrusive_list_pop_back :: proc "contextless" (list: ^IntrusiveList) -> ^IntrusiveListNode { + head := list.head + if head == nil { + return nil + } + if intrusive_list_node_is_only(list^, head) { + list.head = nil + return head + } + tail := list.head.prev + tail.prev.next = tail.next + tail.next.prev = tail.prev + return tail +} + +intrusive_list_node_is_only :: proc "contextless" (list: IntrusiveList, node: ^IntrusiveListNode) -> bool { + return node == list.head && node == node.next && node == node.prev +} + +intrusive_list_remove :: proc "contextless" (list: ^IntrusiveList, node: ^IntrusiveListNode) { + if intrusive_list_node_is_only(list^, node) { + list.head = nil + } else { + prev := node.prev + next := node.next + prev.next = next + next.prev = prev + } +} + +intrusive_list_get_next :: proc "contextless" (node: ^IntrusiveListNode, $T: typeid, $field_name: string) -> ^T + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == IntrusiveListNode { + return (^T)(uintptr(node.next) - offset_of_by_string(T, field_name)) +} + +intrusive_list_get_previous :: proc "contextless" (node: ^IntrusiveListNode, $T: typeid, $field_name: string) -> ^T + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == IntrusiveListNode { + return (^T)(uintptr(node.prev) - offset_of_by_string(T, field_name)) +} + +intrusive_list_get_relative :: proc "contextless" (node: ^IntrusiveListNode, $T: typeid, $field_name: string, pos: int) -> ^T { + it := intrusive_list_iterator_from_node(node, T, field_name) + switch math.sign(pos) { + case 0: + return (^T)(uintptr(it.curr) - it.offset) + case 1: + node: ^T + for i in 0.. bool { + return list.head == nil +} + +intrusive_list_iterator_head :: proc "contextless" (list: IntrusiveList, $T: typeid, $field_name: string) -> IntrusiveListIterator(T) + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == IntrusiveListNode { + return {list.head, offset_of_by_string(T, field_name)} +} + +intrusive_list_iterator_from_node :: proc "contextless" (node: ^IntrusiveListNode, $T: typeid, $field_name: string) -> IntrusiveListIterator(T) + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == IntrusiveListNode { + return {node, offset_of_by_string(T, field_name)} +} + +intrusive_list_iterate_next :: proc "contextless" (it: ^IntrusiveListIterator($T)) -> ^T { + it.curr = it.curr.next + return (^T)(uintptr(it.curr) - it.offset) +} + +intrusive_list_iterate_prev :: proc "contextless" (it: ^IntrusiveListIterator($T)) -> ^T { + it.curr = it.curr.prev + return (^T)(uintptr(it.curr) - it.offset) +} diff --git a/tests/tests.odin b/tests/tests.odin new file mode 100644 index 0000000..80db6e1 --- /dev/null +++ b/tests/tests.odin @@ -0,0 +1,79 @@ +package tests + +import "core:testing" +import list "../" + +Entity :: struct { + id: int, + using node: list.IntrusiveListNode, + position: [2]f32, +} + +IntrusiveListTestData :: struct { + nodes: [3]Entity, + list: list.IntrusiveList, + first, second, last: ^list.IntrusiveListNode, +} + +intrusive_list_setup_data :: proc(data: ^IntrusiveListTestData) { + for i in 0..