forked from ethersphere/proximity-order-trie
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.go
More file actions
156 lines (136 loc) · 3.86 KB
/
Copy pathindex.go
File metadata and controls
156 lines (136 loc) · 3.86 KB
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package pot
import (
"context"
"fmt"
"github.com/ethersphere/proximity-order-trie/pkg/elements"
)
// Index represents a mutable pot
type Index struct {
mode elements.Mode // mode
read chan elements.Node // hands out current root for reads
write chan elements.Node // hands out current root for writes and locks
root chan elements.Node // channel for new roots
quit chan struct{} // closing this channel signals quit
}
// New constructs a new mutable pot
func New(mode elements.Mode) (*Index, error) {
idx := &Index{
mode: mode,
read: make(chan elements.Node),
write: make(chan elements.Node),
root: make(chan elements.Node),
quit: make(chan struct{}),
}
root := idx.mode.New()
go idx.muxProcess(root)
return idx, nil
}
// NewReference constructs a new mutable pot from a reference
func NewReference(ctx context.Context, mode elements.Mode, ref []byte) (*Index, error) {
idx := &Index{
mode: mode,
read: make(chan elements.Node),
write: make(chan elements.Node),
root: make(chan elements.Node),
quit: make(chan struct{}),
}
root, loaded, err := idx.mode.Load(ctx, ref)
if err != nil {
return nil, err
}
if !loaded {
return nil, fmt.Errorf("root not loaded from persistent storage")
}
go idx.muxProcess(root)
return idx, nil
}
// muxProcess is a forever loop serving as a locking mechanism for the pot index
// it allows only a single write operation at a time but multiple reads
func (idx *Index) muxProcess(root elements.Node) {
write := idx.write
quit := idx.quit
for {
select {
case <-quit:
return
case idx.read <- root: //
case write <- root: // write locks the pot for writes
write = nil // locks the pot until root updated
quit = nil // disallow quit until write finish
case root = <-idx.root:
write = idx.write
quit = idx.quit
}
}
}
// Add inserts an entry to the mutable pot
func (idx *Index) Add(ctx context.Context, e elements.Entry) error {
return idx.Update(ctx, e.Key(), &e )
}
// Delete removes the entry at the given key from the mutable pot
func (idx *Index) Delete(ctx context.Context, k []byte) error {
return idx.Update(ctx, k, nil)
}
// Update exposes the pot update function more directly
func (idx *Index) Update(ctx context.Context, k []byte, e *elements.Entry) error {
var root elements.Node
// get the pot root and capture the write lock
select {
case <-ctx.Done():
return ctx.Err()
case root = <-idx.write:
}
update, err := idx.mode.Update(ctx, root, k, e)
if err != nil {
return err
}
if update != nil {
root = update
}
// update with new pot root and release the write lock
select {
case <-ctx.Done():
return ctx.Err()
case idx.root <- root:
}
return nil
}
// Find retrieves the entry at the given key from the mutable pot or gives elements.ErrNotFound
func (idx *Index) Find(ctx context.Context, k []byte) (elements.Entry, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case root := <-idx.read:
return elements.Find(ctx, root, k, idx.mode)
}
}
// Iterate wraps the underlying pot's iterator
func (idx *Index) Iterate(ctx context.Context, p, k []byte, f func(elements.Entry) (stop bool, err error)) error {
return elements.Iterate(ctx, elements.NewAt(0, <-idx.read), p, k, idx.mode, f)
}
// Size returns the size (number of entries) of the pot
func (idx *Index) Size() int {
root := <-idx.read
if root == nil {
return 0
}
return root.Size()
}
// Save calls the mode specific save method for the root node
func (idx *Index) Save(ctx context.Context) ([]byte, error) {
root := <-idx.read
if root.Empty() {
return nil, fmt.Errorf("root node is nil")
}
return idx.mode.Save(ctx)
}
// Close quits the process loop and closes the mode
func (idx *Index) Close() error {
close(idx.quit)
return nil
}
// String pretty prints the current state of the pot
func (idx *Index) String() string {
root := <-idx.read
return elements.NewAt(0, root).String()
}