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 }