package cache import ( "sync" "github.com/elliotchance/orderedmap/v2" ) type RingMapInterface interface { Exists(key string, value interface{}) bool } // Copied from https://github.com/prgsmall/ringmap to use orderedmap v2 type RingMap struct { orderedMap *orderedmap.OrderedMap[string, interface{}] capacity int writeLock sync.RWMutex } func NewRingMap(capacity int) *RingMap { return &RingMap{ orderedMap: orderedmap.NewOrderedMap[string, interface{}](), capacity: capacity, } } // Convenience function to check if key and value already exists // If key and value does not exists, it is added // If key exists with different value, it is replaced with new value func (m *RingMap) Exists(key string, value interface{}) bool { m.writeLock.RLock() el := m.orderedMap.GetElement(key) m.writeLock.RUnlock() exists := el != nil if exists && el.Value != value { m.Delete(key) exists = false } else { m.clearLast() } m.writeLock.Lock() defer m.writeLock.Unlock() m.orderedMap.Set(key, value) return exists } // Get returns the value for a key. If the key does not exist, the second return // parameter will be false and the value will be nil. func (m *RingMap) Get(key string) (interface{}, bool) { m.writeLock.RLock() defer m.writeLock.RUnlock() return m.orderedMap.Get(key) } // Set will set (or replace) a value for a key. If the key was new, then true // will be returned. The returned value will be false if the value was replaced // (even if the value was the same). If a new key is being added and the map is // full, then the front element will be deleted to make room for the new element. func (m *RingMap) Set(key string, value interface{}) bool { _, didExist := m.Get(key) if !didExist { m.clearLast() } m.writeLock.Lock() defer m.writeLock.Unlock() m.orderedMap.Set(key, value) return !didExist } // Put will set a value for a key. If the key already exists, it will be deleted // from and a recreated at the end of the list. If the key was new, then true // will be returned. The returned value will be false if the value was replaced // (even if the value was the same). If a new key is being added and the map is // full, then the front element will be deleted to make room for the new element. func (m *RingMap) Put(key string, value interface{}) bool { _, didExist := m.Get(key) if didExist { m.Delete(key) } else { m.clearLast() } m.writeLock.Lock() defer m.writeLock.Unlock() m.orderedMap.Set(key, value) return !didExist } // GetOrDefault returns the value for a key. If the key does not exist, returns // the default value instead. func (m *RingMap) GetOrDefault(key string, defaultValue interface{}) interface{} { m.writeLock.RLock() defer m.writeLock.RUnlock() return m.orderedMap.GetOrDefault(key, defaultValue) } // Len returns the number of elements in the map. func (m *RingMap) Len() int { m.writeLock.RLock() defer m.writeLock.RUnlock() return m.orderedMap.Len() } // Capacity returns the capacity of the map func (m *RingMap) Capacity() int { m.writeLock.RLock() defer m.writeLock.RUnlock() return m.capacity } // IsFull returns true if the number of elements in the map is Capacity() func (m *RingMap) IsFull() bool { m.writeLock.RLock() defer m.writeLock.RUnlock() return m.orderedMap.Len() == m.capacity } // Keys returns all of the keys in the order they were inserted. If a key was // replaced it will retain the same position. To ensure most recently set keys // are always at the end you must always Delete before Set. func (m *RingMap) Keys() (keys []string) { m.writeLock.RLock() defer m.writeLock.RUnlock() return m.orderedMap.Keys() } // Delete will remove a key from the map. It will return true if the key was // removed (the key did exist). func (m *RingMap) Delete(key string) (didDelete bool) { m.writeLock.Lock() defer m.writeLock.Unlock() return m.orderedMap.Delete(key) } // Front will return the element that is the first (oldest Set element). If // there are no elements this will return nil. func (m *RingMap) Front() *orderedmap.Element[string, interface{}] { m.writeLock.RLock() defer m.writeLock.RUnlock() return m.orderedMap.Front() } // Back will return the element that is the last (most recent Set element). If // there are no elements this will return nil. func (m *RingMap) Back() *orderedmap.Element[string, interface{}] { m.writeLock.RLock() defer m.writeLock.RUnlock() return m.orderedMap.Back() } func (m *RingMap) clearLast() { if m.IsFull() { m.Delete(m.Front().Key) } }