mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 19:32:26 -06:00 
			
		
		
		
	* [feature] Read + Write tombstones for deleted Actors * copyTombstone * update to use resultcache instead of old ttl cache Signed-off-by: kim <grufwub@gmail.com> * update go-cache library to fix result cache capacity / ordering bugs Signed-off-by: kim <grufwub@gmail.com> * bump go-cache/v3 to v3.1.6 to fix bugs Signed-off-by: kim <grufwub@gmail.com> * switch on status code * better explain ErrGone reasoning Signed-off-by: kim <grufwub@gmail.com> Co-authored-by: kim <grufwub@gmail.com>
		
			
				
	
	
		
			289 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			289 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)
 | 
						|
}
 |