github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/util/adt/map.go (about) 1 package adt 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 7 hamt "github.com/filecoin-project/go-hamt-ipld/v3" 8 "github.com/filecoin-project/go-state-types/abi" 9 "github.com/filecoin-project/go-state-types/cbor" 10 cid "github.com/ipfs/go-cid" 11 cbg "github.com/whyrusleeping/cbor-gen" 12 "golang.org/x/xerrors" 13 ) 14 15 // DefaultHamtOptions specifies default options used to construct Filecoin HAMTs. 16 // Specific HAMT instances may specify additional options, especially the bitwidth. 17 var DefaultHamtOptions = []hamt.Option{ 18 hamt.UseHashFunction(func(input []byte) []byte { 19 res := sha256.Sum256(input) 20 return res[:] 21 }), 22 } 23 24 // Map stores key-value pairs in a HAMT. 25 type Map struct { 26 lastCid cid.Cid 27 root *hamt.Node 28 store Store 29 } 30 31 // AsMap interprets a store as a HAMT-based map with root `r`. 32 // The HAMT is interpreted with branching factor 2^bitwidth. 33 // We could drop this parameter if https://github.com/filecoin-project/go-hamt-ipld/issues/79 is implemented. 34 func AsMap(s Store, root cid.Cid, bitwidth int) (*Map, error) { 35 options := append(DefaultHamtOptions, hamt.UseTreeBitWidth(bitwidth)) 36 nd, err := hamt.LoadNode(s.Context(), s, root, options...) 37 if err != nil { 38 return nil, xerrors.Errorf("failed to load hamt node: %w", err) 39 } 40 41 return &Map{ 42 lastCid: root, 43 root: nd, 44 store: s, 45 }, nil 46 } 47 48 // Creates a new map backed by an empty HAMT. 49 func MakeEmptyMap(s Store, bitwidth int) (*Map, error) { 50 options := append(DefaultHamtOptions, hamt.UseTreeBitWidth(bitwidth)) 51 nd, err := hamt.NewNode(s, options...) 52 if err != nil { 53 return nil, err 54 } 55 return &Map{ 56 lastCid: cid.Undef, 57 root: nd, 58 store: s, 59 }, nil 60 } 61 62 // Creates and stores a new empty map, returning its CID. 63 func StoreEmptyMap(s Store, bitwidth int) (cid.Cid, error) { 64 m, err := MakeEmptyMap(s, bitwidth) 65 if err != nil { 66 return cid.Undef, err 67 } 68 return m.Root() 69 } 70 71 // Returns the root cid of underlying HAMT. 72 func (m *Map) Root() (cid.Cid, error) { 73 if err := m.root.Flush(m.store.Context()); err != nil { 74 return cid.Undef, xerrors.Errorf("failed to flush map root: %w", err) 75 } 76 77 c, err := m.store.Put(m.store.Context(), m.root) 78 if err != nil { 79 return cid.Undef, xerrors.Errorf("writing map root object: %w", err) 80 } 81 m.lastCid = c 82 83 return c, nil 84 } 85 86 // Put adds value `v` with key `k` to the hamt store. 87 func (m *Map) Put(k abi.Keyer, v cbor.Marshaler) error { 88 if err := m.root.Set(m.store.Context(), k.Key(), v); err != nil { 89 return xerrors.Errorf("failed to set key %v value %v in node %v: %w", k.Key(), v, m.lastCid, err) 90 } 91 return nil 92 } 93 94 // Get retrieves the value at `k` into `out`, if the `k` is present and `out` is non-nil. 95 // Returns whether the key was found. 96 func (m *Map) Get(k abi.Keyer, out cbor.Unmarshaler) (bool, error) { 97 if found, err := m.root.Find(m.store.Context(), k.Key(), out); err != nil { 98 return false, xerrors.Errorf("failed to get key %v in node %v: %w", m.lastCid, k.Key(), err) 99 } else { 100 return found, nil 101 } 102 } 103 104 // Has checks for the existence of a key without deserializing its value. 105 func (m *Map) Has(k abi.Keyer) (bool, error) { 106 if found, err := m.root.Find(m.store.Context(), k.Key(), nil); err != nil { 107 return false, xerrors.Errorf("failed to check key %v in node %v: %w", m.lastCid, k.Key(), err) 108 } else { 109 return found, nil 110 } 111 } 112 113 // Sets key key `k` to value `v` iff the key is not already present. 114 func (m *Map) PutIfAbsent(k abi.Keyer, v cbor.Marshaler) (bool, error) { 115 if modified, err := m.root.SetIfAbsent(m.store.Context(), k.Key(), v); err != nil { 116 return false, xerrors.Errorf("failed to set key %v value %v in node %v: %w", k.Key(), v, m.lastCid, err) 117 } else { 118 return modified, nil 119 } 120 } 121 122 // Removes the value at `k` from the hamt store, if it exists. 123 // Returns whether the key was previously present. 124 func (m *Map) TryDelete(k abi.Keyer) (bool, error) { 125 if found, err := m.root.Delete(m.store.Context(), k.Key()); err != nil { 126 return false, xerrors.Errorf("failed to delete key %v in node %v: %v", k.Key(), m.root, err) 127 } else { 128 return found, nil 129 } 130 } 131 132 // Removes the value at `k` from the hamt store, expecting it to exist. 133 func (m *Map) Delete(k abi.Keyer) error { 134 if found, err := m.root.Delete(m.store.Context(), k.Key()); err != nil { 135 return xerrors.Errorf("failed to delete key %v in node %v: %v", k.Key(), m.root, err) 136 } else if !found { 137 return xerrors.Errorf("no such key %v to delete in node %v", k.Key(), m.root) 138 } 139 return nil 140 } 141 142 // Iterates all entries in the map, deserializing each value in turn into `out` and then 143 // calling a function with the corresponding key. 144 // Iteration halts if the function returns an error. 145 // If the output parameter is nil, deserialization is skipped. 146 func (m *Map) ForEach(out cbor.Unmarshaler, fn func(key string) error) error { 147 return m.root.ForEach(m.store.Context(), func(k string, val *cbg.Deferred) error { 148 if out != nil { 149 // Why doesn't hamt.ForEach() just return the value as bytes? 150 err := out.UnmarshalCBOR(bytes.NewReader(val.Raw)) 151 if err != nil { 152 return err 153 } 154 } 155 return fn(k) 156 }) 157 } 158 159 // Collects all the keys from the map into a slice of strings. 160 func (m *Map) CollectKeys() (out []string, err error) { 161 err = m.ForEach(nil, func(key string) error { 162 out = append(out, key) 163 return nil 164 }) 165 return 166 } 167 168 // Retrieves the value for `k` into the 'out' unmarshaler (if non-nil), and removes the entry. 169 // Returns a boolean indicating whether the element was previously in the map. 170 func (m *Map) Pop(k abi.Keyer, out cbor.Unmarshaler) (bool, error) { 171 key := k.Key() 172 if found, err := m.root.Find(m.store.Context(), key, out); err != nil || !found { 173 return found, err 174 } 175 176 if found, err := m.root.Delete(m.store.Context(), key); err != nil { 177 return false, err 178 } else if !found { 179 return false, xerrors.Errorf("failed to find key %v to delete", k.Key()) 180 } 181 return true, nil 182 }