github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/amino/binary_encode.go (about)

     1  package amino
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"reflect"
    10  )
    11  
    12  const beOptionByte = 0x01
    13  
    14  // ----------------------------------------
    15  // cdc.encodeReflectBinary
    16  
    17  /*
    18  This is the main entrypoint for encoding all types in binary form.  This
    19  function calls encodeReflectBinary*, and generally those functions should
    20  only call this one, for all overrides happen here.
    21  
    22  The value may be a nil interface, but not a nil pointer.
    23  
    24  The argument "bare" is ignored when the value is a primitive type, or a
    25  byteslice or bytearray or generally a list type (except for unpacked lists,
    26  which are more like structs).  EncodeByteSlice() is of course byte-length
    27  prefixed, but EncodeTime() is not, as it is a struct.
    28  
    29  For structs and struct-like things like unpacked lists, the "bare" argument
    30  determines whether to include the length-prefix or not.
    31  
    32  NOTE: Unlike encodeReflectJSON, rv may not be a pointer.  This is because the
    33  binary representation of pointers depend on the context.  A nil pointer in the
    34  context of a struct field is represented by its presence or absence in the
    35  encoding bytes (w/ bare=false, which normally would produce 0x00), whereas in
    36  the context of a list, (for amino 1.x anyways, which is constrained by proto3),
    37  nil pointers and non-nil pointers to empty structs have the same representation
    38  (0x00).  This is a Proto3 limitation -- choosing repeated fields as the method
    39  of encoding lists is an unfortunate hack.  Amino2 will resolve this issue.
    40  
    41  The following contracts apply to all similar encode methods.
    42  CONTRACT: rv is not a pointer
    43  CONTRACT: rv is valid.
    44  */
    45  func (cdc *Codec) encodeReflectBinary(w io.Writer, info *TypeInfo, rv reflect.Value,
    46  	fopts FieldOptions, bare bool, options uint64,
    47  ) (err error) {
    48  	if rv.Kind() == reflect.Ptr {
    49  		// Whether to encode nil pointers as 0x00 or not at all depend on the
    50  		// context, so pointers should be handled first by the caller.
    51  		panic("not allowed to be called with a reflect.Ptr")
    52  	}
    53  	if !rv.IsValid() {
    54  		panic("not allowed to be called with invalid / zero Value")
    55  	}
    56  	if printLog {
    57  		fmt.Printf("(E) encodeReflectBinary(info: %v, rv: %#v (%v), fopts: %v)\n",
    58  			info, rv.Interface(), rv.Type(), fopts)
    59  		defer func() {
    60  			fmt.Printf("(E) -> err: %v\n", err)
    61  		}()
    62  	}
    63  
    64  	// Handle the most special case, "well known".
    65  	if info.IsBinaryWellKnownType {
    66  		var ok bool
    67  		ok, err = encodeReflectBinaryWellKnown(w, info, rv, fopts, bare)
    68  		if ok || err != nil {
    69  			return
    70  		}
    71  	}
    72  
    73  	// Handle override if rv implements MarshalAmino.
    74  	if info.IsAminoMarshaler {
    75  		// First, encode rv into repr instance.
    76  		var rrv reflect.Value
    77  		rinfo := info.ReprType
    78  		rrv, err = toReprObject(rv)
    79  		if err != nil {
    80  			return
    81  		}
    82  		// Then, encode the repr instance.
    83  		err = cdc.encodeReflectBinary(w, rinfo, rrv, fopts, bare, options)
    84  		return
    85  	}
    86  
    87  	switch info.Type.Kind() {
    88  	// ----------------------------------------
    89  	// Complex
    90  
    91  	case reflect.Interface:
    92  		err = cdc.encodeReflectBinaryInterface(w, info, rv, fopts, bare)
    93  
    94  	case reflect.Array:
    95  		if info.Type.Elem().Kind() == reflect.Uint8 {
    96  			err = cdc.encodeReflectBinaryByteArray(w, info, rv, fopts)
    97  		} else {
    98  			err = cdc.encodeReflectBinaryList(w, info, rv, fopts, bare)
    99  		}
   100  
   101  	case reflect.Slice:
   102  		if info.Type.Elem().Kind() == reflect.Uint8 {
   103  			err = cdc.encodeReflectBinaryByteSlice(w, info, rv, fopts)
   104  		} else {
   105  			err = cdc.encodeReflectBinaryList(w, info, rv, fopts, bare)
   106  		}
   107  
   108  	case reflect.Struct:
   109  		err = cdc.encodeReflectBinaryStruct(w, info, rv, fopts, bare)
   110  
   111  	// ----------------------------------------
   112  	// Signed
   113  
   114  	case reflect.Int64:
   115  		if fopts.BinFixed64 {
   116  			err = EncodeInt64(w, rv.Int())
   117  		} else {
   118  			err = EncodeVarint(w, rv.Int())
   119  		}
   120  
   121  	case reflect.Int32:
   122  		if fopts.BinFixed32 {
   123  			err = EncodeInt32(w, int32(rv.Int()))
   124  		} else {
   125  			err = EncodeVarint(w, rv.Int())
   126  		}
   127  
   128  	case reflect.Int16:
   129  		err = EncodeVarint(w, rv.Int())
   130  
   131  	case reflect.Int8:
   132  		err = EncodeVarint(w, rv.Int())
   133  
   134  	case reflect.Int:
   135  		if fopts.BinFixed64 {
   136  			err = EncodeInt64(w, rv.Int())
   137  		} else if fopts.BinFixed32 {
   138  			err = EncodeInt32(w, int32(rv.Int()))
   139  		} else {
   140  			err = EncodeVarint(w, rv.Int())
   141  		}
   142  
   143  	// ----------------------------------------
   144  	// Unsigned
   145  
   146  	case reflect.Uint64:
   147  		if fopts.BinFixed64 {
   148  			err = EncodeUint64(w, rv.Uint())
   149  		} else {
   150  			err = EncodeUvarint(w, rv.Uint())
   151  		}
   152  
   153  	case reflect.Uint32:
   154  		if fopts.BinFixed32 {
   155  			err = EncodeUint32(w, uint32(rv.Uint()))
   156  		} else {
   157  			err = EncodeUvarint(w, rv.Uint())
   158  		}
   159  
   160  	case reflect.Uint16:
   161  		err = EncodeUvarint(w, rv.Uint())
   162  
   163  	case reflect.Uint8:
   164  		if options&beOptionByte != 0 {
   165  			err = EncodeByte(w, uint8(rv.Uint()))
   166  		} else {
   167  			err = EncodeUvarint(w, rv.Uint())
   168  		}
   169  
   170  	case reflect.Uint:
   171  		if fopts.BinFixed64 {
   172  			err = EncodeUint64(w, rv.Uint())
   173  		} else if fopts.BinFixed32 {
   174  			err = EncodeUint32(w, uint32(rv.Uint()))
   175  		} else {
   176  			err = EncodeUvarint(w, rv.Uint())
   177  		}
   178  
   179  	// ----------------------------------------
   180  	// Misc
   181  
   182  	case reflect.Bool:
   183  		err = EncodeBool(w, rv.Bool())
   184  
   185  	case reflect.Float64:
   186  		if !fopts.Unsafe {
   187  			err = errors.New("amino float* support requires `amino:\"unsafe\"`")
   188  			return
   189  		}
   190  		err = EncodeFloat64(w, rv.Float())
   191  
   192  	case reflect.Float32:
   193  		if !fopts.Unsafe {
   194  			err = errors.New("amino float* support requires `amino:\"unsafe\"`")
   195  			return
   196  		}
   197  		err = EncodeFloat32(w, float32(rv.Float()))
   198  
   199  	case reflect.String:
   200  		err = EncodeString(w, rv.String())
   201  
   202  	// ----------------------------------------
   203  	// Default
   204  
   205  	default:
   206  		panic(fmt.Sprintf("unsupported type %v", info.Type.Kind()))
   207  	}
   208  
   209  	return err
   210  }
   211  
   212  func (cdc *Codec) encodeReflectBinaryInterface(w io.Writer, iinfo *TypeInfo, rv reflect.Value,
   213  	fopts FieldOptions, bare bool,
   214  ) (err error) {
   215  	if printLog {
   216  		fmt.Println("(e) encodeReflectBinaryInterface")
   217  		defer func() {
   218  			fmt.Printf("(e) -> err: %v\n", err)
   219  		}()
   220  	}
   221  
   222  	// Special case when rv is nil, write nothing or 0x00.
   223  	if rv.IsNil() {
   224  		return writeMaybeBare(w, nil, bare)
   225  	}
   226  
   227  	// Get concrete non-pointer reflect value & type.
   228  	crv := rv.Elem()
   229  	crt := crv.Type()
   230  	dcrv, crvIsPtr, crvIsNilPtr := maybeDerefValue(crv)
   231  	if crvIsPtr && dcrv.Kind() == reflect.Interface {
   232  		// See "MARKER: No interface-pointers" in codec.go
   233  		panic("should not happen")
   234  	}
   235  	if crvIsPtr && crvIsNilPtr {
   236  		panic(fmt.Sprintf("Illegal nil-pointer of type %v for registered interface %v. "+
   237  			"For compatibility with other languages, nil-pointer interface values are forbidden.", dcrv.Type(), iinfo.Type))
   238  	}
   239  
   240  	// Get *TypeInfo for concrete type.
   241  	var cinfo *TypeInfo
   242  	cinfo, err = cdc.getTypeInfoWLock(crt)
   243  	if err != nil {
   244  		return
   245  	}
   246  	if !cinfo.Registered {
   247  		err = fmt.Errorf("cannot encode unregistered concrete type %v", crt)
   248  		return
   249  	}
   250  
   251  	// For Proto3 compatibility, encode interfaces as google.protobuf.Any
   252  	// Write field #1, TypeURL
   253  	buf := bytes.NewBuffer(nil)
   254  	{
   255  		fnum := uint32(1)
   256  		err = encodeFieldNumberAndTyp3(buf, fnum, Typ3ByteLength)
   257  		if err != nil {
   258  			return
   259  		}
   260  		err = EncodeString(buf, cinfo.TypeURL)
   261  		if err != nil {
   262  			return
   263  		}
   264  	}
   265  	// Write field #2, Value, if not empty/default.
   266  	// writeFieldIfNotEmpty() is not a substitute for this slightly different
   267  	// logic here, because we need to enforce that the value is a []byte type
   268  	// as per google.protobuf.Any.
   269  	{
   270  		// google.protobuf.Any values must be a struct, or an unpacked list which
   271  		// is indistinguishable from a struct.
   272  		buf2 := bytes.NewBuffer(nil)
   273  		if !cinfo.IsStructOrUnpacked(fopts) {
   274  			writeEmpty := false
   275  			// Encode with an implicit struct, with a single field with number 1.
   276  			// The type of this implicit field determines whether any
   277  			// length-prefixing happens after the typ3 byte.
   278  			// The second FieldOptions is empty, because this isn't a list of
   279  			// Typ3ByteLength things, so however it is encoded, that option is no
   280  			// longer needed.
   281  			if err = cdc.writeFieldIfNotEmpty(buf2, 1, cinfo, FieldOptions{}, FieldOptions{}, dcrv, writeEmpty); err != nil {
   282  				return
   283  			}
   284  		} else {
   285  			// The passed in BinFieldNum is only relevant for when the type is to
   286  			// be encoded unpacked (elements are Typ3ByteLength).  In that case,
   287  			// encodeReflectBinary will repeat the field number as set here, as if
   288  			// encoded with an implicit struct.
   289  			err = cdc.encodeReflectBinary(buf2, cinfo, dcrv, FieldOptions{BinFieldNum: 1}, true, 0)
   290  			if err != nil {
   291  				return
   292  			}
   293  		}
   294  		bz2 := buf2.Bytes()
   295  		if len(bz2) == 0 || len(bz2) == 1 && bz2[0] == 0x00 {
   296  			// Do not write
   297  		} else {
   298  			// Write
   299  			fnum := uint32(2)
   300  			err = encodeFieldNumberAndTyp3(buf, fnum, Typ3ByteLength)
   301  			if err != nil {
   302  				return
   303  			}
   304  			err = EncodeByteSlice(buf, bz2)
   305  			if err != nil {
   306  				return
   307  			}
   308  		}
   309  	}
   310  
   311  	return writeMaybeBare(w, buf.Bytes(), bare)
   312  }
   313  
   314  func (cdc *Codec) encodeReflectBinaryByteArray(w io.Writer, info *TypeInfo, rv reflect.Value,
   315  	fopts FieldOptions,
   316  ) (err error) {
   317  	ert := info.Type.Elem()
   318  	if ert.Kind() != reflect.Uint8 {
   319  		panic("should not happen")
   320  	}
   321  	length := info.Type.Len()
   322  
   323  	// If rv is an interface, get the elem.
   324  
   325  	// Get byteslice.
   326  	var byteslice []byte
   327  	if rv.CanAddr() {
   328  		byteslice = rv.Slice(0, length).Bytes()
   329  	} else {
   330  		byteslice = make([]byte, length)
   331  		reflect.Copy(reflect.ValueOf(byteslice), rv) // XXX: looks expensive!
   332  	}
   333  
   334  	// Write byte-length prefixed byteslice.
   335  	err = EncodeByteSlice(w, byteslice)
   336  	return
   337  }
   338  
   339  func (cdc *Codec) encodeReflectBinaryList(w io.Writer, info *TypeInfo, rv reflect.Value,
   340  	fopts FieldOptions, bare bool,
   341  ) (err error) {
   342  	if printLog {
   343  		fmt.Println("(e) encodeReflectBinaryList")
   344  		defer func() {
   345  			fmt.Printf("(e) -> err: %v\n", err)
   346  		}()
   347  	}
   348  	ert := info.Type.Elem()
   349  	if ert.Kind() == reflect.Uint8 {
   350  		panic("should not happen")
   351  	}
   352  	einfo, err := cdc.getTypeInfoWLock(ert)
   353  	if err != nil {
   354  		return
   355  	}
   356  
   357  	// Proto3 byte-length prefixing incurs alloc cost on the encoder.
   358  	// Here we incur it for unpacked form for ease of dev.
   359  	buf := bytes.NewBuffer(nil)
   360  
   361  	// If elem is not already a ByteLength type, write in packed form.
   362  	// This is a Proto wart due to Proto backwards compatibility issues.
   363  	// Amino2 will probably migrate to use the List typ3.
   364  	newoptions := uint64(0)
   365  	// Special case for list of (repr) bytes: encode as "bytes".
   366  	if einfo.ReprType.Type.Kind() == reflect.Uint8 {
   367  		newoptions |= beOptionByte
   368  	}
   369  	typ3 := einfo.GetTyp3(fopts)
   370  	if typ3 != Typ3ByteLength || (newoptions&beOptionByte > 0) {
   371  		// Write elems in packed form.
   372  		for i := 0; i < rv.Len(); i++ {
   373  			erv := rv.Index(i)
   374  			// If pointer, get dereferenced element value (or zero).
   375  			if ert.Kind() == reflect.Ptr {
   376  				if erv.IsNil() {
   377  					erv = reflect.New(ert.Elem()).Elem()
   378  				} else {
   379  					erv = erv.Elem()
   380  				}
   381  			}
   382  			// Write the element value.
   383  			err = cdc.encodeReflectBinary(buf, einfo, erv, fopts, false, newoptions)
   384  			if err != nil {
   385  				return
   386  			}
   387  		}
   388  	} else { // typ3 == Typ3ByteLength
   389  		// NOTE: ert is for the element value, while einfo.Type is dereferenced.
   390  		ertIsPointer := ert.Kind() == reflect.Ptr
   391  		ertIsStruct := einfo.Type.Kind() == reflect.Struct
   392  		writeImplicit := isListType(einfo.Type) &&
   393  			einfo.Elem.ReprType.Type.Kind() != reflect.Uint8 &&
   394  			einfo.Elem.ReprType.GetTyp3(fopts) != Typ3ByteLength
   395  
   396  		// Write elems in unpacked form.
   397  		for i := 0; i < rv.Len(); i++ {
   398  			// Write elements as repeated fields of the parent struct.
   399  			err = encodeFieldNumberAndTyp3(buf, fopts.BinFieldNum, Typ3ByteLength)
   400  			if err != nil {
   401  				return
   402  			}
   403  			// Get dereferenced element value and info.
   404  			erv := rv.Index(i)
   405  			if isNonstructDefaultValue(erv) {
   406  				// Special case if:
   407  				//  - erv is a struct pointer and
   408  				//  - field option doesn't have NilElements set
   409  				if ertIsStruct && ertIsPointer && !fopts.NilElements {
   410  					// NOTE: Not sure what to do here, but for future-proofing,
   411  					// we explicitly fail on nil pointers, just like
   412  					// Proto3's Golang client does.
   413  					// This also makes it easier to upgrade to Amino2
   414  					// which would enable the encoding of nil structs.
   415  					return errors.New("nil struct pointers in lists not supported unless nil_elements field tag is also set")
   416  				}
   417  				// Nothing to encode, so the length is 0.
   418  				err = EncodeByte(buf, byte(0x00))
   419  				if err != nil {
   420  					return
   421  				}
   422  			} else {
   423  				// Write the element value as a ByteLength prefixed.
   424  				derv := erv
   425  				if ertIsPointer {
   426  					derv = erv.Elem()
   427  				}
   428  
   429  				// Special case: nested lists.
   430  				// Multidimensional lists (nested inner lists also in unpacked
   431  				// form) are represented as lists of implicit structs.
   432  				if writeImplicit {
   433  					// Write field key for Value field of implicit struct.
   434  					buf2 := new(bytes.Buffer)
   435  					err = encodeFieldNumberAndTyp3(buf2, 1, Typ3ByteLength)
   436  					if err != nil {
   437  						return
   438  					}
   439  					// Write field value of implicit struct to buf2.
   440  					efopts := fopts
   441  					efopts.BinFieldNum = 0 // dontcare
   442  					err = cdc.encodeReflectBinary(buf2, einfo, derv, efopts, false, 0)
   443  					if err != nil {
   444  						return
   445  					}
   446  					// Write implicit struct to buf.
   447  					err = EncodeByteSlice(buf, buf2.Bytes())
   448  					if err != nil {
   449  						return
   450  					}
   451  				} else {
   452  					// General case
   453  					efopts := fopts
   454  					efopts.BinFieldNum = 1
   455  					err = cdc.encodeReflectBinary(buf, einfo, derv, efopts, false, 0)
   456  					if err != nil {
   457  						return
   458  					}
   459  				}
   460  			}
   461  		}
   462  	}
   463  
   464  	return writeMaybeBare(w, buf.Bytes(), bare)
   465  }
   466  
   467  // CONTRACT: info.Type.Elem().Kind() == reflect.Uint8
   468  func (cdc *Codec) encodeReflectBinaryByteSlice(w io.Writer, info *TypeInfo, rv reflect.Value,
   469  	fopts FieldOptions,
   470  ) (err error) {
   471  	if printLog {
   472  		fmt.Println("(e) encodeReflectBinaryByteSlice")
   473  		defer func() {
   474  			fmt.Printf("(e) -> err: %v\n", err)
   475  		}()
   476  	}
   477  	ert := info.Type.Elem()
   478  	if ert.Kind() != reflect.Uint8 {
   479  		panic("should not happen")
   480  	}
   481  
   482  	// Write byte-length prefixed byte-slice.
   483  	byteslice := rv.Bytes()
   484  	err = EncodeByteSlice(w, byteslice)
   485  	return
   486  }
   487  
   488  func (cdc *Codec) encodeReflectBinaryStruct(w io.Writer, info *TypeInfo, rv reflect.Value,
   489  	fopts FieldOptions, bare bool,
   490  ) (err error) {
   491  	if printLog {
   492  		fmt.Println("(e) encodeReflectBinaryBinaryStruct")
   493  		defer func() {
   494  			fmt.Printf("(e) -> err: %v\n", err)
   495  		}()
   496  	}
   497  
   498  	// Proto3 incurs a cost in writing non-root structs.
   499  	// Here we incur it for root structs as well for ease of dev.
   500  	buf := bytes.NewBuffer(nil)
   501  
   502  	for _, field := range info.Fields {
   503  		// Get type info for field.
   504  		finfo := field.TypeInfo
   505  		// Get dereferenced field value and info.
   506  		frv := rv.Field(field.Index)
   507  		dfrv, frvIsPtr, _ := maybeDerefValue(frv)
   508  		if !field.WriteEmpty && isNonstructDefaultValue(frv) {
   509  			// Do not encode default value fields
   510  			// (except when `amino:"write_empty"` is set).
   511  			continue
   512  		}
   513  		// Below, if frv is pointer, it isn't a nil pointer.
   514  		if field.UnpackedList {
   515  			// Write repeated field entries for each list item.
   516  			err = cdc.encodeReflectBinaryList(buf, finfo, dfrv, field.FieldOptions, true)
   517  			if err != nil {
   518  				return
   519  			}
   520  		} else {
   521  			// write empty if explicitly set or if this is a non-nil pointer:
   522  			writeEmpty := field.WriteEmpty || frvIsPtr // (non-nil)
   523  			err = cdc.writeFieldIfNotEmpty(buf, field.BinFieldNum, finfo, fopts, field.FieldOptions, dfrv, writeEmpty)
   524  			if err != nil {
   525  				return
   526  			}
   527  		}
   528  	}
   529  
   530  	return writeMaybeBare(w, buf.Bytes(), bare)
   531  }
   532  
   533  // ----------------------------------------
   534  // Misc.
   535  
   536  // Write field key.
   537  func encodeFieldNumberAndTyp3(w io.Writer, num uint32, typ Typ3) (err error) {
   538  	if (typ & 0xF8) != 0 {
   539  		panic(fmt.Sprintf("invalid Typ3 byte %v", typ))
   540  	}
   541  	if num > (1<<29 - 1) {
   542  		panic(fmt.Sprintf("invalid field number %v", num))
   543  	}
   544  
   545  	// Pack Typ3 and field number.
   546  	value64 := (uint64(num) << 3) | uint64(typ)
   547  
   548  	// Write uvarint value for field and Typ3.
   549  	var buf [10]byte
   550  	n := binary.PutUvarint(buf[:], value64)
   551  	_, err = w.Write(buf[0:n])
   552  	return
   553  }
   554  
   555  func (cdc *Codec) writeFieldIfNotEmpty(
   556  	buf *bytes.Buffer,
   557  	fieldNum uint32,
   558  	finfo *TypeInfo,
   559  	structsFopts FieldOptions, // the wrapping struct's FieldOptions if any
   560  	fieldOpts FieldOptions, // the field's FieldOptions
   561  	derefedVal reflect.Value,
   562  	isWriteEmpty bool,
   563  ) error {
   564  	lBeforeKey := buf.Len()
   565  	// Write field key (number and type).
   566  	err := encodeFieldNumberAndTyp3(buf, fieldNum, finfo.GetTyp3(fieldOpts))
   567  	if err != nil {
   568  		return err
   569  	}
   570  	lBeforeValue := buf.Len()
   571  
   572  	// Write field value from rv.
   573  	err = cdc.encodeReflectBinary(buf, finfo, derefedVal, fieldOpts, false, 0)
   574  	if err != nil {
   575  		return err
   576  	}
   577  	lAfterValue := buf.Len()
   578  
   579  	if !isWriteEmpty && lBeforeValue == lAfterValue-1 && buf.Bytes()[buf.Len()-1] == 0x00 {
   580  		// rollback typ3/fieldnum and last byte if
   581  		// not a pointer and empty:
   582  		buf.Truncate(lBeforeKey)
   583  	}
   584  	return nil
   585  }
   586  
   587  // NOTE: This is slightly less efficient than recursing as in the
   588  // implementation for encodeReflectBinaryWelKnown.
   589  func writeMaybeBare(w io.Writer, bz []byte, bare bool) (err error) {
   590  	// Special case
   591  	if len(bz) == 0 {
   592  		if bare {
   593  			return
   594  		} else {
   595  			_, err = w.Write([]byte{0x00})
   596  		}
   597  		return
   598  	}
   599  	// General case
   600  	if bare {
   601  		// Write byteslice without byte-length prefixing.
   602  		_, err = w.Write(bz)
   603  	} else {
   604  		// Write byte-length prefixed byteslice.
   605  		err = EncodeByteSlice(w, bz)
   606  	}
   607  	return err
   608  }