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  }