-
Notifications
You must be signed in to change notification settings - Fork 0
/
or_set.go
112 lines (101 loc) · 3.2 KB
/
or_set.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package crdt
import (
"github.com/google/uuid"
"golang.org/x/exp/maps"
)
// ORSet is a CRDT set in which specific elements that are added to the set
// are given a unique ID that must be removed explicitly for an element to be
// perceived as not being in the set.
// The types of elements (T) must be set on initialization.
type ORSet[T comparable] struct {
name string
set map[T]map[string]bool
tombstone map[T]map[string]bool
}
// NewORSet constructs an empty ORSet with the associated name.
// It is assumed the name of this specific ORSet uniquely identifies this
// set throughout the cluster.
func NewORSet[T comparable](name string) *ORSet[T] {
orset := new(ORSet[T])
orset.name = name
orset.set = make(map[T]map[string]bool)
orset.tombstone = make(map[T]map[string]bool)
return orset
}
// Add adds the specified value to the set.
// Functionally, this creates a new unique ID and adds it to this element's
// List of currently added IDs.
func (orset *ORSet[T]) Add(value T) {
uniqueId := uuid.NewString()
if uniqueIds, exists := orset.set[value]; exists {
uniqueIds[uniqueId] = true
return
}
uniqueIds := make(map[string]bool)
uniqueIds[uniqueId] = true
orset.set[value] = uniqueIds
}
// Remove removes the specified value from the set.
// Functionally, this adds all unique IDs for the specified value in orset.set
// to orset.tombstone and removes them from orset.set.
func (orset *ORSet[T]) Remove(value T) {
if existingIds, existsInSet := orset.set[value]; existsInSet {
tombstoneIds := make(map[string]bool)
if uniqueIdTombstone, existsInTombstone := orset.tombstone[value]; existsInTombstone {
tombstoneIds = uniqueIdTombstone
}
maps.Copy(tombstoneIds, existingIds)
orset.tombstone[value] = tombstoneIds
delete(orset.set, value)
}
}
// Lookup reports whether T value exists within the set.
func (orset *ORSet[T]) Lookup(value T) bool {
if uniqueIds, exists := orset.set[value]; exists {
return len(uniqueIds) > 0
}
return false
}
// Size returns the number of elements that currently exist within the set.
func (orset *ORSet[T]) Size() int {
return len(orset.set)
}
// Merge performs the following three actions:
// - Add all elements (and their unique IDs) from that.set to orset.set
// - Add all elements (and their unique IDs) from that.tombstone to
// orset.tombstone
// - Remove all elements from orset.set if all their unique IDs exist within
// orset.tombstone
//
// This is an idempotent operation and is a no-op if orset.name != that.name.
func (orset *ORSet[T]) Merge(that *ORSet[T]) {
if orset.name != that.name {
return
}
for value, thatUuids := range that.set {
if uuids, exists := orset.set[value]; exists {
maps.Copy(uuids, thatUuids)
} else {
orset.set[value] = thatUuids
}
}
for value, thatUuids := range that.tombstone {
if uuids, exists := orset.tombstone[value]; exists {
maps.Copy(uuids, thatUuids)
} else {
orset.tombstone[value] = thatUuids
}
}
maps.DeleteFunc(orset.set, func(value T, uuids map[string]bool) bool {
tombstoneUuids, exists := orset.tombstone[value]
if !exists {
return false
}
for uuid := range uuids {
if _, exists := tombstoneUuids[uuid]; !exists {
return false
}
}
return true
})
}