This repository has been archived by the owner on Jun 6, 2023. It is now read-only.
/
map.go
139 lines (121 loc) · 3.92 KB
/
map.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
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
package adt
import (
"bytes"
"crypto/sha256"
hamt "github.com/filecoin-project/go-hamt-ipld/v2"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/cbor"
cid "github.com/ipfs/go-cid"
errors "github.com/pkg/errors"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
)
// Branching factor of the HAMT.
// This value has been empirically chosen, but the optimal value for maps with different mutation profiles
// may differ, in which case we can expose it for configuration.
const hamtBitwidth = 5
// HamtOptions specifies all the options used to construct filecoin HAMTs.
var HamtOptions = []hamt.Option{
hamt.UseTreeBitWidth(hamtBitwidth),
hamt.UseHashFunction(func(input []byte) []byte {
res := sha256.Sum256(input)
return res[:]
}),
}
// Map stores key-value pairs in a HAMT.
type Map struct {
lastCid cid.Cid
root *hamt.Node
store Store
}
// AsMap interprets a store as a HAMT-based map with root `r`.
func AsMap(s Store, r cid.Cid) (*Map, error) {
nd, err := hamt.LoadNode(s.Context(), s, r, HamtOptions...)
if err != nil {
return nil, xerrors.Errorf("failed to load hamt node: %w", err)
}
return &Map{
lastCid: r,
root: nd,
store: s,
}, nil
}
// Creates a new map backed by an empty HAMT and flushes it to the store.
func MakeEmptyMap(s Store) *Map {
nd := hamt.NewNode(s, HamtOptions...)
return &Map{
lastCid: cid.Undef,
root: nd,
store: s,
}
}
// Returns the root cid of underlying HAMT.
func (m *Map) Root() (cid.Cid, error) {
if err := m.root.Flush(m.store.Context()); err != nil {
return cid.Undef, xerrors.Errorf("failed to flush map root: %w", err)
}
c, err := m.store.Put(m.store.Context(), m.root)
if err != nil {
return cid.Undef, xerrors.Errorf("writing map root object: %w", err)
}
m.lastCid = c
return c, nil
}
// Put adds value `v` with key `k` to the hamt store.
func (m *Map) Put(k abi.Keyer, v cbor.Marshaler) error {
if err := m.root.Set(m.store.Context(), k.Key(), v); err != nil {
return errors.Wrapf(err, "map put failed set in node %v with key %v value %v", m.lastCid, k.Key(), v)
}
return nil
}
// Get puts the value at `k` into `out`.
func (m *Map) Get(k abi.Keyer, out cbor.Unmarshaler) (bool, error) {
if err := m.root.Find(m.store.Context(), k.Key(), out); err != nil {
if err == hamt.ErrNotFound {
return false, nil
}
return false, errors.Wrapf(err, "map get failed find in node %v with key %v", m.lastCid, k.Key())
}
return true, nil
}
// Has checks for the existance of a key without deserializing its value.
func (m *Map) Has(k abi.Keyer) (bool, error) {
if _, err := m.root.FindRaw(m.store.Context(), k.Key()); err != nil {
if err == hamt.ErrNotFound {
return false, nil
}
return false, errors.Wrapf(err, "map get failed find in node %v with key %v", m.lastCid, k.Key())
}
return true, nil
}
// Delete removes the value at `k` from the hamt store.
func (m *Map) Delete(k abi.Keyer) error {
if err := m.root.Delete(m.store.Context(), k.Key()); err != nil {
return errors.Wrapf(err, "map delete failed in node %v key %v", m.root, k.Key())
}
return nil
}
// Iterates all entries in the map, deserializing each value in turn into `out` and then
// calling a function with the corresponding key.
// Iteration halts if the function returns an error.
// If the output parameter is nil, deserialization is skipped.
func (m *Map) ForEach(out cbor.Unmarshaler, fn func(key string) error) error {
return m.root.ForEach(m.store.Context(), func(k string, val interface{}) error {
if out != nil {
// Why doesn't hamt.ForEach() just return the value as bytes?
err := out.UnmarshalCBOR(bytes.NewReader(val.(*cbg.Deferred).Raw))
if err != nil {
return err
}
}
return fn(k)
})
}
// Collects all the keys from the map into a slice of strings.
func (m *Map) CollectKeys() (out []string, err error) {
err = m.ForEach(nil, func(key string) error {
out = append(out, key)
return nil
})
return
}