github.com/sbinet/go@v0.0.0-20160827155028-54d7de7dd62b/src/encoding/gob/encode.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:generate go run encgen.go -output enc_helpers.go 6 7 package gob 8 9 import ( 10 "encoding" 11 "math" 12 "reflect" 13 "sync" 14 ) 15 16 const uint64Size = 8 17 18 type encHelper func(state *encoderState, v reflect.Value) bool 19 20 // encoderState is the global execution state of an instance of the encoder. 21 // Field numbers are delta encoded and always increase. The field 22 // number is initialized to -1 so 0 comes out as delta(1). A delta of 23 // 0 terminates the structure. 24 type encoderState struct { 25 enc *Encoder 26 b *encBuffer 27 sendZero bool // encoding an array element or map key/value pair; send zero values 28 fieldnum int // the last field number written. 29 buf [1 + uint64Size]byte // buffer used by the encoder; here to avoid allocation. 30 next *encoderState // for free list 31 } 32 33 // encBuffer is an extremely simple, fast implementation of a write-only byte buffer. 34 // It never returns a non-nil error, but Write returns an error value so it matches io.Writer. 35 type encBuffer struct { 36 data []byte 37 scratch [64]byte 38 } 39 40 var encBufferPool = sync.Pool{ 41 New: func() interface{} { 42 e := new(encBuffer) 43 e.data = e.scratch[0:0] 44 return e 45 }, 46 } 47 48 func (e *encBuffer) WriteByte(c byte) { 49 e.data = append(e.data, c) 50 } 51 52 func (e *encBuffer) Write(p []byte) (int, error) { 53 e.data = append(e.data, p...) 54 return len(p), nil 55 } 56 57 func (e *encBuffer) WriteString(s string) { 58 e.data = append(e.data, s...) 59 } 60 61 func (e *encBuffer) Len() int { 62 return len(e.data) 63 } 64 65 func (e *encBuffer) Bytes() []byte { 66 return e.data 67 } 68 69 func (e *encBuffer) Reset() { 70 if len(e.data) >= tooBig { 71 e.data = e.scratch[0:0] 72 } else { 73 e.data = e.data[0:0] 74 } 75 } 76 77 func (enc *Encoder) newEncoderState(b *encBuffer) *encoderState { 78 e := enc.freeList 79 if e == nil { 80 e = new(encoderState) 81 e.enc = enc 82 } else { 83 enc.freeList = e.next 84 } 85 e.sendZero = false 86 e.fieldnum = 0 87 e.b = b 88 if len(b.data) == 0 { 89 b.data = b.scratch[0:0] 90 } 91 return e 92 } 93 94 func (enc *Encoder) freeEncoderState(e *encoderState) { 95 e.next = enc.freeList 96 enc.freeList = e 97 } 98 99 // Unsigned integers have a two-state encoding. If the number is less 100 // than 128 (0 through 0x7F), its value is written directly. 101 // Otherwise the value is written in big-endian byte order preceded 102 // by the byte length, negated. 103 104 // encodeUint writes an encoded unsigned integer to state.b. 105 func (state *encoderState) encodeUint(x uint64) { 106 if x <= 0x7F { 107 state.b.WriteByte(uint8(x)) 108 return 109 } 110 i := uint64Size 111 for x > 0 { 112 state.buf[i] = uint8(x) 113 x >>= 8 114 i-- 115 } 116 state.buf[i] = uint8(i - uint64Size) // = loop count, negated 117 state.b.Write(state.buf[i : uint64Size+1]) 118 } 119 120 // encodeInt writes an encoded signed integer to state.w. 121 // The low bit of the encoding says whether to bit complement the (other bits of the) 122 // uint to recover the int. 123 func (state *encoderState) encodeInt(i int64) { 124 var x uint64 125 if i < 0 { 126 x = uint64(^i<<1) | 1 127 } else { 128 x = uint64(i << 1) 129 } 130 state.encodeUint(x) 131 } 132 133 // encOp is the signature of an encoding operator for a given type. 134 type encOp func(i *encInstr, state *encoderState, v reflect.Value) 135 136 // The 'instructions' of the encoding machine 137 type encInstr struct { 138 op encOp 139 field int // field number in input 140 index []int // struct index 141 indir int // how many pointer indirections to reach the value in the struct 142 } 143 144 // update emits a field number and updates the state to record its value for delta encoding. 145 // If the instruction pointer is nil, it does nothing 146 func (state *encoderState) update(instr *encInstr) { 147 if instr != nil { 148 state.encodeUint(uint64(instr.field - state.fieldnum)) 149 state.fieldnum = instr.field 150 } 151 } 152 153 // Each encoder for a composite is responsible for handling any 154 // indirections associated with the elements of the data structure. 155 // If any pointer so reached is nil, no bytes are written. If the 156 // data item is zero, no bytes are written. Single values - ints, 157 // strings etc. - are indirected before calling their encoders. 158 // Otherwise, the output (for a scalar) is the field number, as an 159 // encoded integer, followed by the field data in its appropriate 160 // format. 161 162 // encIndirect dereferences pv indir times and returns the result. 163 func encIndirect(pv reflect.Value, indir int) reflect.Value { 164 for ; indir > 0; indir-- { 165 if pv.IsNil() { 166 break 167 } 168 pv = pv.Elem() 169 } 170 return pv 171 } 172 173 // encBool encodes the bool referenced by v as an unsigned 0 or 1. 174 func encBool(i *encInstr, state *encoderState, v reflect.Value) { 175 b := v.Bool() 176 if b || state.sendZero { 177 state.update(i) 178 if b { 179 state.encodeUint(1) 180 } else { 181 state.encodeUint(0) 182 } 183 } 184 } 185 186 // encInt encodes the signed integer (int int8 int16 int32 int64) referenced by v. 187 func encInt(i *encInstr, state *encoderState, v reflect.Value) { 188 value := v.Int() 189 if value != 0 || state.sendZero { 190 state.update(i) 191 state.encodeInt(value) 192 } 193 } 194 195 // encUint encodes the unsigned integer (uint uint8 uint16 uint32 uint64 uintptr) referenced by v. 196 func encUint(i *encInstr, state *encoderState, v reflect.Value) { 197 value := v.Uint() 198 if value != 0 || state.sendZero { 199 state.update(i) 200 state.encodeUint(value) 201 } 202 } 203 204 // floatBits returns a uint64 holding the bits of a floating-point number. 205 // Floating-point numbers are transmitted as uint64s holding the bits 206 // of the underlying representation. They are sent byte-reversed, with 207 // the exponent end coming out first, so integer floating point numbers 208 // (for example) transmit more compactly. This routine does the 209 // swizzling. 210 func floatBits(f float64) uint64 { 211 u := math.Float64bits(f) 212 var v uint64 213 for i := 0; i < 8; i++ { 214 v <<= 8 215 v |= u & 0xFF 216 u >>= 8 217 } 218 return v 219 } 220 221 // encFloat encodes the floating point value (float32 float64) referenced by v. 222 func encFloat(i *encInstr, state *encoderState, v reflect.Value) { 223 f := v.Float() 224 if f != 0 || state.sendZero { 225 bits := floatBits(f) 226 state.update(i) 227 state.encodeUint(bits) 228 } 229 } 230 231 // encComplex encodes the complex value (complex64 complex128) referenced by v. 232 // Complex numbers are just a pair of floating-point numbers, real part first. 233 func encComplex(i *encInstr, state *encoderState, v reflect.Value) { 234 c := v.Complex() 235 if c != 0+0i || state.sendZero { 236 rpart := floatBits(real(c)) 237 ipart := floatBits(imag(c)) 238 state.update(i) 239 state.encodeUint(rpart) 240 state.encodeUint(ipart) 241 } 242 } 243 244 // encUint8Array encodes the byte array referenced by v. 245 // Byte arrays are encoded as an unsigned count followed by the raw bytes. 246 func encUint8Array(i *encInstr, state *encoderState, v reflect.Value) { 247 b := v.Bytes() 248 if len(b) > 0 || state.sendZero { 249 state.update(i) 250 state.encodeUint(uint64(len(b))) 251 state.b.Write(b) 252 } 253 } 254 255 // encString encodes the string referenced by v. 256 // Strings are encoded as an unsigned count followed by the raw bytes. 257 func encString(i *encInstr, state *encoderState, v reflect.Value) { 258 s := v.String() 259 if len(s) > 0 || state.sendZero { 260 state.update(i) 261 state.encodeUint(uint64(len(s))) 262 state.b.WriteString(s) 263 } 264 } 265 266 // encStructTerminator encodes the end of an encoded struct 267 // as delta field number of 0. 268 func encStructTerminator(i *encInstr, state *encoderState, v reflect.Value) { 269 state.encodeUint(0) 270 } 271 272 // Execution engine 273 274 // encEngine an array of instructions indexed by field number of the encoding 275 // data, typically a struct. It is executed top to bottom, walking the struct. 276 type encEngine struct { 277 instr []encInstr 278 } 279 280 const singletonField = 0 281 282 // valid reports whether the value is valid and a non-nil pointer. 283 // (Slices, maps, and chans take care of themselves.) 284 func valid(v reflect.Value) bool { 285 switch v.Kind() { 286 case reflect.Invalid: 287 return false 288 case reflect.Ptr: 289 return !v.IsNil() 290 } 291 return true 292 } 293 294 // encodeSingle encodes a single top-level non-struct value. 295 func (enc *Encoder) encodeSingle(b *encBuffer, engine *encEngine, value reflect.Value) { 296 state := enc.newEncoderState(b) 297 defer enc.freeEncoderState(state) 298 state.fieldnum = singletonField 299 // There is no surrounding struct to frame the transmission, so we must 300 // generate data even if the item is zero. To do this, set sendZero. 301 state.sendZero = true 302 instr := &engine.instr[singletonField] 303 if instr.indir > 0 { 304 value = encIndirect(value, instr.indir) 305 } 306 if valid(value) { 307 instr.op(instr, state, value) 308 } 309 } 310 311 // encodeStruct encodes a single struct value. 312 func (enc *Encoder) encodeStruct(b *encBuffer, engine *encEngine, value reflect.Value) { 313 if !valid(value) { 314 return 315 } 316 state := enc.newEncoderState(b) 317 defer enc.freeEncoderState(state) 318 state.fieldnum = -1 319 for i := 0; i < len(engine.instr); i++ { 320 instr := &engine.instr[i] 321 if i >= value.NumField() { 322 // encStructTerminator 323 instr.op(instr, state, reflect.Value{}) 324 break 325 } 326 field := value.FieldByIndex(instr.index) 327 if instr.indir > 0 { 328 field = encIndirect(field, instr.indir) 329 // TODO: Is field guaranteed valid? If so we could avoid this check. 330 if !valid(field) { 331 continue 332 } 333 } 334 instr.op(instr, state, field) 335 } 336 } 337 338 // encodeArray encodes an array. 339 func (enc *Encoder) encodeArray(b *encBuffer, value reflect.Value, op encOp, elemIndir int, length int, helper encHelper) { 340 state := enc.newEncoderState(b) 341 defer enc.freeEncoderState(state) 342 state.fieldnum = -1 343 state.sendZero = true 344 state.encodeUint(uint64(length)) 345 if helper != nil && helper(state, value) { 346 return 347 } 348 for i := 0; i < length; i++ { 349 elem := value.Index(i) 350 if elemIndir > 0 { 351 elem = encIndirect(elem, elemIndir) 352 // TODO: Is elem guaranteed valid? If so we could avoid this check. 353 if !valid(elem) { 354 errorf("encodeArray: nil element") 355 } 356 } 357 op(nil, state, elem) 358 } 359 } 360 361 // encodeReflectValue is a helper for maps. It encodes the value v. 362 func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir int) { 363 for i := 0; i < indir && v.IsValid(); i++ { 364 v = reflect.Indirect(v) 365 } 366 if !v.IsValid() { 367 errorf("encodeReflectValue: nil element") 368 } 369 op(nil, state, v) 370 } 371 372 // encodeMap encodes a map as unsigned count followed by key:value pairs. 373 func (enc *Encoder) encodeMap(b *encBuffer, mv reflect.Value, keyOp, elemOp encOp, keyIndir, elemIndir int) { 374 state := enc.newEncoderState(b) 375 state.fieldnum = -1 376 state.sendZero = true 377 keys := mv.MapKeys() 378 state.encodeUint(uint64(len(keys))) 379 for _, key := range keys { 380 encodeReflectValue(state, key, keyOp, keyIndir) 381 encodeReflectValue(state, mv.MapIndex(key), elemOp, elemIndir) 382 } 383 enc.freeEncoderState(state) 384 } 385 386 // encodeInterface encodes the interface value iv. 387 // To send an interface, we send a string identifying the concrete type, followed 388 // by the type identifier (which might require defining that type right now), followed 389 // by the concrete value. A nil value gets sent as the empty string for the name, 390 // followed by no value. 391 func (enc *Encoder) encodeInterface(b *encBuffer, iv reflect.Value) { 392 // Gobs can encode nil interface values but not typed interface 393 // values holding nil pointers, since nil pointers point to no value. 394 elem := iv.Elem() 395 if elem.Kind() == reflect.Ptr && elem.IsNil() { 396 errorf("gob: cannot encode nil pointer of type %s inside interface", iv.Elem().Type()) 397 } 398 state := enc.newEncoderState(b) 399 state.fieldnum = -1 400 state.sendZero = true 401 if iv.IsNil() { 402 state.encodeUint(0) 403 return 404 } 405 406 ut := userType(iv.Elem().Type()) 407 registerLock.RLock() 408 name, ok := concreteTypeToName[ut.base] 409 registerLock.RUnlock() 410 if !ok { 411 errorf("type not registered for interface: %s", ut.base) 412 } 413 // Send the name. 414 state.encodeUint(uint64(len(name))) 415 state.b.WriteString(name) 416 // Define the type id if necessary. 417 enc.sendTypeDescriptor(enc.writer(), state, ut) 418 // Send the type id. 419 enc.sendTypeId(state, ut) 420 // Encode the value into a new buffer. Any nested type definitions 421 // should be written to b, before the encoded value. 422 enc.pushWriter(b) 423 data := encBufferPool.Get().(*encBuffer) 424 data.Write(spaceForLength) 425 enc.encode(data, elem, ut) 426 if enc.err != nil { 427 error_(enc.err) 428 } 429 enc.popWriter() 430 enc.writeMessage(b, data) 431 data.Reset() 432 encBufferPool.Put(data) 433 if enc.err != nil { 434 error_(enc.err) 435 } 436 enc.freeEncoderState(state) 437 } 438 439 // isZero reports whether the value is the zero of its type. 440 func isZero(val reflect.Value) bool { 441 switch val.Kind() { 442 case reflect.Array: 443 for i := 0; i < val.Len(); i++ { 444 if !isZero(val.Index(i)) { 445 return false 446 } 447 } 448 return true 449 case reflect.Map, reflect.Slice, reflect.String: 450 return val.Len() == 0 451 case reflect.Bool: 452 return !val.Bool() 453 case reflect.Complex64, reflect.Complex128: 454 return val.Complex() == 0 455 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr: 456 return val.IsNil() 457 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 458 return val.Int() == 0 459 case reflect.Float32, reflect.Float64: 460 return val.Float() == 0 461 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 462 return val.Uint() == 0 463 case reflect.Struct: 464 for i := 0; i < val.NumField(); i++ { 465 if !isZero(val.Field(i)) { 466 return false 467 } 468 } 469 return true 470 } 471 panic("unknown type in isZero " + val.Type().String()) 472 } 473 474 // encGobEncoder encodes a value that implements the GobEncoder interface. 475 // The data is sent as a byte array. 476 func (enc *Encoder) encodeGobEncoder(b *encBuffer, ut *userTypeInfo, v reflect.Value) { 477 // TODO: should we catch panics from the called method? 478 479 var data []byte 480 var err error 481 // We know it's one of these. 482 switch ut.externalEnc { 483 case xGob: 484 data, err = v.Interface().(GobEncoder).GobEncode() 485 case xBinary: 486 data, err = v.Interface().(encoding.BinaryMarshaler).MarshalBinary() 487 case xText: 488 data, err = v.Interface().(encoding.TextMarshaler).MarshalText() 489 } 490 if err != nil { 491 error_(err) 492 } 493 state := enc.newEncoderState(b) 494 state.fieldnum = -1 495 state.encodeUint(uint64(len(data))) 496 state.b.Write(data) 497 enc.freeEncoderState(state) 498 } 499 500 var encOpTable = [...]encOp{ 501 reflect.Bool: encBool, 502 reflect.Int: encInt, 503 reflect.Int8: encInt, 504 reflect.Int16: encInt, 505 reflect.Int32: encInt, 506 reflect.Int64: encInt, 507 reflect.Uint: encUint, 508 reflect.Uint8: encUint, 509 reflect.Uint16: encUint, 510 reflect.Uint32: encUint, 511 reflect.Uint64: encUint, 512 reflect.Uintptr: encUint, 513 reflect.Float32: encFloat, 514 reflect.Float64: encFloat, 515 reflect.Complex64: encComplex, 516 reflect.Complex128: encComplex, 517 reflect.String: encString, 518 } 519 520 // encOpFor returns (a pointer to) the encoding op for the base type under rt and 521 // the indirection count to reach it. 522 func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp, building map[*typeInfo]bool) (*encOp, int) { 523 ut := userType(rt) 524 // If the type implements GobEncoder, we handle it without further processing. 525 if ut.externalEnc != 0 { 526 return gobEncodeOpFor(ut) 527 } 528 // If this type is already in progress, it's a recursive type (e.g. map[string]*T). 529 // Return the pointer to the op we're already building. 530 if opPtr := inProgress[rt]; opPtr != nil { 531 return opPtr, ut.indir 532 } 533 typ := ut.base 534 indir := ut.indir 535 k := typ.Kind() 536 var op encOp 537 if int(k) < len(encOpTable) { 538 op = encOpTable[k] 539 } 540 if op == nil { 541 inProgress[rt] = &op 542 // Special cases 543 switch t := typ; t.Kind() { 544 case reflect.Slice: 545 if t.Elem().Kind() == reflect.Uint8 { 546 op = encUint8Array 547 break 548 } 549 // Slices have a header; we decode it to find the underlying array. 550 elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building) 551 helper := encSliceHelper[t.Elem().Kind()] 552 op = func(i *encInstr, state *encoderState, slice reflect.Value) { 553 if !state.sendZero && slice.Len() == 0 { 554 return 555 } 556 state.update(i) 557 state.enc.encodeArray(state.b, slice, *elemOp, elemIndir, slice.Len(), helper) 558 } 559 case reflect.Array: 560 // True arrays have size in the type. 561 elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building) 562 helper := encArrayHelper[t.Elem().Kind()] 563 op = func(i *encInstr, state *encoderState, array reflect.Value) { 564 state.update(i) 565 state.enc.encodeArray(state.b, array, *elemOp, elemIndir, array.Len(), helper) 566 } 567 case reflect.Map: 568 keyOp, keyIndir := encOpFor(t.Key(), inProgress, building) 569 elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building) 570 op = func(i *encInstr, state *encoderState, mv reflect.Value) { 571 // We send zero-length (but non-nil) maps because the 572 // receiver might want to use the map. (Maps don't use append.) 573 if !state.sendZero && mv.IsNil() { 574 return 575 } 576 state.update(i) 577 state.enc.encodeMap(state.b, mv, *keyOp, *elemOp, keyIndir, elemIndir) 578 } 579 case reflect.Struct: 580 // Generate a closure that calls out to the engine for the nested type. 581 getEncEngine(userType(typ), building) 582 info := mustGetTypeInfo(typ) 583 op = func(i *encInstr, state *encoderState, sv reflect.Value) { 584 state.update(i) 585 // indirect through info to delay evaluation for recursive structs 586 enc := info.encoder.Load().(*encEngine) 587 state.enc.encodeStruct(state.b, enc, sv) 588 } 589 case reflect.Interface: 590 op = func(i *encInstr, state *encoderState, iv reflect.Value) { 591 if !state.sendZero && (!iv.IsValid() || iv.IsNil()) { 592 return 593 } 594 state.update(i) 595 state.enc.encodeInterface(state.b, iv) 596 } 597 } 598 } 599 if op == nil { 600 errorf("can't happen: encode type %s", rt) 601 } 602 return &op, indir 603 } 604 605 // gobEncodeOpFor returns the op for a type that is known to implement GobEncoder. 606 func gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) { 607 rt := ut.user 608 if ut.encIndir == -1 { 609 rt = reflect.PtrTo(rt) 610 } else if ut.encIndir > 0 { 611 for i := int8(0); i < ut.encIndir; i++ { 612 rt = rt.Elem() 613 } 614 } 615 var op encOp 616 op = func(i *encInstr, state *encoderState, v reflect.Value) { 617 if ut.encIndir == -1 { 618 // Need to climb up one level to turn value into pointer. 619 if !v.CanAddr() { 620 errorf("unaddressable value of type %s", rt) 621 } 622 v = v.Addr() 623 } 624 if !state.sendZero && isZero(v) { 625 return 626 } 627 state.update(i) 628 state.enc.encodeGobEncoder(state.b, ut, v) 629 } 630 return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver. 631 } 632 633 // compileEnc returns the engine to compile the type. 634 func compileEnc(ut *userTypeInfo, building map[*typeInfo]bool) *encEngine { 635 srt := ut.base 636 engine := new(encEngine) 637 seen := make(map[reflect.Type]*encOp) 638 rt := ut.base 639 if ut.externalEnc != 0 { 640 rt = ut.user 641 } 642 if ut.externalEnc == 0 && srt.Kind() == reflect.Struct { 643 for fieldNum, wireFieldNum := 0, 0; fieldNum < srt.NumField(); fieldNum++ { 644 f := srt.Field(fieldNum) 645 if !isSent(&f) { 646 continue 647 } 648 op, indir := encOpFor(f.Type, seen, building) 649 engine.instr = append(engine.instr, encInstr{*op, wireFieldNum, f.Index, indir}) 650 wireFieldNum++ 651 } 652 if srt.NumField() > 0 && len(engine.instr) == 0 { 653 errorf("type %s has no exported fields", rt) 654 } 655 engine.instr = append(engine.instr, encInstr{encStructTerminator, 0, nil, 0}) 656 } else { 657 engine.instr = make([]encInstr, 1) 658 op, indir := encOpFor(rt, seen, building) 659 engine.instr[0] = encInstr{*op, singletonField, nil, indir} 660 } 661 return engine 662 } 663 664 // getEncEngine returns the engine to compile the type. 665 func getEncEngine(ut *userTypeInfo, building map[*typeInfo]bool) *encEngine { 666 info, err := getTypeInfo(ut) 667 if err != nil { 668 error_(err) 669 } 670 enc, ok := info.encoder.Load().(*encEngine) 671 if !ok { 672 enc = buildEncEngine(info, ut, building) 673 } 674 return enc 675 } 676 677 func buildEncEngine(info *typeInfo, ut *userTypeInfo, building map[*typeInfo]bool) *encEngine { 678 // Check for recursive types. 679 if building != nil && building[info] { 680 return nil 681 } 682 info.encInit.Lock() 683 defer info.encInit.Unlock() 684 enc, ok := info.encoder.Load().(*encEngine) 685 if !ok { 686 if building == nil { 687 building = make(map[*typeInfo]bool) 688 } 689 building[info] = true 690 enc = compileEnc(ut, building) 691 info.encoder.Store(enc) 692 } 693 return enc 694 } 695 696 func (enc *Encoder) encode(b *encBuffer, value reflect.Value, ut *userTypeInfo) { 697 defer catchError(&enc.err) 698 engine := getEncEngine(ut, nil) 699 indir := ut.indir 700 if ut.externalEnc != 0 { 701 indir = int(ut.encIndir) 702 } 703 for i := 0; i < indir; i++ { 704 value = reflect.Indirect(value) 705 } 706 if ut.externalEnc == 0 && value.Type().Kind() == reflect.Struct { 707 enc.encodeStruct(b, engine, value) 708 } else { 709 enc.encodeSingle(b, engine, value) 710 } 711 }