mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:32:25 -05: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)
 | |
| }
 |