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  }