// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved. // See the file LICENSE for licensing terms. package linked // ListElement is an element of a linked list. type ListElement[T any] struct { next, prev *ListElement[T] list *List[T] Value T } // Next returns the next element or nil. func (e *ListElement[T]) Next() *ListElement[T] { if p := e.next; e.list != nil && p != &e.list.sentinel { return p } return nil } // Prev returns the previous element or nil. func (e *ListElement[T]) Prev() *ListElement[T] { if p := e.prev; e.list != nil && p != &e.list.sentinel { return p } return nil } // List implements a doubly linked list with a sentinel node. // // See: https://en.wikipedia.org/wiki/Doubly_linked_list // // This datastructure is designed to be an almost complete drop-in replacement // for the standard library's "container/list". // // The primary design change is to remove all memory allocations from the list // definition. This allows these lists to be used in performance critical paths. // Additionally the zero value is not useful. Lists must be created with the // NewList method. type List[T any] struct { // sentinel is only used as a placeholder to avoid complex nil checks. // sentinel.Value is never used. sentinel ListElement[T] length int } // NewList creates a new doubly linked list. func NewList[T any]() *List[T] { l := &List[T]{} l.sentinel.next = &l.sentinel l.sentinel.prev = &l.sentinel l.sentinel.list = l return l } // Len returns the number of elements in l. func (l *List[_]) Len() int { return l.length } // Front returns the element at the front of l. // If l is empty, nil is returned. func (l *List[T]) Front() *ListElement[T] { if l.length == 0 { return nil } return l.sentinel.next } // Back returns the element at the back of l. // If l is empty, nil is returned. func (l *List[T]) Back() *ListElement[T] { if l.length == 0 { return nil } return l.sentinel.prev } // Remove removes e from l if e is in l. func (l *List[T]) Remove(e *ListElement[T]) { if e.list != l { return } e.prev.next = e.next e.next.prev = e.prev e.next = nil e.prev = nil e.list = nil l.length-- } // PushFront inserts e at the front of l. // If e is already in a list, l is not modified. func (l *List[T]) PushFront(e *ListElement[T]) { l.insertAfter(e, &l.sentinel) } // PushBack inserts e at the back of l. // If e is already in a list, l is not modified. func (l *List[T]) PushBack(e *ListElement[T]) { l.insertAfter(e, l.sentinel.prev) } // InsertBefore inserts e immediately before location. // If e is already in a list, l is not modified. // If location is not in l, l is not modified. func (l *List[T]) InsertBefore(e *ListElement[T], location *ListElement[T]) { if location.list == l { l.insertAfter(e, location.prev) } } // InsertAfter inserts e immediately after location. // If e is already in a list, l is not modified. // If location is not in l, l is not modified. func (l *List[T]) InsertAfter(e *ListElement[T], location *ListElement[T]) { if location.list == l { l.insertAfter(e, location) } } // MoveToFront moves e to the front of l. // If e is not in l, l is not modified. func (l *List[T]) MoveToFront(e *ListElement[T]) { // If e is already at the front of l, there is nothing to do. if e != l.sentinel.next { l.moveAfter(e, &l.sentinel) } } // MoveToBack moves e to the back of l. // If e is not in l, l is not modified. func (l *List[T]) MoveToBack(e *ListElement[T]) { l.moveAfter(e, l.sentinel.prev) } // MoveBefore moves e immediately before location. // If the elements are equal or not in l, the list is not modified. func (l *List[T]) MoveBefore(e, location *ListElement[T]) { // Don't introduce a cycle by moving an element before itself. if e != location { l.moveAfter(e, location.prev) } } // MoveAfter moves e immediately after location. // If the elements are equal or not in l, the list is not modified. func (l *List[T]) MoveAfter(e, location *ListElement[T]) { l.moveAfter(e, location) } func (l *List[T]) insertAfter(e, location *ListElement[T]) { if e.list != nil { // Don't insert an element that is already in a list return } e.prev = location e.next = location.next e.prev.next = e e.next.prev = e e.list = l l.length++ } func (l *List[T]) moveAfter(e, location *ListElement[T]) { if e.list != l || location.list != l || e == location { // Don't modify an element that is in a different list. // Don't introduce a cycle by moving an element after itself. return } e.prev.next = e.next e.next.prev = e.prev e.prev = location e.next = location.next e.prev.next = e e.next.prev = e } // PushFront inserts v into a new element at the front of l. func PushFront[T any](l *List[T], v T) { l.PushFront(&ListElement[T]{ Value: v, }) } // PushBack inserts v into a new element at the back of l. func PushBack[T any](l *List[T], v T) { l.PushBack(&ListElement[T]{ Value: v, }) } // InsertBefore inserts v into a new element immediately before location. // If location is not in l, l is not modified. func InsertBefore[T any](l *List[T], v T, location *ListElement[T]) { l.InsertBefore( &ListElement[T]{ Value: v, }, location, ) } // InsertAfter inserts v into a new element immediately after location. // If location is not in l, l is not modified. func InsertAfter[T any](l *List[T], v T, location *ListElement[T]) { l.InsertAfter( &ListElement[T]{ Value: v, }, location, ) }