mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-25 13:16:40 +00:00
155 lines
2.6 KiB
Go
155 lines
2.6 KiB
Go
|
package maps
|
||
|
|
||
|
// list is a doubly-linked list containing elemnts with key-value pairs of given generic parameter types.
|
||
|
type list[K comparable, V any] struct {
|
||
|
head *elem[K, V]
|
||
|
tail *elem[K, V]
|
||
|
len int
|
||
|
}
|
||
|
|
||
|
// Index returns the element at index from list.
|
||
|
func (l *list[K, V]) Index(idx int) *elem[K, V] {
|
||
|
switch {
|
||
|
// Idx in first half
|
||
|
case idx < l.len/2:
|
||
|
elem := l.head
|
||
|
for i := 0; i < idx; i++ {
|
||
|
elem = elem.next
|
||
|
}
|
||
|
return elem
|
||
|
|
||
|
// Index in last half
|
||
|
default:
|
||
|
elem := l.tail
|
||
|
for i := l.len - 1; i > idx; i-- {
|
||
|
elem = elem.prev
|
||
|
}
|
||
|
return elem
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// PushFront will push the given element to the front of the list.
|
||
|
func (l *list[K, V]) PushFront(elem *elem[K, V]) {
|
||
|
if l.len == 0 {
|
||
|
// Set new tail + head
|
||
|
l.head = elem
|
||
|
l.tail = elem
|
||
|
|
||
|
// Link elem to itself
|
||
|
elem.next = elem
|
||
|
elem.prev = elem
|
||
|
} else {
|
||
|
oldHead := l.head
|
||
|
|
||
|
// Link to old head
|
||
|
elem.next = oldHead
|
||
|
oldHead.prev = elem
|
||
|
|
||
|
// Link up to tail
|
||
|
elem.prev = l.tail
|
||
|
l.tail.next = elem
|
||
|
|
||
|
// Set new head
|
||
|
l.head = elem
|
||
|
}
|
||
|
|
||
|
// Incr count
|
||
|
l.len++
|
||
|
}
|
||
|
|
||
|
// PushBack will push the given element to the back of the list.
|
||
|
func (l *list[K, V]) PushBack(elem *elem[K, V]) {
|
||
|
if l.len == 0 {
|
||
|
// Set new tail + head
|
||
|
l.head = elem
|
||
|
l.tail = elem
|
||
|
|
||
|
// Link elem to itself
|
||
|
elem.next = elem
|
||
|
elem.prev = elem
|
||
|
} else {
|
||
|
oldTail := l.tail
|
||
|
|
||
|
// Link up to head
|
||
|
elem.next = l.head
|
||
|
l.head.prev = elem
|
||
|
|
||
|
// Link to old tail
|
||
|
elem.prev = oldTail
|
||
|
oldTail.next = elem
|
||
|
|
||
|
// Set new tail
|
||
|
l.tail = elem
|
||
|
}
|
||
|
|
||
|
// Incr count
|
||
|
l.len++
|
||
|
}
|
||
|
|
||
|
// PopTail will pop the current tail of the list, returns nil if empty.
|
||
|
func (l *list[K, V]) PopTail() *elem[K, V] {
|
||
|
if l.len == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
elem := l.tail
|
||
|
l.Unlink(elem)
|
||
|
return elem
|
||
|
}
|
||
|
|
||
|
// Unlink will unlink the given element from the doubly-linked list chain.
|
||
|
func (l *list[K, V]) Unlink(elem *elem[K, V]) {
|
||
|
if l.len <= 1 {
|
||
|
// Drop elem's links
|
||
|
elem.next = nil
|
||
|
elem.prev = nil
|
||
|
|
||
|
// Only elem in list
|
||
|
l.head = nil
|
||
|
l.tail = nil
|
||
|
l.len = 0
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Get surrounding elems
|
||
|
next := elem.next
|
||
|
prev := elem.prev
|
||
|
|
||
|
// Relink chain
|
||
|
next.prev = prev
|
||
|
prev.next = next
|
||
|
|
||
|
switch elem {
|
||
|
// Set new head
|
||
|
case l.head:
|
||
|
l.head = next
|
||
|
|
||
|
// Set new tail
|
||
|
case l.tail:
|
||
|
l.tail = prev
|
||
|
}
|
||
|
|
||
|
// Drop elem's links
|
||
|
elem.next = nil
|
||
|
elem.prev = nil
|
||
|
|
||
|
// Decr count
|
||
|
l.len--
|
||
|
}
|
||
|
|
||
|
// elem represents an element in a doubly-linked list.
|
||
|
type elem[K comparable, V any] struct {
|
||
|
next *elem[K, V]
|
||
|
prev *elem[K, V]
|
||
|
K K
|
||
|
V V
|
||
|
}
|
||
|
|
||
|
// allocElems will allocate a slice of empty elements of length.
|
||
|
func allocElems[K comparable, V any](i int) []*elem[K, V] {
|
||
|
s := make([]*elem[K, V], i)
|
||
|
for i := range s {
|
||
|
s[i] = &elem[K, V]{}
|
||
|
}
|
||
|
return s
|
||
|
}
|