github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/util/adt/array.go (about) 1 package adt 2 3 import ( 4 "bytes" 5 6 amt "github.com/filecoin-project/go-amt-ipld/v3" 7 8 "github.com/filecoin-project/go-state-types/cbor" 9 cid "github.com/ipfs/go-cid" 10 cbg "github.com/whyrusleeping/cbor-gen" 11 "golang.org/x/xerrors" 12 ) 13 14 var DefaultAmtOptions = []amt.Option{} 15 16 // Array stores a sparse sequence of values in an AMT. 17 type Array struct { 18 root *amt.Root 19 store Store 20 } 21 22 // AsArray interprets a store as an AMT-based array with root `r`. 23 func AsArray(s Store, r cid.Cid, bitwidth int) (*Array, error) { 24 options := append(DefaultAmtOptions, amt.UseTreeBitWidth(uint(bitwidth))) 25 root, err := amt.LoadAMT(s.Context(), s, r, options...) 26 if err != nil { 27 return nil, xerrors.Errorf("failed to root: %w", err) 28 } 29 30 return &Array{ 31 root: root, 32 store: s, 33 }, nil 34 } 35 36 // Creates a new array backed by an empty AMT. 37 func MakeEmptyArray(s Store, bitwidth int) (*Array, error) { 38 options := append(DefaultAmtOptions, amt.UseTreeBitWidth(uint(bitwidth))) 39 root, err := amt.NewAMT(s, options...) 40 if err != nil { 41 return nil, err 42 } 43 return &Array{ 44 root: root, 45 store: s, 46 }, nil 47 } 48 49 // Writes a new empty array to the store, returning its CID. 50 func StoreEmptyArray(s Store, bitwidth int) (cid.Cid, error) { 51 arr, err := MakeEmptyArray(s, bitwidth) 52 if err != nil { 53 return cid.Undef, err 54 } 55 return arr.Root() 56 } 57 58 // Returns the root CID of the underlying AMT. 59 func (a *Array) Root() (cid.Cid, error) { 60 return a.root.Flush(a.store.Context()) 61 } 62 63 // Appends a value to the end of the array. Assumes continuous array. 64 // If the array isn't continuous use Set and a separate counter 65 func (a *Array) AppendContinuous(value cbor.Marshaler) error { 66 if err := a.root.Set(a.store.Context(), a.root.Len(), value); err != nil { 67 return xerrors.Errorf("append failed to set index %v value %v in root %v: %w", a.root.Len(), value, a.root, err) 68 } 69 return nil 70 } 71 72 func (a *Array) Set(i uint64, value cbor.Marshaler) error { 73 if err := a.root.Set(a.store.Context(), i, value); err != nil { 74 return xerrors.Errorf("failed to set index %v value %v in root %v: %w", i, value, a.root, err) 75 } 76 return nil 77 } 78 79 // Removes the value at index `i` from the AMT, if it exists. 80 // Returns whether the index was previously present. 81 func (a *Array) TryDelete(i uint64) (bool, error) { 82 if found, err := a.root.Delete(a.store.Context(), i); err != nil { 83 return false, xerrors.Errorf("array delete failed to delete index %v in root %v: %w", i, a.root, err) 84 } else { 85 return found, nil 86 } 87 } 88 89 // Removes the value at index `i` from the AMT, expecting it to exist. 90 func (a *Array) Delete(i uint64) error { 91 if found, err := a.root.Delete(a.store.Context(), i); err != nil { 92 return xerrors.Errorf("failed to delete index %v in root %v: %w", i, a.root, err) 93 } else if !found { 94 return xerrors.Errorf("no such index %v in root %v to delete: %w", i, a.root, err) 95 } 96 return nil 97 } 98 99 func (a *Array) BatchDelete(ix []uint64, strict bool) error { 100 if _, err := a.root.BatchDelete(a.store.Context(), ix, strict); err != nil { 101 return xerrors.Errorf("failed to batch delete keys %v: %w", ix, err) 102 } 103 return nil 104 } 105 106 // Iterates all entries in the array, deserializing each value in turn into `out` and then calling a function. 107 // Iteration halts if the function returns an error. 108 // If the output parameter is nil, deserialization is skipped. 109 func (a *Array) ForEach(out cbor.Unmarshaler, fn func(i int64) error) error { 110 return a.root.ForEach(a.store.Context(), func(k uint64, val *cbg.Deferred) error { 111 if out != nil { 112 if deferred, ok := out.(*cbg.Deferred); ok { 113 // fast-path deferred -> deferred to avoid re-decoding. 114 *deferred = *val 115 } else if err := out.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { 116 return err 117 } 118 } 119 return fn(int64(k)) 120 }) 121 } 122 123 func (a *Array) Length() uint64 { 124 return a.root.Len() 125 } 126 127 // Get retrieves array element into the 'out' unmarshaler, returning a boolean 128 // indicating whether the element was found in the array 129 func (a *Array) Get(k uint64, out cbor.Unmarshaler) (bool, error) { 130 if found, err := a.root.Get(a.store.Context(), k, out); err != nil { 131 return false, xerrors.Errorf("failed to get index %v in root %v: %w", k, a.root, err) 132 } else { 133 return found, nil 134 } 135 } 136 137 // Retrieves an array value into the 'out' unmarshaler (if non-nil), and removes the entry. 138 // Returns a boolean indicating whether the element was previously in the array. 139 func (a *Array) Pop(k uint64, out cbor.Unmarshaler) (bool, error) { 140 if found, err := a.root.Get(a.store.Context(), k, out); err != nil { 141 return false, xerrors.Errorf("failed to get index %v in root %v: %w", k, a.root, err) 142 } else if !found { 143 return false, nil 144 } 145 146 if found, err := a.root.Delete(a.store.Context(), k); err != nil { 147 return false, xerrors.Errorf("failed to delete index %v in root %v: %w", k, a.root, err) 148 } else if !found { 149 return false, xerrors.Errorf("can't find index %v to delete in root %v", k, a.root) 150 } 151 return true, nil 152 }