github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/util/adt/multimap.go (about) 1 package adt 2 3 import ( 4 "github.com/filecoin-project/go-state-types/abi" 5 "github.com/filecoin-project/go-state-types/cbor" 6 cid "github.com/ipfs/go-cid" 7 errors "github.com/pkg/errors" 8 cbg "github.com/whyrusleeping/cbor-gen" 9 "golang.org/x/xerrors" 10 ) 11 12 // Multimap stores multiple values per key in a HAMT of AMTs. 13 // The order of insertion of values for each key is retained. 14 type Multimap struct { 15 mp *Map 16 innerBitwidth int 17 } 18 19 // Interprets a store as a HAMT-based map of AMTs with root `r`. 20 // The outer map is interpreted with a branching factor of 2^bitwidth. 21 func AsMultimap(s Store, r cid.Cid, outerBitwidth, innerBitwidth int) (*Multimap, error) { 22 m, err := AsMap(s, r, outerBitwidth) 23 if err != nil { 24 return nil, err 25 } 26 27 return &Multimap{m, innerBitwidth}, nil 28 } 29 30 // Creates a new map backed by an empty HAMT and flushes it to the store. 31 // The outer map has a branching factor of 2^bitwidth. 32 func MakeEmptyMultimap(s Store, outerBitwidth, innerBitwidth int) (*Multimap, error) { 33 m, err := MakeEmptyMap(s, outerBitwidth) 34 if err != nil { 35 return nil, err 36 } 37 return &Multimap{m, innerBitwidth}, nil 38 } 39 40 // Creates and stores a new empty multimap, returning its CID. 41 func StoreEmptyMultimap(store Store, outerBitwidth, innerBitwidth int) (cid.Cid, error) { 42 mmap, err := MakeEmptyMultimap(store, outerBitwidth, innerBitwidth) 43 if err != nil { 44 return cid.Undef, err 45 } 46 return mmap.Root() 47 } 48 49 // Returns the root cid of the underlying HAMT. 50 func (mm *Multimap) Root() (cid.Cid, error) { 51 return mm.mp.Root() 52 } 53 54 // Adds a value for a key. 55 func (mm *Multimap) Add(key abi.Keyer, value cbor.Marshaler) error { 56 // Load the array under key, or initialize a new empty one if not found. 57 array, found, err := mm.Get(key) 58 if err != nil { 59 return err 60 } 61 if !found { 62 array, err = MakeEmptyArray(mm.mp.store, mm.innerBitwidth) 63 if err != nil { 64 return err 65 } 66 } 67 68 // Append to the array. 69 if err = array.AppendContinuous(value); err != nil { 70 return errors.Wrapf(err, "failed to add multimap key %v value %v", key, value) 71 } 72 73 c, err := array.Root() 74 if err != nil { 75 return xerrors.Errorf("failed to flush child array: %w", err) 76 } 77 78 // Store the new array root under key. 79 newArrayRoot := cbg.CborCid(c) 80 err = mm.mp.Put(key, &newArrayRoot) 81 if err != nil { 82 return errors.Wrapf(err, "failed to store multimap values") 83 } 84 return nil 85 } 86 87 // Removes all values for a key. 88 func (mm *Multimap) RemoveAll(key abi.Keyer) error { 89 if _, err := mm.mp.TryDelete(key); err != nil { 90 return errors.Wrapf(err, "failed to delete multimap key %v root %v", key, mm.mp.root) 91 } 92 return nil 93 } 94 95 // Iterates all entries for a key in the order they were inserted, deserializing each value in turn into `out` and then 96 // calling a function. 97 // Iteration halts if the function returns an error. 98 // If the output parameter is nil, deserialization is skipped. 99 func (mm *Multimap) ForEach(key abi.Keyer, out cbor.Unmarshaler, fn func(i int64) error) error { 100 array, found, err := mm.Get(key) 101 if err != nil { 102 return err 103 } 104 if found { 105 return array.ForEach(out, fn) 106 } 107 return nil 108 } 109 110 func (mm *Multimap) ForAll(fn func(k string, arr *Array) error) error { 111 var arrRoot cbg.CborCid 112 if err := mm.mp.ForEach(&arrRoot, func(k string) error { 113 arr, err := AsArray(mm.mp.store, cid.Cid(arrRoot), mm.innerBitwidth) 114 if err != nil { 115 return err 116 } 117 118 return fn(k, arr) 119 }); err != nil { 120 return err 121 } 122 123 return nil 124 } 125 126 func (mm *Multimap) Get(key abi.Keyer) (*Array, bool, error) { 127 var arrayRoot cbg.CborCid 128 found, err := mm.mp.Get(key, &arrayRoot) 129 if err != nil { 130 return nil, false, errors.Wrapf(err, "failed to load multimap key %v", key) 131 } 132 var array *Array 133 if found { 134 array, err = AsArray(mm.mp.store, cid.Cid(arrayRoot), mm.innerBitwidth) 135 if err != nil { 136 return nil, false, xerrors.Errorf("failed to load value %v as an array: %w", key, err) 137 } 138 } 139 return array, found, nil 140 }