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  }