forked from extern/smegmesh
02dfd73e08
- Separated synchronisation calls into independent processes - Commented code for submission
198 lines
3.6 KiB
Go
198 lines
3.6 KiB
Go
// crdt provides go implementations for crdts
|
|
package crdt
|
|
|
|
import (
|
|
"cmp"
|
|
"sync"
|
|
)
|
|
|
|
// Bucket: bucket represents a value in the grow only map
|
|
type Bucket[D any] struct {
|
|
Vector uint64
|
|
Contents D
|
|
Gravestone bool
|
|
}
|
|
|
|
// GMap is a set that can only grow in size
|
|
type GMap[K cmp.Ordered, D any] struct {
|
|
lock sync.RWMutex
|
|
contents map[uint64]Bucket[D]
|
|
clock *VectorClock[K]
|
|
}
|
|
|
|
// Put: put a new entry in the grow-only-map
|
|
func (g *GMap[K, D]) Put(key K, value D) {
|
|
g.lock.Lock()
|
|
|
|
clock := g.clock.IncrementClock()
|
|
|
|
g.contents[g.clock.hashFunc(key)] = Bucket[D]{
|
|
Vector: clock,
|
|
Contents: value,
|
|
}
|
|
|
|
g.lock.Unlock()
|
|
}
|
|
|
|
// Contains: returns whether or not the key is contained
|
|
// in the g-map
|
|
func (g *GMap[K, D]) Contains(key K) bool {
|
|
return g.contains(g.clock.hashFunc(key))
|
|
}
|
|
|
|
func (g *GMap[K, D]) contains(key uint64) bool {
|
|
g.lock.RLock()
|
|
|
|
_, ok := g.contents[key]
|
|
|
|
g.lock.RUnlock()
|
|
|
|
return ok
|
|
}
|
|
|
|
func (g *GMap[K, D]) put(key uint64, b Bucket[D]) {
|
|
g.lock.Lock()
|
|
|
|
if g.contents[key].Vector < b.Vector {
|
|
g.contents[key] = b
|
|
}
|
|
|
|
g.lock.Unlock()
|
|
}
|
|
|
|
func (g *GMap[K, D]) get(key uint64) Bucket[D] {
|
|
g.lock.RLock()
|
|
bucket := g.contents[key]
|
|
g.lock.RUnlock()
|
|
|
|
return bucket
|
|
}
|
|
|
|
// Get: get the value associated with the given key
|
|
func (g *GMap[K, D]) Get(key K) D {
|
|
if !g.Contains(key) {
|
|
var def D
|
|
return def
|
|
}
|
|
|
|
return g.get(g.clock.hashFunc(key)).Contents
|
|
}
|
|
|
|
// Mark: marks the node, this means the status of the node
|
|
// is an undefined state
|
|
func (g *GMap[K, D]) Mark(key K) {
|
|
if !g.Contains(key) {
|
|
return
|
|
}
|
|
|
|
g.lock.Lock()
|
|
bucket := g.contents[g.clock.hashFunc(key)]
|
|
bucket.Gravestone = true
|
|
g.contents[g.clock.hashFunc(key)] = bucket
|
|
g.lock.Unlock()
|
|
}
|
|
|
|
// IsMarked: returns true if the node is marked (in an undefined state)
|
|
func (g *GMap[K, D]) IsMarked(key K) bool {
|
|
marked := false
|
|
|
|
g.lock.RLock()
|
|
|
|
bucket, ok := g.contents[g.clock.hashFunc(key)]
|
|
|
|
if ok {
|
|
marked = bucket.Gravestone
|
|
}
|
|
|
|
g.lock.RUnlock()
|
|
return marked
|
|
}
|
|
|
|
// Keys: return all the keys in the grow-only map
|
|
func (g *GMap[K, D]) Keys() []uint64 {
|
|
g.lock.RLock()
|
|
|
|
contents := make([]uint64, len(g.contents))
|
|
index := 0
|
|
|
|
for key := range g.contents {
|
|
contents[index] = key
|
|
index++
|
|
}
|
|
|
|
g.lock.RUnlock()
|
|
return contents
|
|
}
|
|
|
|
// Save: saves the grow only map
|
|
func (g *GMap[K, D]) Save() map[uint64]Bucket[D] {
|
|
buckets := make(map[uint64]Bucket[D])
|
|
g.lock.RLock()
|
|
|
|
for key, value := range g.contents {
|
|
buckets[key] = value
|
|
}
|
|
|
|
g.lock.RUnlock()
|
|
return buckets
|
|
}
|
|
|
|
// SaveWithKeys: get all the values corresponding with the provided keys
|
|
func (g *GMap[K, D]) SaveWithKeys(keys []uint64) map[uint64]Bucket[D] {
|
|
buckets := make(map[uint64]Bucket[D])
|
|
g.lock.RLock()
|
|
|
|
for _, key := range keys {
|
|
buckets[key] = g.contents[key]
|
|
}
|
|
|
|
g.lock.RUnlock()
|
|
return buckets
|
|
}
|
|
|
|
// GetClock: get all the vector clocks in the g_map
|
|
func (g *GMap[K, D]) GetClock() map[uint64]uint64 {
|
|
clock := make(map[uint64]uint64)
|
|
g.lock.RLock()
|
|
|
|
for key, bucket := range g.contents {
|
|
clock[key] = bucket.Vector
|
|
}
|
|
|
|
g.lock.RUnlock()
|
|
return clock
|
|
}
|
|
|
|
// GetHash: get the hash of the g_map representing its state
|
|
func (g *GMap[K, D]) GetHash() uint64 {
|
|
hash := uint64(0)
|
|
|
|
g.lock.RLock()
|
|
|
|
for _, value := range g.contents {
|
|
hash += value.Vector
|
|
}
|
|
|
|
g.lock.RUnlock()
|
|
return hash
|
|
}
|
|
|
|
// Prune: prune all stale entries
|
|
func (g *GMap[K, D]) Prune() {
|
|
stale := g.clock.getStale()
|
|
g.lock.Lock()
|
|
|
|
for _, outlier := range stale {
|
|
delete(g.contents, outlier)
|
|
}
|
|
|
|
g.lock.Unlock()
|
|
}
|
|
|
|
func NewGMap[K cmp.Ordered, D any](clock *VectorClock[K]) *GMap[K, D] {
|
|
return &GMap[K, D]{
|
|
contents: make(map[uint64]Bucket[D]),
|
|
clock: clock,
|
|
}
|
|
}
|