github.com/protolambda/zssz@v0.1.5/types/ssz_list.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	. "github.com/protolambda/zssz/dec"
     6  	. "github.com/protolambda/zssz/enc"
     7  	. "github.com/protolambda/zssz/htr"
     8  	"github.com/protolambda/zssz/merkle"
     9  	. "github.com/protolambda/zssz/pretty"
    10  	"github.com/protolambda/zssz/util/ptrutil"
    11  	"reflect"
    12  	"unsafe"
    13  )
    14  
    15  type SSZList struct {
    16  	alloc         ptrutil.SliceAllocationFn
    17  	elemMemSize   uintptr
    18  	elemSSZ       SSZ
    19  	fixedElemSize uint64
    20  	limit         uint64
    21  	byteLimit     uint64
    22  	maxFuzzLen    uint64
    23  }
    24  
    25  func NewSSZList(factory SSZFactoryFn, typ reflect.Type) (*SSZList, error) {
    26  	if typ.Kind() != reflect.Slice {
    27  		return nil, fmt.Errorf("typ %v is not a dynamic-length array", typ)
    28  	}
    29  	limit, err := ReadListLimit(typ)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	elemTyp := typ.Elem()
    35  
    36  	elemSSZ, err := factory(elemTyp)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	var fixedElemSize, byteLimit uint64
    41  	if elemSSZ.IsFixed() {
    42  		fixedElemSize = elemSSZ.FixedLen()
    43  		byteLimit = limit * elemSSZ.FixedLen()
    44  	} else {
    45  		fixedElemSize = BYTES_PER_LENGTH_OFFSET
    46  		byteLimit = limit * elemSSZ.MaxLen()
    47  	}
    48  	res := &SSZList{
    49  		alloc:         ptrutil.MakeSliceAllocFn(typ),
    50  		elemMemSize:   elemTyp.Size(),
    51  		elemSSZ:       elemSSZ,
    52  		fixedElemSize: fixedElemSize,
    53  		limit:         limit,
    54  		byteLimit:     byteLimit,
    55  		maxFuzzLen:    8 + (limit * elemSSZ.FuzzMaxLen()),
    56  	}
    57  	return res, nil
    58  }
    59  
    60  func (v *SSZList) FuzzMinLen() uint64 {
    61  	return 8
    62  }
    63  
    64  func (v *SSZList) FuzzMaxLen() uint64 {
    65  	return v.maxFuzzLen
    66  }
    67  
    68  func (v *SSZList) MinLen() uint64 {
    69  	return 0
    70  }
    71  
    72  func (v *SSZList) MaxLen() uint64 {
    73  	return v.byteLimit
    74  }
    75  
    76  func (v *SSZList) FixedLen() uint64 {
    77  	return 0
    78  }
    79  
    80  func (v *SSZList) IsFixed() bool {
    81  	return false
    82  }
    83  
    84  func (v *SSZList) SizeOf(p unsafe.Pointer) uint64 {
    85  	sh := ptrutil.ReadSliceHeader(p)
    86  	if v.elemSSZ.IsFixed() {
    87  		return uint64(sh.Len) * v.fixedElemSize
    88  	} else {
    89  		out := uint64(sh.Len) * BYTES_PER_LENGTH_OFFSET
    90  		memOffset := uintptr(0)
    91  		for i := 0; i < sh.Len; i++ {
    92  			elemPtr := unsafe.Pointer(uintptr(sh.Data) + memOffset)
    93  			memOffset += v.elemMemSize
    94  			out += v.elemSSZ.SizeOf(elemPtr)
    95  		}
    96  		return out
    97  	}
    98  }
    99  
   100  func (v *SSZList) Encode(eb *EncodingWriter, p unsafe.Pointer) error {
   101  	sh := ptrutil.ReadSliceHeader(p)
   102  	if v.elemSSZ.IsFixed() {
   103  		return EncodeFixedSeries(v.elemSSZ.Encode, uint64(sh.Len), v.elemMemSize, eb, sh.Data)
   104  	} else {
   105  		return EncodeVarSeries(v.elemSSZ.Encode, v.elemSSZ.SizeOf, uint64(sh.Len), v.elemMemSize, eb, sh.Data)
   106  	}
   107  }
   108  
   109  func (v *SSZList) decodeFuzzmode(dr *DecodingReader, p unsafe.Pointer) error {
   110  	x, err := dr.ReadUint64()
   111  	if err != nil {
   112  		return err
   113  	}
   114  	span := dr.GetBytesSpan()
   115  	if span > v.maxFuzzLen-8 {
   116  		span = v.maxFuzzLen - 8
   117  	}
   118  	length := uint64(0)
   119  	if span != 0 {
   120  		length = (x % span) / v.elemSSZ.FuzzMinLen()
   121  	}
   122  	contentsPtr := v.alloc.MutateLenOrAllocNew(p, length)
   123  	if v.elemSSZ.IsFixed() {
   124  		return DecodeFixedSeries(v.elemSSZ.Decode, length, v.elemMemSize, dr, contentsPtr)
   125  	} else {
   126  		return DecodeVarSeriesFuzzMode(v.elemSSZ, length, v.elemMemSize, dr, contentsPtr)
   127  	}
   128  }
   129  
   130  func (v *SSZList) decode(dr *DecodingReader, p unsafe.Pointer) error {
   131  	if v.elemSSZ.IsFixed() {
   132  		return DecodeFixedSlice(v.elemSSZ.Decode, v.elemSSZ.FixedLen(), dr.GetBytesSpan(), v.limit, v.alloc, v.elemMemSize, dr, p)
   133  	} else {
   134  		// still pass the fixed length of the element, but just to check a minimum length requirement.
   135  		return DecodeVarSlice(v.elemSSZ.Decode, v.elemSSZ.FixedLen(), dr.GetBytesSpan(), v.limit, v.alloc, v.elemMemSize, dr, p)
   136  	}
   137  }
   138  
   139  func (v *SSZList) Decode(dr *DecodingReader, p unsafe.Pointer) error {
   140  	if dr.IsFuzzMode() {
   141  		return v.decodeFuzzmode(dr, p)
   142  	} else {
   143  		return v.decode(dr, p)
   144  	}
   145  }
   146  
   147  func (v *SSZList) DryCheck(dr *DecodingReader) error {
   148  	if v.elemSSZ.IsFixed() {
   149  		return DryCheckFixedSlice(v.elemSSZ.DryCheck, v.elemSSZ.FixedLen(), dr.GetBytesSpan(), v.limit, dr)
   150  	} else {
   151  		return DryCheckVarSlice(v.elemSSZ.DryCheck, v.elemSSZ.FixedLen(), dr.GetBytesSpan(), v.limit, dr)
   152  	}
   153  }
   154  
   155  func (v *SSZList) HashTreeRoot(h MerkleFn, p unsafe.Pointer) [32]byte {
   156  	elemHtr := v.elemSSZ.HashTreeRoot
   157  	elemSize := v.elemMemSize
   158  	sh := ptrutil.ReadSliceHeader(p)
   159  	leaf := func(i uint64) []byte {
   160  		r := elemHtr(h, unsafe.Pointer(uintptr(sh.Data)+(elemSize*uintptr(i))))
   161  		return r[:]
   162  	}
   163  	return h.MixIn(merkle.Merkleize(h, uint64(sh.Len), v.limit, leaf), uint64(sh.Len))
   164  }
   165  
   166  func (v *SSZList) Pretty(indent uint32, w *PrettyWriter, p unsafe.Pointer) {
   167  	sh := ptrutil.ReadSliceHeader(p)
   168  	length := uint64(sh.Len)
   169  	w.WriteIndent(indent)
   170  	w.Write("[\n")
   171  	CallSeries(func(i uint64, p unsafe.Pointer) {
   172  		v.elemSSZ.Pretty(indent+1, w, p)
   173  		if i == length-1 {
   174  			w.Write("\n")
   175  		} else {
   176  			w.Write(",\n")
   177  		}
   178  	}, length, v.elemMemSize, sh.Data)
   179  	w.WriteIndent(indent)
   180  	w.Write("]")
   181  }