This repository has been archived on 2022-07-30. You can view files and clone it, but cannot push or open issues/pull-requests.
mdb/itemmap.go

107 lines
2.3 KiB
Go

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))
}