// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved. // See the file LICENSE for licensing terms. package linkedhashmap import ( "container/list" "sync" ) var _ LinkedHashmap[int, struct{}] = (*linkedHashmap[int, struct{}])(nil) // Hashmap provides an O(1) mapping from a comparable key to any value. // Comparable is defined by https://golang.org/ref/spec#Comparison_operators. type Hashmap[K, V any] interface { Put(key K, val V) Get(key K) (val V, exists bool) Delete(key K) (deleted bool) Len() int } // LinkedHashmap is a hashmap that keeps track of the oldest pairing an the // newest pairing. type LinkedHashmap[K, V any] interface { Hashmap[K, V] Oldest() (key K, val V, exists bool) Newest() (key K, val V, exists bool) NewIterator() Iter[K, V] } type keyValue[K, V any] struct { key K value V } type linkedHashmap[K comparable, V any] struct { lock sync.RWMutex entryMap map[K]*list.Element entryList *list.List } func New[K comparable, V any]() LinkedHashmap[K, V] { return &linkedHashmap[K, V]{ entryMap: make(map[K]*list.Element), entryList: list.New(), } } func (lh *linkedHashmap[K, V]) Put(key K, val V) { lh.lock.Lock() defer lh.lock.Unlock() lh.put(key, val) } func (lh *linkedHashmap[K, V]) Get(key K) (V, bool) { lh.lock.RLock() defer lh.lock.RUnlock() return lh.get(key) } func (lh *linkedHashmap[K, V]) Delete(key K) bool { lh.lock.Lock() defer lh.lock.Unlock() return lh.delete(key) } func (lh *linkedHashmap[K, V]) Len() int { lh.lock.RLock() defer lh.lock.RUnlock() return lh.len() } func (lh *linkedHashmap[K, V]) Oldest() (K, V, bool) { lh.lock.RLock() defer lh.lock.RUnlock() return lh.oldest() } func (lh *linkedHashmap[K, V]) Newest() (K, V, bool) { lh.lock.RLock() defer lh.lock.RUnlock() return lh.newest() } func (lh *linkedHashmap[K, V]) put(key K, value V) { if e, ok := lh.entryMap[key]; ok { lh.entryList.MoveToBack(e) e.Value = keyValue[K, V]{ key: key, value: value, } } else { lh.entryMap[key] = lh.entryList.PushBack(keyValue[K, V]{ key: key, value: value, }) } } func (lh *linkedHashmap[K, V]) get(key K) (V, bool) { if e, ok := lh.entryMap[key]; ok { kv := e.Value.(keyValue[K, V]) return kv.value, true } return zero[V](), false } func (lh *linkedHashmap[K, V]) delete(key K) bool { e, ok := lh.entryMap[key] if ok { lh.entryList.Remove(e) delete(lh.entryMap, key) } return ok } func (lh *linkedHashmap[K, V]) len() int { return len(lh.entryMap) } func (lh *linkedHashmap[K, V]) oldest() (K, V, bool) { if val := lh.entryList.Front(); val != nil { kv := val.Value.(keyValue[K, V]) return kv.key, kv.value, true } return zero[K](), zero[V](), false } func (lh *linkedHashmap[K, V]) newest() (K, V, bool) { if val := lh.entryList.Back(); val != nil { kv := val.Value.(keyValue[K, V]) return kv.key, kv.value, true } return zero[K](), zero[V](), false } func (lh *linkedHashmap[K, V]) NewIterator() Iter[K, V] { return &iterator[K, V]{lh: lh} }