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  }