package mdb import ( "math/rand" "sync" "sync/atomic" "git.crumpington.com/private/mdb/keyedmutex" "git.crumpington.com/private/mdb/kvstore" ) // Implements ItemMap and ItemUniqueIndex interfaces. type itemMap[T any] struct { primary bool kv *kvstore.KV collection string idLock keyedmutex.KeyedMutex[uint64] mapLock sync.Mutex m map[uint64]*T getID func(*T) uint64 maxID uint64 } func newItemMap[T any](kv *kvstore.KV, collection string, getID func(*T) uint64) *itemMap[T] { m := &itemMap[T]{ primary: kv.Primary(), kv: kv, collection: collection, idLock: keyedmutex.New[uint64](), m: map[uint64]*T{}, getID: getID, } kv.Iterate(collection, func(id uint64, data []byte) { item := decode[T](data) if id > m.maxID { m.maxID = id } m.m[id] = item }) return m } func (m *itemMap[T]) load(src map[uint64]*T) error { // No-op: The itemmap is the source for loading all other indices. return nil } // ---------------------------------------------------------------------------- func (m *itemMap[T]) Get(id uint64) (*T, bool) { return m.mapGet(id) } // Should hold item lock when calling. func (idx *itemMap[T]) insert(item *T) { id := idx.getID(item) if idx.primary { idx.kv.Store(idx.collection, id, encode(item)) } idx.mapSet(id, item) } // Should hold item lock when calling. old and new MUST have the same ID. func (idx *itemMap[T]) update(old, new *T) { idx.insert(new) } // Should hold item lock when calling. func (idx *itemMap[T]) delete(item *T) { id := idx.getID(item) if idx.primary { idx.kv.Delete(idx.collection, id) } idx.mapDelete(id) } // ---------------------------------------------------------------------------- func (idx *itemMap[T]) mapSet(id uint64, item *T) { idx.mapLock.Lock() idx.m[id] = item idx.mapLock.Unlock() } func (idx *itemMap[T]) mapDelete(id uint64) { idx.mapLock.Lock() delete(idx.m, id) idx.mapLock.Unlock() } func (idx *itemMap[T]) mapGet(id uint64) (*T, bool) { idx.mapLock.Lock() item, ok := idx.m[id] idx.mapLock.Unlock() return item, ok } // ---------------------------------------------------------------------------- func (idx *itemMap[T]) nextID() uint64 { n := 1 + rand.Int63n(256) return atomic.AddUint64(&idx.maxID, uint64(n)) }