github.com/ergo-services/ergo@v1.999.224/etf/encode.go (about) 1 package etf 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "math" 7 "math/big" 8 "reflect" 9 10 "github.com/ergo-services/ergo/lib" 11 ) 12 13 var ( 14 ErrStringTooLong = fmt.Errorf("Encoding error. String too long. Max allowed length is 65535") 15 ErrAtomTooLong = fmt.Errorf("Encoding error. Atom too long. Max allowed UTF-8 chars is 255") 16 17 // internal types 18 goSlice = byte(240) 19 goMap = byte(241) 20 goStruct = byte(242) 21 goSliceRegistered = byte(243) 22 goMapRegistered = byte(244) 23 goStructRegistered = byte(245) 24 25 marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() 26 ) 27 28 // EncodeOptions 29 type EncodeOptions struct { 30 AtomCache *AtomCacheOut 31 SenderAtomCache map[Atom]CacheItem 32 EncodingAtomCache *EncodingAtomCache 33 AtomMapping *AtomMapping 34 35 // FlagBigPidRef The node accepts a larger amount of data in pids 36 // and references (node container types version 4). 37 // In the pid case full 32-bit ID and Serial fields in NEW_PID_EXT 38 // and in the reference case up to 5 32-bit ID words are now 39 // accepted in NEWER_REFERENCE_EXT. Introduced in OTP 24. 40 FlagBigPidRef bool 41 42 // FlagBigCreation The node understands big node creation tags NEW_PID_EXT, 43 // NEWER_REFERENCE_EXT. 44 FlagBigCreation bool 45 46 NodeName string 47 PeerName string 48 } 49 50 // Encode 51 func Encode(term Term, b *lib.Buffer, options EncodeOptions) (retErr error) { 52 if lib.CatchPanic() { 53 defer func() { 54 // We should catch any panic happened during encoding Golang types. 55 if r := recover(); r != nil { 56 retErr = fmt.Errorf("%v", r) 57 } 58 }() 59 } 60 var stack, child *stackElement 61 62 cacheEnabled := options.AtomCache != nil 63 cacheIndex := int16(0) 64 if cacheEnabled { 65 cacheIndex = int16(len(options.EncodingAtomCache.L)) 66 } 67 68 // Atom cache: (if its enabled: options.AtomCache != nil) 69 // 1. check for an atom in options.WriterAtomCache (map) 70 // 2. if not found in WriterAtomCache call AtomCache.Append(atom), 71 // encode it as a regular atom (ettAtom*) 72 // 3. if found 73 // add options.EncodingAtomCache[i] = CacheItem, where i is just a counter 74 // within this encoding process. 75 76 // encode atom as ettCacheRef with value = i 77 for { 78 79 child = nil 80 81 if stack != nil { 82 83 if stack.i == stack.children { 84 if stack.parent == nil { 85 return nil 86 } 87 stack, stack.parent = stack.parent, nil 88 continue 89 } 90 91 switch stack.termType { 92 case ettList: 93 if stack.i == stack.children-1 { 94 // last item of list should be ettNil 95 term = nil 96 break 97 } 98 term = stack.term.(List)[stack.i] 99 case ettListImproper: 100 // improper list like [a|b] has no ettNil as a last item 101 term = stack.term.(ListImproper)[stack.i] 102 103 case ettSmallTuple: 104 term = stack.term.(Tuple)[stack.i] 105 106 case ettPid: 107 p := stack.term.(Pid) 108 if stack.i == 0 { 109 term = p.Node 110 break 111 } 112 113 buf := b.Extend(9) 114 115 // ID a 32-bit big endian unsigned integer. 116 // If FlagBigPidRef is not set, only 15 bits may be used 117 // and the rest must be 0. 118 if options.FlagBigPidRef { 119 binary.BigEndian.PutUint32(buf[:4], uint32(p.ID)) 120 } else { 121 // 15 bits only 2**15 - 1 = 32767 122 binary.BigEndian.PutUint32(buf[:4], uint32(p.ID)&32767) 123 } 124 125 // Serial a 32-bit big endian unsigned integer. 126 // If distribution FlagBigPidRef is not set, only 13 bits may be used 127 // and the rest must be 0. 128 if options.FlagBigPidRef { 129 binary.BigEndian.PutUint32(buf[4:8], uint32(p.ID>>32)) 130 } else { 131 // 13 bits only 2**13 - 1 = 8191 132 binary.BigEndian.PutUint32(buf[4:8], (uint32(p.ID>>15) & 8191)) 133 } 134 135 // Same as NEW_PID_EXT except the Creation field is 136 // only one byte and only two bits are significant, 137 // the rest are to be 0. 138 buf[8] = byte(p.Creation) & 3 139 140 stack.i++ 141 continue 142 143 case ettNewPid: 144 p := stack.term.(Pid) 145 if stack.i == 0 { 146 term = p.Node 147 break 148 } 149 150 buf := b.Extend(12) 151 // ID 152 if options.FlagBigPidRef { 153 binary.BigEndian.PutUint32(buf[:4], uint32(p.ID)) 154 } else { 155 // 15 bits only 2**15 - 1 = 32767 156 binary.BigEndian.PutUint32(buf[:4], uint32(p.ID)&32767) 157 } 158 // Serial 159 if options.FlagBigPidRef { 160 binary.BigEndian.PutUint32(buf[4:8], uint32(p.ID>>32)) 161 } else { 162 // 13 bits only 2**13 - 1 = 8191 163 binary.BigEndian.PutUint32(buf[4:8], (uint32(p.ID>>32))&8191) 164 } 165 // Creation 166 binary.BigEndian.PutUint32(buf[8:12], p.Creation) 167 168 stack.i++ 169 continue 170 171 case ettNewRef: 172 r := stack.term.(Ref) 173 if stack.i == 0 { 174 term = stack.term.(Ref).Node 175 break 176 } 177 178 lenID := 3 179 buf := b.Extend(1 + lenID*4) 180 // Only one byte long and only two bits are significant, the rest must be 0. 181 buf[0] = byte(r.Creation & 3) 182 buf = buf[1:] 183 for i := 0; i < lenID; i++ { 184 // In the first word (4 bytes) of ID, only 18 bits 185 // are significant, the rest must be 0. 186 if i == 0 { 187 // 2**18 - 1 = 262143 188 binary.BigEndian.PutUint32(buf[:4], r.ID[i]&262143) 189 } else { 190 binary.BigEndian.PutUint32(buf[:4], r.ID[i]) 191 } 192 buf = buf[4:] 193 } 194 195 stack.i++ 196 continue 197 198 case ettNewerRef: 199 r := stack.term.(Ref) 200 if stack.i == 0 { 201 term = stack.term.(Ref).Node 202 break 203 } 204 205 // // FIXME Erlang 24 has a bug https://github.com/erlang/otp/issues/5097 206 // uncomment once they fix it 207 lenID := 3 208 //if options.FlagBigPidRef { 209 // lenID = 5 210 //} 211 buf := b.Extend(4 + lenID*4) 212 binary.BigEndian.PutUint32(buf[0:4], r.Creation) 213 buf = buf[4:] 214 for i := 0; i < lenID; i++ { 215 binary.BigEndian.PutUint32(buf[:4], r.ID[i]) 216 buf = buf[4:] 217 } 218 219 stack.i++ 220 continue 221 222 case ettMap: 223 key := stack.tmp.(List)[stack.i/2] 224 if stack.i&0x01 == 0x01 { // a value 225 term = stack.term.(Map)[key] 226 break 227 } 228 term = key 229 230 case goMapRegistered: 231 if stack.i == 0 { // registered type name as a key 232 term = stack.tmp 233 break 234 } 235 if stack.i == 1 { // nil as a value for the key (registered type name) 236 term = nil 237 break 238 } 239 key := stack.term.([]reflect.Value)[(stack.i-2)/2] 240 if stack.i&0x01 == 0x01 { // a value 241 term = stack.reg.MapIndex(key).Interface() 242 break 243 } 244 term = key.Interface() // a key 245 246 case goMap: 247 key := stack.tmp.([]reflect.Value)[stack.i/2] 248 if stack.i&0x01 == 0x01 { // a value 249 term = stack.term.(func(reflect.Value) reflect.Value)(key).Interface() 250 break 251 } 252 term = key.Interface() // a key 253 254 case goSliceRegistered: 255 if stack.i == 0 { 256 term = stack.tmp 257 break 258 } 259 if stack.i == stack.children-1 { 260 // last item of list should be ettNil 261 term = nil 262 break 263 } 264 term = stack.term.(func(int) reflect.Value)(stack.i - 1).Interface() 265 266 case goSlice: 267 if stack.i == stack.children-1 { 268 // last item of list should be ettNil 269 term = nil 270 break 271 } 272 term = stack.term.(func(int) reflect.Value)(stack.i).Interface() 273 274 case goStructRegistered: 275 if stack.i == 0 { 276 // first item must be a sturct name (stored in stack.tmp). 277 term = stack.tmp 278 break 279 } 280 // field value 281 term = stack.term.(func(int) reflect.Value)(stack.i - 1).Interface() 282 283 case goStruct: 284 field := stack.tmp.(func(int) reflect.StructField)(stack.i / 2) 285 fieldName := field.Name 286 287 if tag := field.Tag.Get("etf"); tag != "" { 288 fieldName = tag 289 } 290 291 if stack.i&0x01 != 0x01 { // a key (field name) 292 term = Atom(fieldName) 293 break 294 } 295 296 // a value 297 fvalue := stack.term.(func(int) reflect.Value)(stack.i / 2) 298 if fvalue.CanInterface() == false { 299 return fmt.Errorf("struct has unexported field %q", fieldName) 300 } 301 term = fvalue.Interface() 302 303 default: 304 305 return errInternal 306 } 307 308 stack.i++ 309 } 310 311 recasting: 312 switch t := term.(type) { 313 case bool: 314 315 if cacheEnabled && cacheIndex < 255 { 316 value := Atom("false") 317 if t { 318 value = Atom("true") 319 } 320 321 // looking for CacheItem 322 ci, found := options.SenderAtomCache[value] 323 if found { 324 i := options.EncodingAtomCache.Append(ci) 325 cacheIndex = int16(i + 1) 326 b.Append([]byte{ettCacheRef, byte(i)}) 327 break 328 } else { 329 // add it to the cache and encode as usual Atom 330 options.AtomCache.Append(value) 331 } 332 } 333 334 if t { 335 b.Append([]byte{ettSmallAtom, 4, 't', 'r', 'u', 'e'}) 336 break 337 } 338 339 b.Append([]byte{ettSmallAtom, 5, 'f', 'a', 'l', 's', 'e'}) 340 341 // do not use reflect.ValueOf(t) because its too expensive 342 case uint8: 343 b.Append([]byte{ettSmallInteger, t}) 344 345 case int8: 346 if t < 0 { 347 term = int32(t) 348 goto recasting 349 } 350 351 b.Append([]byte{ettSmallInteger, uint8(t)}) 352 break 353 354 case uint16: 355 if t <= math.MaxUint8 { 356 b.Append([]byte{ettSmallInteger, byte(t)}) 357 break 358 } 359 term = int32(t) 360 goto recasting 361 362 case int16: 363 if t >= 0 && t <= math.MaxUint8 { 364 b.Append([]byte{ettSmallInteger, byte(t)}) 365 break 366 } 367 368 term = int32(t) 369 goto recasting 370 371 case uint32: 372 if t <= math.MaxUint8 { 373 b.Append([]byte{ettSmallInteger, byte(t)}) 374 break 375 } 376 377 if t > math.MaxInt32 { 378 term = int64(t) 379 goto recasting 380 } 381 382 term = int32(t) 383 goto recasting 384 385 case int32: 386 if t >= 0 && t <= math.MaxUint8 { 387 b.Append([]byte{ettSmallInteger, byte(t)}) 388 break 389 } 390 391 // 1 (ettInteger) + 4 (32bit integer) 392 buf := b.Extend(1 + 4) 393 buf[0] = ettInteger 394 binary.BigEndian.PutUint32(buf[1:5], uint32(t)) 395 396 case uint: 397 if t <= math.MaxUint8 { 398 b.Append([]byte{ettSmallInteger, byte(t)}) 399 break 400 } 401 402 if t > math.MaxInt32 { 403 term = int64(t) 404 goto recasting 405 } 406 407 term = int32(t) 408 goto recasting 409 410 case int: 411 if t >= 0 && t <= math.MaxUint8 { 412 b.Append([]byte{ettSmallInteger, byte(t)}) 413 break 414 } 415 416 if t > math.MaxInt32 || t < math.MinInt32 { 417 term = int64(t) 418 goto recasting 419 } 420 421 term = int32(t) 422 goto recasting 423 424 case uint64: 425 if t <= math.MaxUint8 { 426 b.Append([]byte{ettSmallInteger, byte(t)}) 427 break 428 } 429 430 if t <= math.MaxInt32 { 431 term = int32(t) 432 goto recasting 433 } 434 435 if t <= math.MaxInt64 { 436 term = int64(t) 437 goto recasting 438 } 439 440 buf := []byte{ettSmallBig, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0} 441 binary.LittleEndian.PutUint64(buf[3:], uint64(t)) 442 b.Append(buf) 443 444 case int64: 445 if t >= 0 && t <= math.MaxUint8 { 446 b.Append([]byte{ettSmallInteger, byte(t)}) 447 break 448 } 449 450 if t >= math.MinInt32 && t <= math.MaxInt32 { 451 term = int32(t) 452 goto recasting 453 } 454 455 if t == math.MinInt64 { 456 // corner case: 457 // if t = -9223372036854775808 (which is math.MinInt64) 458 // we can't just revert the sign because it overflows math.MaxInt64 value 459 buf := []byte{ettSmallBig, 8, 1, 0, 0, 0, 0, 0, 0, 0, 128} 460 b.Append(buf) 461 break 462 } 463 464 negative := byte(0) 465 if t < 0 { 466 negative = 1 467 t = -t 468 } 469 470 buf := []byte{ettSmallBig, 0, negative, 0, 0, 0, 0, 0, 0, 0, 0} 471 binary.LittleEndian.PutUint64(buf[3:], uint64(t)) 472 switch { 473 case t < 4294967296: 474 buf[1] = 4 475 b.Append(buf[:7]) 476 477 case t < 1099511627776: 478 buf[1] = 5 479 b.Append(buf[:8]) 480 481 case t < 281474976710656: 482 buf[1] = 6 483 b.Append(buf[:9]) 484 485 case t < 72057594037927936: 486 buf[1] = 7 487 b.Append(buf[:10]) 488 489 default: 490 buf[1] = 8 491 b.Append(buf) 492 } 493 494 case big.Int: 495 bytes := t.Bytes() 496 negative := t.Sign() < 0 497 l := len(bytes) 498 499 for i := 0; i < l/2; i++ { 500 bytes[i], bytes[l-1-i] = bytes[l-1-i], bytes[i] 501 } 502 503 if l < 256 { 504 // 1 (ettSmallBig) + 1 (len) + 1 (sign) + bytes 505 buf := b.Extend(1 + 1 + 1 + l) 506 buf[0] = ettSmallBig 507 buf[1] = byte(l) 508 509 if negative { 510 buf[2] = 1 511 } else { 512 buf[2] = 0 513 } 514 515 copy(buf[3:], bytes) 516 517 break 518 } 519 520 // 1 (ettLargeBig) + 4 (len) + 1(sign) + bytes 521 buf := b.Extend(1 + 4 + 1 + l) 522 buf[0] = ettLargeBig 523 binary.BigEndian.PutUint32(buf[1:5], uint32(l)) 524 525 if negative { 526 buf[5] = 1 527 } else { 528 buf[5] = 0 529 } 530 531 copy(buf[6:], bytes) 532 533 case string: 534 lenString := len(t) 535 536 if lenString > 65535 { 537 return ErrStringTooLong 538 } 539 540 // 1 (ettString) + 2 (len) + string 541 buf := b.Extend(1 + 2 + lenString) 542 buf[0] = ettString 543 binary.BigEndian.PutUint16(buf[1:3], uint16(lenString)) 544 copy(buf[3:], t) 545 546 case Charlist: 547 term = []rune(t) 548 goto recasting 549 550 case String: 551 term = []byte(t) 552 goto recasting 553 554 case Atom: 555 // As from ERTS 9.0 (OTP 20), atoms may contain any Unicode 556 // characters and are always encoded using the UTF-8 external 557 // formats ATOM_UTF8_EXT or SMALL_ATOM_UTF8_EXT. 558 559 // replace atom value if we have mapped value for it 560 if options.AtomMapping != nil { 561 options.AtomMapping.MutexOut.RLock() 562 if mapped, ok := options.AtomMapping.Out[t]; ok { 563 t = mapped 564 } 565 options.AtomMapping.MutexOut.RUnlock() 566 } 567 568 // https://erlang.org/doc/apps/erts/erl_ext_dist.html#utf8_atoms 569 // The maximum number of allowed characters in an atom is 255. 570 // In the UTF-8 case, each character can need 4 bytes to be encoded. 571 if len([]rune(t)) > 255 { 572 return ErrAtomTooLong 573 } 574 575 if cacheEnabled && cacheIndex < 255 { 576 // looking for CacheItem 577 ci, found := options.SenderAtomCache[t] 578 if found { 579 i := options.EncodingAtomCache.Append(ci) 580 cacheIndex = int16(i + 1) 581 b.Append([]byte{ettCacheRef, byte(i)}) 582 break 583 } else { 584 // add it to the cache and encode as usual Atom 585 options.AtomCache.Append(t) 586 } 587 } 588 589 lenAtom := len(t) 590 if lenAtom < 256 { 591 buf := b.Extend(1 + 1 + lenAtom) 592 buf[0] = ettSmallAtomUTF8 593 buf[1] = byte(lenAtom) 594 copy(buf[2:], t) 595 break 596 } 597 598 // 1 (ettAtomUTF8) + 2 (len) + atom 599 buf := b.Extend(1 + 2 + lenAtom) 600 buf[0] = ettAtomUTF8 601 binary.BigEndian.PutUint16(buf[1:3], uint16(lenAtom)) 602 copy(buf[3:], t) 603 604 case float32: 605 term = float64(t) 606 goto recasting 607 608 case float64: 609 // 1 (ettNewFloat) + 8 (float) 610 buf := b.Extend(1 + 8) 611 buf[0] = ettNewFloat 612 bits := math.Float64bits(t) 613 binary.BigEndian.PutUint64(buf[1:9], uint64(bits)) 614 615 case nil: 616 b.AppendByte(ettNil) 617 618 case Tuple: 619 lenTuple := len(t) 620 if lenTuple < 256 { 621 b.Append([]byte{ettSmallTuple, byte(lenTuple)}) 622 } else { 623 buf := b.Extend(5) 624 buf[0] = ettLargeTuple 625 binary.BigEndian.PutUint32(buf[1:5], uint32(lenTuple)) 626 } 627 child = &stackElement{ 628 parent: stack, 629 termType: ettSmallTuple, // doesn't matter what exact type for the further processing 630 term: t, 631 children: lenTuple, 632 } 633 634 case Pid: 635 child = &stackElement{ 636 parent: stack, 637 term: t, 638 children: 2, 639 } 640 if options.FlagBigCreation { 641 child.termType = ettNewPid 642 b.AppendByte(ettNewPid) 643 } else { 644 child.termType = ettPid 645 b.AppendByte(ettPid) 646 } 647 648 case Alias: 649 term = Ref(t) 650 goto recasting 651 652 case Ref: 653 buf := b.Extend(3) 654 655 child = &stackElement{ 656 parent: stack, 657 term: t, 658 children: 2, 659 } 660 if options.FlagBigCreation { 661 buf[0] = ettNewerRef 662 child.termType = ettNewerRef 663 664 } else { 665 buf[0] = ettNewRef 666 child.termType = ettNewRef 667 } 668 669 // LEN a 16-bit big endian unsigned integer not larger 670 // than 5 when the FlagBigPidRef has been set; otherwise not larger than 3. 671 672 // FIXME Erlang 24 has a bug https://github.com/erlang/otp/issues/5097 673 // uncomment once they fix it 674 //if options.FlagBigPidRef { 675 // binary.BigEndian.PutUint16(buf[1:3], 5) 676 //} else { 677 binary.BigEndian.PutUint16(buf[1:3], 3) 678 //} 679 680 case Map: 681 lenMap := len(t) 682 buf := b.Extend(5) 683 buf[0] = ettMap 684 binary.BigEndian.PutUint32(buf[1:], uint32(lenMap)) 685 686 keys := make(List, 0, lenMap) 687 for key := range t { 688 keys = append(keys, key) 689 } 690 691 child = &stackElement{ 692 parent: stack, 693 termType: ettMap, 694 term: t, 695 children: lenMap * 2, 696 tmp: keys, 697 } 698 699 case ListImproper: 700 if len(t) == 0 { 701 b.AppendByte(ettNil) 702 continue 703 } 704 lenList := len(t) - 1 705 buf := b.Extend(5) 706 buf[0] = ettList 707 binary.BigEndian.PutUint32(buf[1:], uint32(lenList)) 708 child = &stackElement{ 709 parent: stack, 710 termType: ettListImproper, 711 term: t, 712 children: lenList + 1, 713 } 714 715 case List: 716 lenList := len(t) 717 if lenList == 0 { 718 b.AppendByte(ettNil) 719 continue 720 } 721 buf := b.Extend(5) 722 buf[0] = ettList 723 binary.BigEndian.PutUint32(buf[1:], uint32(lenList)) 724 child = &stackElement{ 725 parent: stack, 726 termType: ettList, 727 term: t, 728 children: lenList + 1, 729 } 730 731 case []byte: 732 lenBinary := len(t) 733 buf := b.Extend(1 + 4 + lenBinary) 734 buf[0] = ettBinary 735 binary.BigEndian.PutUint32(buf[1:5], uint32(lenBinary)) 736 copy(buf[5:], t) 737 738 case Marshaler: 739 m, err := t.MarshalETF() 740 if err != nil { 741 return err 742 } 743 744 lenBinary := len(m) 745 buf := b.Extend(1 + 4 + lenBinary) 746 buf[0] = ettBinary 747 binary.BigEndian.PutUint32(buf[1:5], uint32(lenBinary)) 748 copy(buf[5:], m) 749 750 default: 751 v := reflect.ValueOf(t) 752 vt := reflect.TypeOf(t) 753 vtAtomName := regTypeName(vt) 754 registered.RLock() 755 rtype, typeIsRegistered := registered.typesEnc[vtAtomName] 756 registered.RUnlock() 757 758 switch v.Kind() { 759 case reflect.Struct: 760 lenStruct := v.NumField() 761 if typeIsRegistered { 762 // registered type. encode as a tuple with vtAtomName as the first element 763 vtAtomName = rtype.name 764 if lenStruct+1 < 255 { 765 b.Append([]byte{ettSmallTuple, byte(lenStruct + 1)}) 766 } else { 767 buf := b.Extend(5) 768 buf[0] = ettLargeTuple 769 binary.BigEndian.PutUint32(buf[1:], uint32(lenStruct+1)) 770 } 771 child = &stackElement{ 772 parent: stack, 773 termType: goStructRegistered, 774 term: v.Field, 775 children: lenStruct + 1, 776 tmp: vtAtomName, 777 } 778 break 779 } 780 781 // will be encoded as a ettMap 782 buf := b.Extend(5) 783 buf[0] = ettMap 784 binary.BigEndian.PutUint32(buf[1:], uint32(lenStruct)) 785 786 child = &stackElement{ 787 parent: stack, 788 termType: goStruct, 789 term: v.Field, 790 children: lenStruct * 2, 791 tmp: v.Type().Field, 792 } 793 794 case reflect.Array, reflect.Slice: 795 lenList := v.Len() 796 797 if typeIsRegistered { 798 vtAtomName = rtype.name 799 lenList++ // first element for the type name 800 buf := b.Extend(5) 801 buf[0] = ettList 802 binary.BigEndian.PutUint32(buf[1:], uint32(lenList)) 803 child = &stackElement{ 804 parent: stack, 805 termType: goSliceRegistered, 806 term: v.Index, 807 children: lenList + 1, 808 tmp: vtAtomName, 809 } 810 break 811 } 812 813 if lenList == 0 { 814 b.AppendByte(ettNil) 815 continue 816 } 817 818 buf := b.Extend(5) 819 buf[0] = ettList 820 binary.BigEndian.PutUint32(buf[1:], uint32(lenList)) 821 child = &stackElement{ 822 parent: stack, 823 termType: goSlice, 824 term: v.Index, 825 children: lenList + 1, 826 } 827 828 case reflect.Map: 829 lenMap := v.Len() 830 if typeIsRegistered { 831 lenMap++ 832 vtAtomName = rtype.name 833 buf := b.Extend(5) 834 buf[0] = ettMap 835 binary.BigEndian.PutUint32(buf[1:], uint32(lenMap)) 836 837 child = &stackElement{ 838 parent: stack, 839 termType: goMapRegistered, 840 term: v.MapKeys(), 841 children: lenMap * 2, 842 tmp: vtAtomName, 843 reg: &v, 844 } 845 break 846 } 847 848 buf := b.Extend(5) 849 buf[0] = ettMap 850 binary.BigEndian.PutUint32(buf[1:], uint32(lenMap)) 851 852 child = &stackElement{ 853 parent: stack, 854 termType: goMap, 855 term: v.MapIndex, 856 children: lenMap * 2, 857 tmp: v.MapKeys(), 858 } 859 860 case reflect.Ptr: 861 // dereference value 862 if !v.IsNil() { 863 term = v.Elem().Interface() 864 goto recasting 865 } 866 867 b.AppendByte(ettNil) 868 if stack == nil { 869 break 870 } 871 872 default: 873 return fmt.Errorf("unsupported type %v with value %#v", v.Type(), v) 874 } 875 } 876 877 if stack == nil && child == nil { 878 return nil 879 } 880 881 if child != nil { 882 stack = child 883 } 884 885 } 886 }