github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/io/size.go (about) 1 package io 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 // This structure is used to calculate the wire size of the serializable 9 // structure. It's an io.Writer that doesn't do any real writes, but instead 10 // just counts the number of bytes to be written. 11 type counterWriter struct { 12 counter int 13 } 14 15 // Write implements the io.Writer interface. 16 func (cw *counterWriter) Write(p []byte) (int, error) { 17 n := len(p) 18 cw.counter += n 19 return n, nil 20 } 21 22 // getVarIntSize returns the size in number of bytes of a variable integer. 23 // (reference: GetVarSize(int value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs) 24 func getVarIntSize(value int) int { 25 var size uintptr 26 27 if value < 0xFD { 28 size = 1 // unit8 29 } else if value <= 0xFFFF { 30 size = 3 // byte + uint16 31 } else { 32 size = 5 // byte + uint32 33 } 34 return int(size) 35 } 36 37 // GetVarSize returns the number of bytes in a serialized variable. It supports ints/uints (estimating 38 // them with variable-length encoding that is used in Neo), strings, pointers to Serializable structures, 39 // slices and arrays of ints/uints or Serializable structures. It's similar to GetVarSize<T>(this T[] value) 40 // used in C#, but differs in that it also supports things like Uint160 or Uint256. 41 func GetVarSize(value any) int { 42 v := reflect.ValueOf(value) 43 switch v.Kind() { 44 case reflect.String: 45 valueSize := len([]byte(v.String())) 46 return getVarIntSize(valueSize) + valueSize 47 case reflect.Int, 48 reflect.Int8, 49 reflect.Int16, 50 reflect.Int32, 51 reflect.Int64: 52 return getVarIntSize(int(v.Int())) 53 case reflect.Uint, 54 reflect.Uint8, 55 reflect.Uint16, 56 reflect.Uint32, 57 reflect.Uint64: 58 return getVarIntSize(int(v.Uint())) 59 case reflect.Ptr: 60 vser, ok := v.Interface().(Serializable) 61 if !ok { 62 panic("unable to calculate GetVarSize for a non-Serializable pointer") 63 } 64 cw := counterWriter{} 65 w := NewBinWriterFromIO(&cw) 66 vser.EncodeBinary(w) 67 if w.Err != nil { 68 panic(fmt.Sprintf("error serializing %s: %s", reflect.TypeOf(value), w.Err.Error())) 69 } 70 return cw.counter 71 case reflect.Slice, reflect.Array: 72 valueLength := v.Len() 73 valueSize := 0 74 75 if valueLength != 0 { 76 switch reflect.ValueOf(value).Index(0).Interface().(type) { 77 case Serializable: 78 for i := 0; i < valueLength; i++ { 79 valueSize += GetVarSize(v.Index(i).Interface()) 80 } 81 case uint8, int8: 82 valueSize = valueLength 83 case uint16, int16: 84 valueSize = valueLength * 2 85 case uint32, int32: 86 valueSize = valueLength * 4 87 case uint64, int64: 88 valueSize = valueLength * 8 89 } 90 } 91 92 return getVarIntSize(valueLength) + valueSize 93 default: 94 panic(fmt.Sprintf("unable to calculate GetVarSize, %s", reflect.TypeOf(value))) 95 } 96 }