mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 13:42:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			154 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			154 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | package maps | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"fmt" | ||
|  | 	"reflect" | ||
|  | ) | ||
|  | 
 | ||
|  | // LRU provides an ordered hashmap implementation that keeps elements ordered according to last recently used (hence, LRU). | ||
|  | type LRUMap[K comparable, V any] struct { | ||
|  | 	ordered[K, V] | ||
|  | 	size int | ||
|  | } | ||
|  | 
 | ||
|  | // NewLRU returns a new instance of LRUMap with given initializing length and maximum capacity. | ||
|  | func NewLRU[K comparable, V any](len, cap int) *LRUMap[K, V] { | ||
|  | 	m := new(LRUMap[K, V]) | ||
|  | 	m.Init(len, cap) | ||
|  | 	return m | ||
|  | } | ||
|  | 
 | ||
|  | // Init will initialize this map with initial length and maximum capacity. | ||
|  | func (m *LRUMap[K, V]) Init(len, cap int) { | ||
|  | 	if cap <= 0 { | ||
|  | 		panic("lru cap must be greater than zero") | ||
|  | 	} else if m.pool != nil { | ||
|  | 		panic("lru map already initialized") | ||
|  | 	} | ||
|  | 	m.ordered.hmap = make(map[K]*elem[K, V], len) | ||
|  | 	m.ordered.pool = allocElems[K, V](len) | ||
|  | 	m.size = cap | ||
|  | } | ||
|  | 
 | ||
|  | // Get will fetch value for given key from map, in the process pushing it to the front of the map. Returns false if not found. | ||
|  | func (m *LRUMap[K, V]) Get(key K) (V, bool) { | ||
|  | 	if elem, ok := m.hmap[key]; ok { | ||
|  | 		// Ensure safe | ||
|  | 		m.write_check() | ||
|  | 
 | ||
|  | 		// Unlink elem from list | ||
|  | 		m.list.Unlink(elem) | ||
|  | 
 | ||
|  | 		// Push to front of list | ||
|  | 		m.list.PushFront(elem) | ||
|  | 
 | ||
|  | 		return elem.V, true | ||
|  | 	} | ||
|  | 	var z V // zero value | ||
|  | 	return z, false | ||
|  | } | ||
|  | 
 | ||
|  | // Add will add the given key-value pair to the map, pushing them to the front of the map. Returns false if already exists. Evicts old at maximum capacity. | ||
|  | func (m *LRUMap[K, V]) Add(key K, value V) bool { | ||
|  | 	return m.AddWithHook(key, value, nil) | ||
|  | } | ||
|  | 
 | ||
|  | // AddWithHook performs .Add() but passing any evicted entry to given hook function. | ||
|  | func (m *LRUMap[K, V]) AddWithHook(key K, value V, evict func(K, V)) bool { | ||
|  | 	// Ensure safe | ||
|  | 	m.write_check() | ||
|  | 
 | ||
|  | 	// Look for existing elem | ||
|  | 	elem, ok := m.hmap[key] | ||
|  | 	if ok { | ||
|  | 		return false | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if m.list.len >= m.size { | ||
|  | 		// We're at capacity, sir! | ||
|  | 		// Pop current tail elem | ||
|  | 		elem = m.list.PopTail() | ||
|  | 
 | ||
|  | 		if evict != nil { | ||
|  | 			// Pass to evict hook | ||
|  | 			evict(elem.K, elem.V) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Delete key from map | ||
|  | 		delete(m.hmap, elem.K) | ||
|  | 	} else { | ||
|  | 		// Allocate elem | ||
|  | 		elem = m.alloc() | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Set elem | ||
|  | 	elem.K = key | ||
|  | 	elem.V = value | ||
|  | 
 | ||
|  | 	// Add element map entry | ||
|  | 	m.hmap[key] = elem | ||
|  | 
 | ||
|  | 	// Push to front of list | ||
|  | 	m.list.PushFront(elem) | ||
|  | 	return true | ||
|  | } | ||
|  | 
 | ||
|  | // Set will ensure that given key-value pair exists in the map, by either adding new or updating existing, pushing them to the front of the map. Evicts old at maximum capacity. | ||
|  | func (m *LRUMap[K, V]) Set(key K, value V) { | ||
|  | 	m.SetWithHook(key, value, nil) | ||
|  | } | ||
|  | 
 | ||
|  | // SetWithHook performs .Set() but passing any evicted entry to given hook function. | ||
|  | func (m *LRUMap[K, V]) SetWithHook(key K, value V, evict func(K, V)) { | ||
|  | 	// Ensure safe | ||
|  | 	m.write_check() | ||
|  | 
 | ||
|  | 	// Look for existing elem | ||
|  | 	elem, ok := m.hmap[key] | ||
|  | 
 | ||
|  | 	if ok { | ||
|  | 		// Unlink elem from list | ||
|  | 		m.list.Unlink(elem) | ||
|  | 
 | ||
|  | 		// Update existing | ||
|  | 		elem.V = value | ||
|  | 	} else { | ||
|  | 		if m.list.len >= m.size { | ||
|  | 			// We're at capacity, sir! | ||
|  | 			// Pop current tail elem | ||
|  | 			elem = m.list.PopTail() | ||
|  | 
 | ||
|  | 			if evict != nil { | ||
|  | 				// Pass to evict hook | ||
|  | 				evict(elem.K, elem.V) | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// Delete key from map | ||
|  | 			delete(m.hmap, elem.K) | ||
|  | 		} else { | ||
|  | 			// Allocate elem | ||
|  | 			elem = m.alloc() | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Set elem | ||
|  | 		elem.K = key | ||
|  | 		elem.V = value | ||
|  | 
 | ||
|  | 		// Add element map entry | ||
|  | 		m.hmap[key] = elem | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Push to front of list | ||
|  | 	m.list.PushFront(elem) | ||
|  | } | ||
|  | 
 | ||
|  | // Cap returns the maximum capacity of this LRU map. | ||
|  | func (m *LRUMap[K, V]) Cap() int { | ||
|  | 	return m.size | ||
|  | } | ||
|  | 
 | ||
|  | // Format implements fmt.Formatter, allowing performant string formatting of map. | ||
|  | func (m *LRUMap[K, V]) Format(state fmt.State, verb rune) { | ||
|  | 	m.format(reflect.TypeOf(m), state, verb) | ||
|  | } |