aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2022-04-27 14:27:33 +0100
committergingerBill <bill@gingerbill.org>2022-04-27 14:27:33 +0100
commit3a9b0a22e728d1158efccb7f90e2c8aa05c486ef (patch)
tree388ea39c6e3aca7e42c80ccc42d9a49fefc60482
parent9349dfba8fec53f52f77a0c8928e115ec93ff447 (diff)
Add `core:container/intrusive/list`
-rw-r--r--core/container/intrusive/list/intrusive_list.odin173
1 files changed, 173 insertions, 0 deletions
diff --git a/core/container/intrusive/list/intrusive_list.odin b/core/container/intrusive/list/intrusive_list.odin
new file mode 100644
index 000000000..88e21edc5
--- /dev/null
+++ b/core/container/intrusive/list/intrusive_list.odin
@@ -0,0 +1,173 @@
+package container_intrusive_list
+
+import "core:intrinsics"
+
+// An intrusive doubly-linked list
+//
+// As this is an intrusive container, a `Node` must be embedded in your own
+// structure which is conventionally called a "link". The use of `push_front`
+// and `push_back` take the address of this node. Retrieving the data
+// associated with the node requires finding the relative offset of the node
+// of the parent structure. The parent type and field name are given to
+// `iterator_*` procedures, or to the built-in `container_of` procedure.
+//
+// This data structure is two-pointers in size:
+// 8 bytes on 32-bit platforms and 16 bytes on 64-bit platforms
+List :: struct {
+ head: ^Node,
+ tail: ^Node,
+}
+
+
+Node :: struct {
+ next, prev: ^Node,
+}
+
+push_front :: proc(list: ^List, node: ^Node) {
+ if list.head != nil {
+ list.head.prev = node
+ node.prev, node.next = nil, list.head
+ list.head = node
+ } else {
+ list.head, list.tail = node, node
+ node.prev, node.next = nil, nil
+ }
+}
+
+push_back :: proc(list: ^List, node: ^Node) {
+ if list.tail != nil {
+ list.tail.next = node
+ node.prev, node.next = list.tail, nil
+ list.tail = node
+ } else {
+ list.head, list.tail = node, node
+ node.prev, node.next = nil, nil
+ }
+}
+
+remove :: proc(list: ^List, node: ^Node) {
+ if node != nil {
+ if node.next != nil {
+ node.next.prev = node.prev
+ }
+ if node.prev != nil {
+ node.prev.next = node.next
+ }
+ if list.head == node {
+ list.head = node.next
+ }
+ if list.tail == node {
+ list.tail = node.prev
+ }
+ }
+}
+
+remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
+ for node := list.head; node != nil; {
+ next := node.next
+ if to_erase(node) {
+ if node.next != nil {
+ node.next.prev = node.prev
+ }
+ if node.prev != nil {
+ node.prev.next = node.next
+ }
+ if list.head == node {
+ list.head = node.next
+ }
+ if list.tail == node {
+ list.tail = node.prev
+ }
+ }
+ node = next
+ }
+}
+
+
+is_empty :: proc(list: ^List) -> bool {
+ return list.head == nil
+}
+
+pop_front :: proc(list: ^List) -> ^Node {
+ link := list.head
+ if link == nil {
+ return nil
+ }
+ if link.next != nil {
+ link.next.prev = link.prev
+ }
+ if link.prev != nil {
+ link.prev.next = link.next
+ }
+ if link == list.head {
+ list.head = link.next
+ }
+ if link == list.tail {
+ list.tail = link.prev
+ }
+ return link
+
+}
+pop_back :: proc(list: ^List) -> ^Node {
+ link := list.tail
+ if link == nil {
+ return nil
+ }
+ if link.next != nil {
+ link.next.prev = link.prev
+ }
+ if link.prev != nil {
+ link.prev.next = link.next
+ }
+ if link == list.head {
+ list.head = link.next
+ }
+ if link == list.tail {
+ list.tail = link.prev
+ }
+ return link
+}
+
+
+Iterator :: struct($T: typeid) {
+ curr: ^Node,
+ offset: uintptr,
+}
+
+iterator_head :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T)
+ where intrinsics.type_has_field(T, field_name),
+ intrinsics.type_field_type(T, field_name) == Node {
+ return {list.head, offset_of_by_string(T, field_name)}
+}
+
+iterator_tail :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T)
+ where intrinsics.type_has_field(T, field_name),
+ intrinsics.type_field_type(T, field_name) == Node {
+ return {list.tail, offset_of_by_string(T, field_name)}
+}
+
+iterator_from_node :: proc(node: ^Node, $T: typeid, $field_name: string) -> Iterator(T)
+ where intrinsics.type_has_field(T, field_name),
+ intrinsics.type_field_type(T, field_name) == Node {
+ return {node, offset_of_by_string(T, field_name)}
+}
+
+iterate_next :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
+ node := it.curr
+ if node == nil {
+ return nil, false
+ }
+ it.curr = node.next
+
+ return (^T)(uintptr(node) - it.offset), true
+}
+
+iterate_prev :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
+ node := it.curr
+ if node == nil {
+ return nil, false
+ }
+ it.curr = node.prev
+
+ return (^T)(uintptr(node) - it.offset), true
+} \ No newline at end of file