dlist.nx
buildroot/runtime/dlist.nx
about
dlist.nx -- intrusive doubly-linked list.
Each node carries prev/next pointers; list has a sentinel node
(heap-allocated) whose prev/next thread the ring. Intrusive:
the node pointer is the handle the caller holds, not a wrapper
around T. Trade-offs vs std::list-style wrapped list:
+ O(1) remove given just a node pointer (no search)
+ one user struct can participate in multiple lists (LRU +
pending + free) with separate DNode fields
- caller allocates nodes
Design note: NishiLang v1 doesn't allow `&struct.field` for
inline fields, so DList stores the sentinel as a separately-
allocated *DNode rather than an embedded DNode. Small extra
alloc at list-construction time only.
Used by:
- LRU caches (move-to-front, evict tail)
- scheduler run queues
- free-block chains in allocators
- order-preserving map iteration (map + sibling dlist)
Invariants:
DL1 Empty list: sentinel.next == sentinel.prev == sentinel.
DL2 dlist_insert_after + dlist_remove maintain
n.next.prev == n for all n.
DL3 dlist_remove does NOT free the node -- caller owns
lifetime.
dependencies 1 imports · 1 importers
imports: syscalls.nx
imported by: lru.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| 32 | struct DNode { |
| 38 | struct DList { |
consts
| none |
functions
| 43 | func dlist_new() -> *DList { |
| 56 | func dnode_new(data: *u8) -> *DNode { |
| 65 | func dlist_empty(L: *DList) -> i64 { |
| 72 | func dlist_insert_after(pos: *DNode, n: *DNode) -> i64 { |
| 81 | func dlist_push_front(L: *DList, n: *DNode) -> i64 { |
| 86 | func dlist_push_back(L: *DList, n: *DNode) -> i64 { |
| 91 | func dlist_remove(n: *DNode) -> i64 { |
| 98 | func dlist_move_to_front(L: *DList, n: *DNode) -> i64 { |
| 105 | func dlist_pop_front(L: *DList) -> *DNode { |
| 113 | func dlist_pop_back(L: *DList) -> *DNode { |
| 121 | func dlist_begin(L: *DList) -> *DNode {
called by 1: main |
| 125 | func dlist_end(L: *DList) -> *DNode { |
| 130 | func main() -> i64 { |