mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 07:02:24 -06:00 
			
		
		
		
	
		
			
	
	
		
			290 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			290 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package maps
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"reflect"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"codeberg.org/gruf/go-byteutil"
							 | 
						||
| 
								 | 
							
									"codeberg.org/gruf/go-kv"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ordered provides a common ordered hashmap base, storing order in a doubly-linked list.
							 | 
						||
| 
								 | 
							
								type ordered[K comparable, V any] struct {
							 | 
						||
| 
								 | 
							
									hmap map[K]*elem[K, V]
							 | 
						||
| 
								 | 
							
									list list[K, V]
							 | 
						||
| 
								 | 
							
									pool []*elem[K, V]
							 | 
						||
| 
								 | 
							
									rnly bool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// write_check panics if map is not in a safe-state to write to.
							 | 
						||
| 
								 | 
							
								func (m ordered[K, V]) write_check() {
							 | 
						||
| 
								 | 
							
									if m.rnly {
							 | 
						||
| 
								 | 
							
										panic("map write during read loop")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Has returns whether key exists in map.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) Has(key K) bool {
							 | 
						||
| 
								 | 
							
									_, ok := m.hmap[key]
							 | 
						||
| 
								 | 
							
									return ok
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Delete will delete given key from map, returns false if not found.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) Delete(key K) bool {
							 | 
						||
| 
								 | 
							
									// Ensure safe
							 | 
						||
| 
								 | 
							
									m.write_check()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Look for existing elem
							 | 
						||
| 
								 | 
							
									elem, ok := m.hmap[key]
							 | 
						||
| 
								 | 
							
									if !ok {
							 | 
						||
| 
								 | 
							
										return false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Drop from list
							 | 
						||
| 
								 | 
							
									m.list.Unlink(elem)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Delete from map
							 | 
						||
| 
								 | 
							
									delete(m.hmap, key)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Return to pool
							 | 
						||
| 
								 | 
							
									m.free(elem)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Range passes given function over the requested range of the map.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) Range(start, length int, fn func(int, K, V)) {
							 | 
						||
| 
								 | 
							
									// Disallow writes
							 | 
						||
| 
								 | 
							
									m.rnly = true
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										m.rnly = false
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Nil check
							 | 
						||
| 
								 | 
							
									_ = fn
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									switch end := start + length; {
							 | 
						||
| 
								 | 
							
									// No loop to iterate
							 | 
						||
| 
								 | 
							
									case length == 0:
							 | 
						||
| 
								 | 
							
										if start < 0 || (m.list.len > 0 && start >= m.list.len) {
							 | 
						||
| 
								 | 
							
											panic("index out of bounds")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Step backwards
							 | 
						||
| 
								 | 
							
									case length < 0:
							 | 
						||
| 
								 | 
							
										// Check loop indices are within map bounds
							 | 
						||
| 
								 | 
							
										if end < -1 || start >= m.list.len || m.list.len == 0 {
							 | 
						||
| 
								 | 
							
											panic("index out of bounds")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get starting index elem
							 | 
						||
| 
								 | 
							
										elem := m.list.Index(start)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for i := start; i > end; i-- {
							 | 
						||
| 
								 | 
							
											fn(i, elem.K, elem.V)
							 | 
						||
| 
								 | 
							
											elem = elem.prev
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Step forwards
							 | 
						||
| 
								 | 
							
									case length > 0:
							 | 
						||
| 
								 | 
							
										// Check loop indices are within map bounds
							 | 
						||
| 
								 | 
							
										if start < 0 || end > m.list.len || m.list.len == 0 {
							 | 
						||
| 
								 | 
							
											panic("index out of bounds")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get starting index elem
							 | 
						||
| 
								 | 
							
										elem := m.list.Index(start)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for i := start; i < end; i++ {
							 | 
						||
| 
								 | 
							
											fn(i, elem.K, elem.V)
							 | 
						||
| 
								 | 
							
											elem = elem.next
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// RangeIf passes given function over the requested range of the map. Returns early on 'fn' -> false.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) RangeIf(start, length int, fn func(int, K, V) bool) {
							 | 
						||
| 
								 | 
							
									// Disallow writes
							 | 
						||
| 
								 | 
							
									m.rnly = true
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										m.rnly = false
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Nil check
							 | 
						||
| 
								 | 
							
									_ = fn
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									switch end := start + length; {
							 | 
						||
| 
								 | 
							
									// No loop to iterate
							 | 
						||
| 
								 | 
							
									case length == 0:
							 | 
						||
| 
								 | 
							
										if start < 0 || (m.list.len > 0 && start >= m.list.len) {
							 | 
						||
| 
								 | 
							
											panic("index out of bounds")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Step backwards
							 | 
						||
| 
								 | 
							
									case length < 0:
							 | 
						||
| 
								 | 
							
										// Check loop indices are within map bounds
							 | 
						||
| 
								 | 
							
										if end < -1 || start >= m.list.len || m.list.len == 0 {
							 | 
						||
| 
								 | 
							
											panic("index out of bounds")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get starting index elem
							 | 
						||
| 
								 | 
							
										elem := m.list.Index(start)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for i := start; i > end; i-- {
							 | 
						||
| 
								 | 
							
											if !fn(i, elem.K, elem.V) {
							 | 
						||
| 
								 | 
							
												return
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											elem = elem.prev
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Step forwards
							 | 
						||
| 
								 | 
							
									case length > 0:
							 | 
						||
| 
								 | 
							
										// Check loop indices are within map bounds
							 | 
						||
| 
								 | 
							
										if start < 0 || end > m.list.len || m.list.len == 0 {
							 | 
						||
| 
								 | 
							
											panic("index out of bounds")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get starting index elem
							 | 
						||
| 
								 | 
							
										elem := m.list.Index(start)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for i := start; i < end; i++ {
							 | 
						||
| 
								 | 
							
											if !fn(i, elem.K, elem.V) {
							 | 
						||
| 
								 | 
							
												return
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											elem = elem.next
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Truncate will truncate the map from the back by given amount, passing dropped elements to given function.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) Truncate(sz int, fn func(K, V)) {
							 | 
						||
| 
								 | 
							
									// Check size withing bounds
							 | 
						||
| 
								 | 
							
									if sz > m.list.len {
							 | 
						||
| 
								 | 
							
										panic("index out of bounds")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if fn == nil {
							 | 
						||
| 
								 | 
							
										// move nil check out of loop
							 | 
						||
| 
								 | 
							
										fn = func(K, V) {}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Disallow writes
							 | 
						||
| 
								 | 
							
									m.rnly = true
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										m.rnly = false
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for i := 0; i < sz; i++ {
							 | 
						||
| 
								 | 
							
										// Pop current tail
							 | 
						||
| 
								 | 
							
										elem := m.list.tail
							 | 
						||
| 
								 | 
							
										m.list.Unlink(elem)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Delete from map
							 | 
						||
| 
								 | 
							
										delete(m.hmap, elem.K)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Pass dropped to func
							 | 
						||
| 
								 | 
							
										fn(elem.K, elem.V)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Release to pool
							 | 
						||
| 
								 | 
							
										m.free(elem)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Len returns the current length of the map.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) Len() int {
							 | 
						||
| 
								 | 
							
									return m.list.len
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// format implements fmt.Formatter, allowing performant string formatting of map.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) format(rtype reflect.Type, state fmt.State, verb rune) {
							 | 
						||
| 
								 | 
							
									var (
							 | 
						||
| 
								 | 
							
										kvbuf byteutil.Buffer
							 | 
						||
| 
								 | 
							
										field kv.Field
							 | 
						||
| 
								 | 
							
										vbose bool
							 | 
						||
| 
								 | 
							
									)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									switch {
							 | 
						||
| 
								 | 
							
									// Only handle 'v' verb
							 | 
						||
| 
								 | 
							
									case verb != 'v':
							 | 
						||
| 
								 | 
							
										panic("invalid verb '" + string(verb) + "' for map")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Prefix with type when verbose
							 | 
						||
| 
								 | 
							
									case state.Flag('#'):
							 | 
						||
| 
								 | 
							
										state.Write([]byte(rtype.String()))
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Disallow writes
							 | 
						||
| 
								 | 
							
									m.rnly = true
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										m.rnly = false
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Write map opening brace
							 | 
						||
| 
								 | 
							
									state.Write([]byte{'{'})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if m.list.len > 0 {
							 | 
						||
| 
								 | 
							
										// Preallocate buffer
							 | 
						||
| 
								 | 
							
										kvbuf.Guarantee(64)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Start at index 0
							 | 
						||
| 
								 | 
							
										elem := m.list.head
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for i := 0; i < m.list.len-1; i++ {
							 | 
						||
| 
								 | 
							
											// Append formatted key-val pair to state
							 | 
						||
| 
								 | 
							
											field.K = fmt.Sprint(elem.K)
							 | 
						||
| 
								 | 
							
											field.V = elem.V
							 | 
						||
| 
								 | 
							
											field.AppendFormat(&kvbuf, vbose)
							 | 
						||
| 
								 | 
							
											_, _ = state.Write(kvbuf.B)
							 | 
						||
| 
								 | 
							
											kvbuf.Reset()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Prepare buffer with comma separator
							 | 
						||
| 
								 | 
							
											kvbuf.B = append(kvbuf.B, `, `...)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Jump to next in list
							 | 
						||
| 
								 | 
							
											elem = elem.next
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Append formatted key-val pair to state
							 | 
						||
| 
								 | 
							
										field.K = fmt.Sprint(elem.K)
							 | 
						||
| 
								 | 
							
										field.V = elem.V
							 | 
						||
| 
								 | 
							
										field.AppendFormat(&kvbuf, vbose)
							 | 
						||
| 
								 | 
							
										_, _ = state.Write(kvbuf.B)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Write map closing brace
							 | 
						||
| 
								 | 
							
									state.Write([]byte{'}'})
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Std returns a clone of map's data in the standard library equivalent map type.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) Std() map[K]V {
							 | 
						||
| 
								 | 
							
									std := make(map[K]V, m.list.len)
							 | 
						||
| 
								 | 
							
									for _, elem := range m.hmap {
							 | 
						||
| 
								 | 
							
										std[elem.K] = elem.V
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return std
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// alloc will acquire list element from pool, or allocate new.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) alloc() *elem[K, V] {
							 | 
						||
| 
								 | 
							
									if len(m.pool) == 0 {
							 | 
						||
| 
								 | 
							
										return &elem[K, V]{}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									idx := len(m.pool) - 1
							 | 
						||
| 
								 | 
							
									elem := m.pool[idx]
							 | 
						||
| 
								 | 
							
									m.pool = m.pool[:idx]
							 | 
						||
| 
								 | 
							
									return elem
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// free will reset elem fields and place back in pool.
							 | 
						||
| 
								 | 
							
								func (m *ordered[K, V]) free(elem *elem[K, V]) {
							 | 
						||
| 
								 | 
							
									var (
							 | 
						||
| 
								 | 
							
										zk K
							 | 
						||
| 
								 | 
							
										zv V
							 | 
						||
| 
								 | 
							
									)
							 | 
						||
| 
								 | 
							
									elem.K = zk
							 | 
						||
| 
								 | 
							
									elem.V = zv
							 | 
						||
| 
								 | 
							
									elem.next = nil
							 | 
						||
| 
								 | 
							
									elem.prev = nil
							 | 
						||
| 
								 | 
							
									m.pool = append(m.pool, elem)
							 | 
						||
| 
								 | 
							
								}
							 |