github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/market/set_multimap.go (about)

     1  package market
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/filecoin-project/go-state-types/abi"
     7  	cid "github.com/ipfs/go-cid"
     8  	"github.com/pkg/errors"
     9  	cbg "github.com/whyrusleeping/cbor-gen"
    10  	"golang.org/x/xerrors"
    11  
    12  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    13  )
    14  
    15  type SetMultimap struct {
    16  	mp            *adt.Map
    17  	store         adt.Store
    18  	innerBitwidth int
    19  }
    20  
    21  // Interprets a store as a HAMT-based map of HAMT-based sets with root `r`.
    22  // Both inner and outer HAMTs are interpreted with branching factor 2^bitwidth.
    23  func AsSetMultimap(s adt.Store, r cid.Cid, outerBitwidth, innerBitwidth int) (*SetMultimap, error) {
    24  	m, err := adt.AsMap(s, r, outerBitwidth)
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	return &SetMultimap{mp: m, store: s, innerBitwidth: innerBitwidth}, nil
    29  }
    30  
    31  // Creates a new map backed by an empty HAMT and flushes it to the store.
    32  // Both inner and outer HAMTs have branching factor 2^bitwidth.
    33  func MakeEmptySetMultimap(s adt.Store, bitwidth int) (*SetMultimap, error) {
    34  	m, err := adt.MakeEmptyMap(s, bitwidth)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return &SetMultimap{mp: m, store: s, innerBitwidth: bitwidth}, nil
    39  }
    40  
    41  // Writes a new empty map to the store and returns its CID.
    42  func StoreEmptySetMultimap(s adt.Store, bitwidth int) (cid.Cid, error){
    43  	mm, err := MakeEmptySetMultimap(s, bitwidth)
    44  	if err != nil {
    45  		return cid.Undef, err
    46  	}
    47  	return mm.Root()
    48  }
    49  
    50  // Returns the root cid of the underlying HAMT.
    51  func (mm *SetMultimap) Root() (cid.Cid, error) {
    52  	return mm.mp.Root()
    53  }
    54  
    55  func (mm *SetMultimap) Put(epoch abi.ChainEpoch, v abi.DealID) error {
    56  	// Load the hamt under key, or initialize a new empty one if not found.
    57  	k := abi.UIntKey(uint64(epoch))
    58  	set, found, err := mm.get(k)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	if !found {
    63  		set, err = adt.MakeEmptySet(mm.store, mm.innerBitwidth)
    64  		if err != nil {
    65  			return err
    66  		}
    67  	}
    68  
    69  	// Add to the set.
    70  	if err = set.Put(dealKey(v)); err != nil {
    71  		return errors.Wrapf(err, "failed to add key to set %v", epoch)
    72  	}
    73  
    74  	src, err := set.Root()
    75  	if err != nil {
    76  		return xerrors.Errorf("failed to flush set root: %w", err)
    77  	}
    78  	// Store the new set root under key.
    79  	newSetRoot := cbg.CborCid(src)
    80  	err = mm.mp.Put(k, &newSetRoot)
    81  	if err != nil {
    82  		return errors.Wrapf(err, "failed to store set")
    83  	}
    84  	return nil
    85  }
    86  
    87  func (mm *SetMultimap) PutMany(epoch abi.ChainEpoch, vs []abi.DealID) error {
    88  	// Load the hamt under key, or initialize a new empty one if not found.
    89  	k := abi.UIntKey(uint64(epoch))
    90  	set, found, err := mm.get(k)
    91  	if err != nil {
    92  		return err
    93  	}
    94  	if !found {
    95  		set, err = adt.MakeEmptySet(mm.store, mm.innerBitwidth)
    96  		if err != nil {
    97  			return err
    98  		}
    99  	}
   100  
   101  	// Add to the set.
   102  	for _, v := range vs {
   103  		if err = set.Put(dealKey(v)); err != nil {
   104  			return errors.Wrapf(err, "failed to add key to set %v", epoch)
   105  		}
   106  	}
   107  
   108  	src, err := set.Root()
   109  	if err != nil {
   110  		return xerrors.Errorf("failed to flush set root: %w", err)
   111  	}
   112  	// Store the new set root under key.
   113  	newSetRoot := cbg.CborCid(src)
   114  	err = mm.mp.Put(k, &newSetRoot)
   115  	if err != nil {
   116  		return errors.Wrapf(err, "failed to store set")
   117  	}
   118  	return nil
   119  }
   120  
   121  // Removes all values for a key.
   122  func (mm *SetMultimap) RemoveAll(key abi.ChainEpoch) error {
   123  	if _, err := mm.mp.TryDelete(abi.UIntKey(uint64(key))); err != nil {
   124  		return xerrors.Errorf("failed to delete set key %v: %w", key, err)
   125  	}
   126  	return nil
   127  }
   128  
   129  // Iterates all entries for a key, iteration halts if the function returns an error.
   130  func (mm *SetMultimap) ForEach(epoch abi.ChainEpoch, fn func(id abi.DealID) error) error {
   131  	set, found, err := mm.get(abi.UIntKey(uint64(epoch)))
   132  	if err != nil {
   133  		return err
   134  	}
   135  	if found {
   136  		return set.ForEach(func(k string) error {
   137  			v, err := parseDealKey(k)
   138  			if err != nil {
   139  				return err
   140  			}
   141  			return fn(v)
   142  		})
   143  	}
   144  	return nil
   145  }
   146  
   147  func (mm *SetMultimap) get(key abi.Keyer) (*adt.Set, bool, error) {
   148  	var setRoot cbg.CborCid
   149  	found, err := mm.mp.Get(key, &setRoot)
   150  	if err != nil {
   151  		return nil, false, errors.Wrapf(err, "failed to load set key %v", key)
   152  	}
   153  	var set *adt.Set
   154  	if found {
   155  		set, err = adt.AsSet(mm.store, cid.Cid(setRoot), mm.innerBitwidth)
   156  		if err != nil {
   157  			return nil, false, err
   158  		}
   159  	}
   160  	return set, found, nil
   161  }
   162  
   163  func dealKey(e abi.DealID) abi.Keyer {
   164  	return abi.UIntKey(uint64(e))
   165  }
   166  
   167  func parseDealKey(s string) (abi.DealID, error) {
   168  	key, err := abi.ParseUIntKey(s)
   169  	return abi.DealID(key), err
   170  }
   171  
   172  func init() {
   173  	// Check that DealID is indeed an unsigned integer to confirm that dealKey is making the right interpretation.
   174  	var e abi.DealID
   175  	if reflect.TypeOf(e).Kind() != reflect.Uint64 {
   176  		panic("incorrect sector number encoding")
   177  	}
   178  }