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 }