github.com/MetalBlockchain/metalgo@v1.11.9/utils/linked/list.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package linked 5 6 // ListElement is an element of a linked list. 7 type ListElement[T any] struct { 8 next, prev *ListElement[T] 9 list *List[T] 10 Value T 11 } 12 13 // Next returns the next element or nil. 14 func (e *ListElement[T]) Next() *ListElement[T] { 15 if p := e.next; e.list != nil && p != &e.list.sentinel { 16 return p 17 } 18 return nil 19 } 20 21 // Prev returns the previous element or nil. 22 func (e *ListElement[T]) Prev() *ListElement[T] { 23 if p := e.prev; e.list != nil && p != &e.list.sentinel { 24 return p 25 } 26 return nil 27 } 28 29 // List implements a doubly linked list with a sentinel node. 30 // 31 // See: https://en.wikipedia.org/wiki/Doubly_linked_list 32 // 33 // This datastructure is designed to be an almost complete drop-in replacement 34 // for the standard library's "container/list". 35 // 36 // The primary design change is to remove all memory allocations from the list 37 // definition. This allows these lists to be used in performance critical paths. 38 // Additionally the zero value is not useful. Lists must be created with the 39 // NewList method. 40 type List[T any] struct { 41 // sentinel is only used as a placeholder to avoid complex nil checks. 42 // sentinel.Value is never used. 43 sentinel ListElement[T] 44 length int 45 } 46 47 // NewList creates a new doubly linked list. 48 func NewList[T any]() *List[T] { 49 l := &List[T]{} 50 l.sentinel.next = &l.sentinel 51 l.sentinel.prev = &l.sentinel 52 l.sentinel.list = l 53 return l 54 } 55 56 // Len returns the number of elements in l. 57 func (l *List[_]) Len() int { 58 return l.length 59 } 60 61 // Front returns the element at the front of l. 62 // If l is empty, nil is returned. 63 func (l *List[T]) Front() *ListElement[T] { 64 if l.length == 0 { 65 return nil 66 } 67 return l.sentinel.next 68 } 69 70 // Back returns the element at the back of l. 71 // If l is empty, nil is returned. 72 func (l *List[T]) Back() *ListElement[T] { 73 if l.length == 0 { 74 return nil 75 } 76 return l.sentinel.prev 77 } 78 79 // Remove removes e from l if e is in l. 80 func (l *List[T]) Remove(e *ListElement[T]) { 81 if e.list != l { 82 return 83 } 84 85 e.prev.next = e.next 86 e.next.prev = e.prev 87 e.next = nil 88 e.prev = nil 89 e.list = nil 90 l.length-- 91 } 92 93 // PushFront inserts e at the front of l. 94 // If e is already in a list, l is not modified. 95 func (l *List[T]) PushFront(e *ListElement[T]) { 96 l.insertAfter(e, &l.sentinel) 97 } 98 99 // PushBack inserts e at the back of l. 100 // If e is already in a list, l is not modified. 101 func (l *List[T]) PushBack(e *ListElement[T]) { 102 l.insertAfter(e, l.sentinel.prev) 103 } 104 105 // InsertBefore inserts e immediately before location. 106 // If e is already in a list, l is not modified. 107 // If location is not in l, l is not modified. 108 func (l *List[T]) InsertBefore(e *ListElement[T], location *ListElement[T]) { 109 if location.list == l { 110 l.insertAfter(e, location.prev) 111 } 112 } 113 114 // InsertAfter inserts e immediately after location. 115 // If e is already in a list, l is not modified. 116 // If location is not in l, l is not modified. 117 func (l *List[T]) InsertAfter(e *ListElement[T], location *ListElement[T]) { 118 if location.list == l { 119 l.insertAfter(e, location) 120 } 121 } 122 123 // MoveToFront moves e to the front of l. 124 // If e is not in l, l is not modified. 125 func (l *List[T]) MoveToFront(e *ListElement[T]) { 126 // If e is already at the front of l, there is nothing to do. 127 if e != l.sentinel.next { 128 l.moveAfter(e, &l.sentinel) 129 } 130 } 131 132 // MoveToBack moves e to the back of l. 133 // If e is not in l, l is not modified. 134 func (l *List[T]) MoveToBack(e *ListElement[T]) { 135 l.moveAfter(e, l.sentinel.prev) 136 } 137 138 // MoveBefore moves e immediately before location. 139 // If the elements are equal or not in l, the list is not modified. 140 func (l *List[T]) MoveBefore(e, location *ListElement[T]) { 141 // Don't introduce a cycle by moving an element before itself. 142 if e != location { 143 l.moveAfter(e, location.prev) 144 } 145 } 146 147 // MoveAfter moves e immediately after location. 148 // If the elements are equal or not in l, the list is not modified. 149 func (l *List[T]) MoveAfter(e, location *ListElement[T]) { 150 l.moveAfter(e, location) 151 } 152 153 func (l *List[T]) insertAfter(e, location *ListElement[T]) { 154 if e.list != nil { 155 // Don't insert an element that is already in a list 156 return 157 } 158 159 e.prev = location 160 e.next = location.next 161 e.prev.next = e 162 e.next.prev = e 163 e.list = l 164 l.length++ 165 } 166 167 func (l *List[T]) moveAfter(e, location *ListElement[T]) { 168 if e.list != l || location.list != l || e == location { 169 // Don't modify an element that is in a different list. 170 // Don't introduce a cycle by moving an element after itself. 171 return 172 } 173 174 e.prev.next = e.next 175 e.next.prev = e.prev 176 177 e.prev = location 178 e.next = location.next 179 e.prev.next = e 180 e.next.prev = e 181 } 182 183 // PushFront inserts v into a new element at the front of l. 184 func PushFront[T any](l *List[T], v T) { 185 l.PushFront(&ListElement[T]{ 186 Value: v, 187 }) 188 } 189 190 // PushBack inserts v into a new element at the back of l. 191 func PushBack[T any](l *List[T], v T) { 192 l.PushBack(&ListElement[T]{ 193 Value: v, 194 }) 195 } 196 197 // InsertBefore inserts v into a new element immediately before location. 198 // If location is not in l, l is not modified. 199 func InsertBefore[T any](l *List[T], v T, location *ListElement[T]) { 200 l.InsertBefore( 201 &ListElement[T]{ 202 Value: v, 203 }, 204 location, 205 ) 206 } 207 208 // InsertAfter inserts v into a new element immediately after location. 209 // If location is not in l, l is not modified. 210 func InsertAfter[T any](l *List[T], v T, location *ListElement[T]) { 211 l.InsertAfter( 212 &ListElement[T]{ 213 Value: v, 214 }, 215 location, 216 ) 217 }