diff options
| author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-11-06 19:49:01 +0000 |
|---|---|---|
| committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-11-06 19:49:01 +0000 |
| commit | 0ce10ea1348e9afd5d0eec6bca986bfe58bac5ac (patch) | |
| tree | 39530b071991b2326f881b2a30a2d82d6c133fd6 /libgo/go/encoding/gob | |
| parent | 57a8bf1b0c6057ccbacb0cf79eb84d1985c2c1fe (diff) | |
| download | ppe42-gcc-0ce10ea1348e9afd5d0eec6bca986bfe58bac5ac.tar.gz ppe42-gcc-0ce10ea1348e9afd5d0eec6bca986bfe58bac5ac.zip | |
libgo: Update to October 24 version of master library.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@204466 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/encoding/gob')
| -rw-r--r-- | libgo/go/encoding/gob/codec_test.go | 18 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/debug.go | 10 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/decode.go | 89 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/doc.go | 40 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/encode.go | 66 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/encoder.go | 12 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/encoder_test.go | 42 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/gobencdec_test.go | 146 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/timing_test.go | 15 | ||||
| -rw-r--r-- | libgo/go/encoding/gob/type.go | 104 |
10 files changed, 381 insertions, 161 deletions
diff --git a/libgo/go/encoding/gob/codec_test.go b/libgo/go/encoding/gob/codec_test.go index 9e38e31d5da..b40f78360c2 100644 --- a/libgo/go/encoding/gob/codec_test.go +++ b/libgo/go/encoding/gob/codec_test.go @@ -1009,24 +1009,6 @@ func TestBadRecursiveType(t *testing.T) { // Can't test decode easily because we can't encode one, so we can't pass one to a Decoder. } -type Bad0 struct { - CH chan int - C float64 -} - -func TestInvalidField(t *testing.T) { - var bad0 Bad0 - bad0.CH = make(chan int) - b := new(bytes.Buffer) - dummyEncoder := new(Encoder) // sufficient for this purpose. - dummyEncoder.encode(b, reflect.ValueOf(&bad0), userType(reflect.TypeOf(&bad0))) - if err := dummyEncoder.err; err == nil { - t.Error("expected error; got none") - } else if strings.Index(err.Error(), "type") < 0 { - t.Error("expected type error; got", err) - } -} - type Indirect struct { A ***[3]int S ***[]int diff --git a/libgo/go/encoding/gob/debug.go b/libgo/go/encoding/gob/debug.go index 31d1351fc4f..6117eb08373 100644 --- a/libgo/go/encoding/gob/debug.go +++ b/libgo/go/encoding/gob/debug.go @@ -415,6 +415,16 @@ func (deb *debugger) typeDefinition(indent tab, id typeId) { deb.delta(1) com := deb.common() wire.GobEncoderT = &gobEncoderType{com} + case 5: // BinaryMarshaler type, one field of {{Common}} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + wire.BinaryMarshalerT = &gobEncoderType{com} + case 6: // TextMarshaler type, one field of {{Common}} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + wire.TextMarshalerT = &gobEncoderType{com} default: errorf("bad field in type %d", fieldNum) } diff --git a/libgo/go/encoding/gob/decode.go b/libgo/go/encoding/gob/decode.go index 7cc7565409c..3e76f4c9066 100644 --- a/libgo/go/encoding/gob/decode.go +++ b/libgo/go/encoding/gob/decode.go @@ -9,6 +9,7 @@ package gob import ( "bytes" + "encoding" "errors" "io" "math" @@ -450,11 +451,11 @@ type decEngine struct { // allocate makes sure storage is available for an object of underlying type rtyp // that is indir levels of indirection through p. -func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { +func allocate(rtyp reflect.Type, p unsafe.Pointer, indir int) unsafe.Pointer { if indir == 0 { return p } - up := unsafe.Pointer(p) + up := p if indir > 1 { up = decIndirect(up, indir) } @@ -462,13 +463,13 @@ func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { // Allocate object. *(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.New(rtyp).Pointer()) } - return *(*uintptr)(up) + return *(*unsafe.Pointer)(up) } // decodeSingle decodes a top-level value that is not a struct and stores it through p. // Such values are preceded by a zero, making them have the memory layout of a // struct field (although with an illegal field number). -func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) { +func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep unsafe.Pointer) { state := dec.newDecoderState(&dec.buf) state.fieldnum = singletonField delta := int(state.decodeUint()) @@ -479,7 +480,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint if instr.indir != ut.indir { errorf("internal error: inconsistent indirection instr %d ut %d", instr.indir, ut.indir) } - ptr := unsafe.Pointer(basep) // offset will be zero + ptr := basep // offset will be zero if instr.indir > 1 { ptr = decIndirect(ptr, instr.indir) } @@ -492,7 +493,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint // differ from ut.indir, which was computed when the engine was built. // This state cannot arise for decodeSingle, which is called directly // from the user's value, not from the innards of an engine. -func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, indir int) { +func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p unsafe.Pointer, indir int) { p = allocate(ut.base, p, indir) state := dec.newDecoderState(&dec.buf) state.fieldnum = -1 @@ -511,7 +512,7 @@ func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, break } instr := &engine.instr[fieldnum] - p := unsafe.Pointer(basep + instr.offset) + p := unsafe.Pointer(uintptr(basep) + instr.offset) if instr.indir > 1 { p = decIndirect(p, instr.indir) } @@ -559,25 +560,25 @@ func (dec *Decoder) ignoreSingle(engine *decEngine) { } // decodeArrayHelper does the work for decoding arrays and slices. -func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) { +func (dec *Decoder) decodeArrayHelper(state *decoderState, p unsafe.Pointer, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) { instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl} for i := 0; i < length; i++ { if state.b.Len() == 0 { errorf("decoding array or slice: length exceeds input size (%d elements)", length) } - up := unsafe.Pointer(p) + up := p if elemIndir > 1 { up = decIndirect(up, elemIndir) } elemOp(instr, state, up) - p += uintptr(elemWid) + p = unsafe.Pointer(uintptr(p) + elemWid) } } // decodeArray decodes an array and stores it through p, that is, p points to the zeroth element. // The length is an unsigned integer preceding the elements. Even though the length is redundant // (it's part of the type), it's a useful check and is included in the encoding. -func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl error) { +func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p unsafe.Pointer, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl error) { if indir > 0 { p = allocate(atyp, p, 1) // All but the last level has been allocated by dec.Indirect } @@ -591,7 +592,7 @@ func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintpt // unlike the other items we can't use a pointer directly. func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, ovfl error) reflect.Value { instr := &decInstr{op, 0, indir, 0, ovfl} - up := unsafe.Pointer(unsafeAddr(v)) + up := unsafeAddr(v) if indir > 1 { up = decIndirect(up, indir) } @@ -603,7 +604,7 @@ func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, // Maps are encoded as a length followed by key:value pairs. // Because the internals of maps are not visible to us, we must // use reflection rather than pointer magic. -func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl error) { +func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p unsafe.Pointer, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl error) { if indir > 0 { p = allocate(mtyp, p, 1) // All but the last level has been allocated by dec.Indirect } @@ -673,7 +674,7 @@ func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintpt hdrp.Cap = n } hdrp.Len = n - dec.decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl) + dec.decodeArrayHelper(state, unsafe.Pointer(hdrp.Data), elemOp, elemWid, n, elemIndir, ovfl) } // ignoreSlice skips over the data for a slice value with no destination. @@ -693,7 +694,7 @@ func setInterfaceValue(ivalue reflect.Value, value reflect.Value) { // decodeInterface decodes an interface value and stores it through p. // Interfaces are encoded as the name of a concrete type followed by a value. // If the name is empty, the value is nil and no value is sent. -func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p uintptr, indir int) { +func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p unsafe.Pointer, indir int) { // Create a writable interface reflect.Value. We need one even for the nil case. ivalue := allocValue(ityp) // Read the name of the concrete type. @@ -767,15 +768,22 @@ func (dec *Decoder) ignoreInterface(state *decoderState) { // decodeGobDecoder decodes something implementing the GobDecoder interface. // The data is encoded as a byte slice. -func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value) { +func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, v reflect.Value) { // Read the bytes for the value. b := make([]byte, state.decodeUint()) _, err := state.b.Read(b) if err != nil { error_(err) } - // We know it's a GobDecoder, so just call the method directly. - err = v.Interface().(GobDecoder).GobDecode(b) + // We know it's one of these. + switch ut.externalDec { + case xGob: + err = v.Interface().(GobDecoder).GobDecode(b) + case xBinary: + err = v.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary(b) + case xText: + err = v.Interface().(encoding.TextUnmarshaler).UnmarshalText(b) + } if err != nil { error_(err) } @@ -825,9 +833,10 @@ var decIgnoreOpMap = map[typeId]decOp{ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProgress map[reflect.Type]*decOp) (*decOp, int) { ut := userType(rt) // If the type implements GobEncoder, we handle it without further processing. - if ut.isGobDecoder { + if ut.externalDec != 0 { return dec.gobDecodeOpFor(ut) } + // If this type is already in progress, it's a recursive type (e.g. map[string]*T). // Return the pointer to the op we're already building. if opPtr := inProgress[rt]; opPtr != nil { @@ -850,7 +859,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress) ovfl := overflow(name) op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { - state.dec.decodeArray(t, state, uintptr(p), *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) + state.dec.decodeArray(t, state, p, *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) } case reflect.Map: @@ -860,8 +869,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), "element of "+name, inProgress) ovfl := overflow(name) op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { - up := unsafe.Pointer(p) - state.dec.decodeMap(t, state, uintptr(up), *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl) + state.dec.decodeMap(t, state, p, *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl) } case reflect.Slice: @@ -890,11 +898,11 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg } op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { // indirect through enginePtr to delay evaluation for recursive structs. - dec.decodeStruct(*enginePtr, userType(typ), uintptr(p), i.indir) + dec.decodeStruct(*enginePtr, userType(typ), p, i.indir) } case reflect.Interface: op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { - state.dec.decodeInterface(t, state, uintptr(p), i.indir) + state.dec.decodeInterface(t, state, p, i.indir) } } } @@ -955,7 +963,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp { state.dec.ignoreStruct(*enginePtr) } - case wire.GobEncoderT != nil: + case wire.GobEncoderT != nil, wire.BinaryMarshalerT != nil, wire.TextMarshalerT != nil: op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { state.dec.ignoreGobDecoder(state) } @@ -994,7 +1002,7 @@ func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) { } else { v = reflect.NewAt(rcvrType, p).Elem() } - state.dec.decodeGobDecoder(state, v) + state.dec.decodeGobDecoder(ut, state, v) } return &op, int(ut.indir) @@ -1011,12 +1019,18 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[re inProgress[fr] = fw ut := userType(fr) wire, ok := dec.wireType[fw] - // If fr is a GobDecoder, the wire type must be GobEncoder. - // And if fr is not a GobDecoder, the wire type must not be either. - if ut.isGobDecoder != (ok && wire.GobEncoderT != nil) { // the parentheses look odd but are correct. + // If wire was encoded with an encoding method, fr must have that method. + // And if not, it must not. + // At most one of the booleans in ut is set. + // We could possibly relax this constraint in the future in order to + // choose the decoding method using the data in the wireType. + // The parentheses look odd but are correct. + if (ut.externalDec == xGob) != (ok && wire.GobEncoderT != nil) || + (ut.externalDec == xBinary) != (ok && wire.BinaryMarshalerT != nil) || + (ut.externalDec == xText) != (ok && wire.TextMarshalerT != nil) { return false } - if ut.isGobDecoder { // This test trumps all others. + if ut.externalDec != 0 { // This test trumps all others. return true } switch t := ut.base; t.Kind() { @@ -1115,8 +1129,7 @@ func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) { rt := ut.base srt := rt - if srt.Kind() != reflect.Struct || - ut.isGobDecoder { + if srt.Kind() != reflect.Struct || ut.externalDec != 0 { return dec.compileSingle(remoteId, ut) } var wireStruct *structType @@ -1224,14 +1237,14 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) { return } engine := *enginePtr - if st := base; st.Kind() == reflect.Struct && !ut.isGobDecoder { + if st := base; st.Kind() == reflect.Struct && ut.externalDec == 0 { if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].StructT.Field) > 0 { name := base.Name() errorf("type mismatch: no fields matched compiling decoder for %s", name) } - dec.decodeStruct(engine, ut, uintptr(unsafeAddr(val)), ut.indir) + dec.decodeStruct(engine, ut, unsafeAddr(val), ut.indir) } else { - dec.decodeSingle(engine, ut, uintptr(unsafeAddr(val))) + dec.decodeSingle(engine, ut, unsafeAddr(val)) } } @@ -1283,13 +1296,13 @@ func init() { // into existing structs or slices cannot be addressed, // so simulate it by returning a pointer to a copy. // Each call allocates once. -func unsafeAddr(v reflect.Value) uintptr { +func unsafeAddr(v reflect.Value) unsafe.Pointer { if v.CanAddr() { - return v.UnsafeAddr() + return unsafe.Pointer(v.UnsafeAddr()) } x := reflect.New(v.Type()).Elem() x.Set(v) - return x.UnsafeAddr() + return unsafe.Pointer(x.UnsafeAddr()) } // Gob depends on being able to take the address diff --git a/libgo/go/encoding/gob/doc.go b/libgo/go/encoding/gob/doc.go index 5bd61b12eb1..28f0c05a5c5 100644 --- a/libgo/go/encoding/gob/doc.go +++ b/libgo/go/encoding/gob/doc.go @@ -8,6 +8,12 @@ Encoder (transmitter) and a Decoder (receiver). A typical use is transporting arguments and results of remote procedure calls (RPCs) such as those provided by package "rpc". +The implementation compiles a custom codec for each data type in the stream and +is most efficient when a single Encoder is used to transmit a stream of values, +amortizing the cost of compilation. + +Basics + A stream of gobs is self-describing. Each data item in the stream is preceded by a specification of its type, expressed in terms of a small set of predefined types. Pointers are not transmitted, but the things they point to are @@ -20,6 +26,8 @@ all type information is sent before it is needed. At the receive side, a Decoder retrieves values from the encoded stream and unpacks them into local variables. +Types and Values + The source and destination values/types need not correspond exactly. For structs, fields (identified by name) that are in the source but absent from the receiving variable will be ignored. Fields that are in the receiving variable but missing @@ -67,19 +75,29 @@ point values may be received into any floating point variable. However, the destination variable must be able to represent the value or the decode operation will fail. -Structs, arrays and slices are also supported. Structs encode and -decode only exported fields. Strings and arrays of bytes are supported -with a special, efficient representation (see below). When a slice -is decoded, if the existing slice has capacity the slice will be -extended in place; if not, a new array is allocated. Regardless, -the length of the resulting slice reports the number of elements -decoded. +Structs, arrays and slices are also supported. Structs encode and decode only +exported fields. Strings and arrays of bytes are supported with a special, +efficient representation (see below). When a slice is decoded, if the existing +slice has capacity the slice will be extended in place; if not, a new array is +allocated. Regardless, the length of the resulting slice reports the number of +elements decoded. + +Functions and channels will not be sent in a gob. Attempting to encode such a value +at top the level will fail. A struct field of chan or func type is treated exactly +like an unexported field and is ignored. + +Gob can encode a value of any type implementing the GobEncoder, +encoding.BinaryMarshaler, or encoding.TextMarshaler interfaces by calling the +corresponding method, in that order of preference. + +Gob can decode a value of any type implementing the GobDecoder, +encoding.BinaryUnmarshaler, or encoding.TextUnmarshaler interfaces by calling +the corresponding method, again in that order of preference. -Functions and channels cannot be sent in a gob. Attempting -to encode a value that contains one will fail. +Encoding Details -The rest of this comment documents the encoding, details that are not important -for most users. Details are presented bottom-up. +This section documents the encoding, details that are not important for most +users. Details are presented bottom-up. An unsigned integer is sent one of two ways. If it is less than 128, it is sent as a byte with that value. Otherwise it is sent as a minimal-length big-endian diff --git a/libgo/go/encoding/gob/encode.go b/libgo/go/encoding/gob/encode.go index ea37a6cbd58..d158b6442a8 100644 --- a/libgo/go/encoding/gob/encode.go +++ b/libgo/go/encoding/gob/encode.go @@ -6,6 +6,7 @@ package gob import ( "bytes" + "encoding" "math" "reflect" "unsafe" @@ -338,14 +339,14 @@ type encEngine struct { const singletonField = 0 // encodeSingle encodes a single top-level non-struct value. -func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintptr) { +func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep unsafe.Pointer) { state := enc.newEncoderState(b) state.fieldnum = singletonField // There is no surrounding struct to frame the transmission, so we must // generate data even if the item is zero. To do this, set sendZero. state.sendZero = true instr := &engine.instr[singletonField] - p := unsafe.Pointer(basep) // offset will be zero + p := basep // offset will be zero if instr.indir > 0 { if p = encIndirect(p, instr.indir); p == nil { return @@ -356,12 +357,12 @@ func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintp } // encodeStruct encodes a single struct value. -func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintptr) { +func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep unsafe.Pointer) { state := enc.newEncoderState(b) state.fieldnum = -1 for i := 0; i < len(engine.instr); i++ { instr := &engine.instr[i] - p := unsafe.Pointer(basep + instr.offset) + p := unsafe.Pointer(uintptr(basep) + instr.offset) if instr.indir > 0 { if p = encIndirect(p, instr.indir); p == nil { continue @@ -373,22 +374,22 @@ func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintp } // encodeArray encodes the array whose 0th element is at p. -func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid uintptr, elemIndir int, length int) { +func (enc *Encoder) encodeArray(b *bytes.Buffer, p unsafe.Pointer, op encOp, elemWid uintptr, elemIndir int, length int) { state := enc.newEncoderState(b) state.fieldnum = -1 state.sendZero = true state.encodeUint(uint64(length)) for i := 0; i < length; i++ { elemp := p - up := unsafe.Pointer(elemp) if elemIndir > 0 { - if up = encIndirect(up, elemIndir); up == nil { + up := encIndirect(elemp, elemIndir) + if up == nil { errorf("encodeArray: nil element") } - elemp = uintptr(up) + elemp = up } - op(nil, state, unsafe.Pointer(elemp)) - p += uintptr(elemWid) + op(nil, state, elemp) + p = unsafe.Pointer(uintptr(p) + elemWid) } enc.freeEncoderState(state) } @@ -401,7 +402,7 @@ func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir in if !v.IsValid() { errorf("encodeReflectValue: nil element") } - op(nil, state, unsafe.Pointer(unsafeAddr(v))) + op(nil, state, unsafeAddr(v)) } // encodeMap encodes a map as unsigned count followed by key:value pairs. @@ -474,7 +475,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { enc.freeEncoderState(state) } -// isZero returns whether the value is the zero of its type. +// isZero reports whether the value is the zero of its type. func isZero(val reflect.Value) bool { switch val.Kind() { case reflect.Array: @@ -511,10 +512,20 @@ func isZero(val reflect.Value) bool { // encGobEncoder encodes a value that implements the GobEncoder interface. // The data is sent as a byte array. -func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value) { +func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, ut *userTypeInfo, v reflect.Value) { // TODO: should we catch panics from the called method? - // We know it's a GobEncoder, so just call the method directly. - data, err := v.Interface().(GobEncoder).GobEncode() + + var data []byte + var err error + // We know it's one of these. + switch ut.externalEnc { + case xGob: + data, err = v.Interface().(GobEncoder).GobEncode() + case xBinary: + data, err = v.Interface().(encoding.BinaryMarshaler).MarshalBinary() + case xText: + data, err = v.Interface().(encoding.TextMarshaler).MarshalText() + } if err != nil { error_(err) } @@ -550,7 +561,7 @@ var encOpTable = [...]encOp{ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) { ut := userType(rt) // If the type implements GobEncoder, we handle it without further processing. - if ut.isGobEncoder { + if ut.externalEnc != 0 { return enc.gobEncodeOpFor(ut) } // If this type is already in progress, it's a recursive type (e.g. map[string]*T). @@ -575,21 +586,21 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp break } // Slices have a header; we decode it to find the underlying array. - elemOp, indir := enc.encOpFor(t.Elem(), inProgress) + elemOp, elemIndir := enc.encOpFor(t.Elem(), inProgress) op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { slice := (*reflect.SliceHeader)(p) if !state.sendZero && slice.Len == 0 { return } state.update(i) - state.enc.encodeArray(state.b, slice.Data, *elemOp, t.Elem().Size(), indir, int(slice.Len)) + state.enc.encodeArray(state.b, unsafe.Pointer(slice.Data), *elemOp, t.Elem().Size(), elemIndir, int(slice.Len)) } case reflect.Array: // True arrays have size in the type. - elemOp, indir := enc.encOpFor(t.Elem(), inProgress) + elemOp, elemIndir := enc.encOpFor(t.Elem(), inProgress) op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { state.update(i) - state.enc.encodeArray(state.b, uintptr(p), *elemOp, t.Elem().Size(), indir, t.Len()) + state.enc.encodeArray(state.b, p, *elemOp, t.Elem().Size(), elemIndir, t.Len()) } case reflect.Map: keyOp, keyIndir := enc.encOpFor(t.Key(), inProgress) @@ -615,7 +626,7 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { state.update(i) // indirect through info to delay evaluation for recursive structs - state.enc.encodeStruct(state.b, info.encoder, uintptr(p)) + state.enc.encodeStruct(state.b, info.encoder, p) } case reflect.Interface: op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { @@ -661,7 +672,7 @@ func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) { return } state.update(i) - state.enc.encodeGobEncoder(state.b, v) + state.enc.encodeGobEncoder(state.b, ut, v) } return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver. } @@ -672,14 +683,13 @@ func (enc *Encoder) compileEnc(ut *userTypeInfo) *encEngine { engine := new(encEngine) seen := make(map[reflect.Type]*encOp) rt := ut.base - if ut.isGobEncoder { + if ut.externalEnc != 0 { rt = ut.user } - if !ut.isGobEncoder && - srt.Kind() == reflect.Struct { + if ut.externalEnc == 0 && srt.Kind() == reflect.Struct { for fieldNum, wireFieldNum := 0, 0; fieldNum < srt.NumField(); fieldNum++ { f := srt.Field(fieldNum) - if !isExported(f.Name) { + if !isSent(&f) { continue } op, indir := enc.encOpFor(f.Type, seen) @@ -736,13 +746,13 @@ func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInf defer catchError(&enc.err) engine := enc.lockAndGetEncEngine(ut) indir := ut.indir - if ut.isGobEncoder { + if ut.externalEnc != 0 { indir = int(ut.encIndir) } for i := 0; i < indir; i++ { value = reflect.Indirect(value) } - if !ut.isGobEncoder && value.Type().Kind() == reflect.Struct { + if ut.externalEnc == 0 && value.Type().Kind() == reflect.Struct { enc.encodeStruct(b, engine, unsafeAddr(value)) } else { enc.encodeSingle(b, engine, unsafeAddr(value)) diff --git a/libgo/go/encoding/gob/encoder.go b/libgo/go/encoding/gob/encoder.go index f669c3d5b24..a3301c3bd33 100644 --- a/libgo/go/encoding/gob/encoder.go +++ b/libgo/go/encoding/gob/encoder.go @@ -6,7 +6,6 @@ package gob import ( "bytes" - "errors" "io" "reflect" "sync" @@ -54,10 +53,6 @@ func (enc *Encoder) popWriter() { enc.w = enc.w[0 : len(enc.w)-1] } -func (enc *Encoder) badType(rt reflect.Type) { - enc.setError(errors.New("gob: can't encode type " + rt.String())) -} - func (enc *Encoder) setError(err error) { if enc.err == nil { // remember the first. enc.err = err @@ -135,7 +130,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp // sendType sends the type info to the other side, if necessary. func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { ut := userType(origt) - if ut.isGobEncoder { + if ut.externalEnc != 0 { // The rules are different: regardless of the underlying type's representation, // we need to tell the other side that the base type is a GobEncoder. return enc.sendActualType(w, state, ut, ut.base) @@ -163,8 +158,7 @@ func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Typ // structs must be sent so we know their fields. break case reflect.Chan, reflect.Func: - // Probably a bad field in a struct. - enc.badType(rt) + // If we get here, it's a field of a struct; ignore it. return } @@ -184,7 +178,7 @@ func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *use // Make sure the type is known to the other side. // First, have we already sent this type? rt := ut.base - if ut.isGobEncoder { + if ut.externalEnc != 0 { rt = ut.user } if _, alreadySent := enc.sent[rt]; !alreadySent { diff --git a/libgo/go/encoding/gob/encoder_test.go b/libgo/go/encoding/gob/encoder_test.go index b684772c691..4ecf51d122b 100644 --- a/libgo/go/encoding/gob/encoder_test.go +++ b/libgo/go/encoding/gob/encoder_test.go @@ -131,7 +131,7 @@ func TestBadData(t *testing.T) { corruptDataCheck("\x03now is the time for all good men", errBadType, t) } -// Types not supported by the Encoder. +// Types not supported at top level by the Encoder. var unsupportedValues = []interface{}{ make(chan int), func(a int) bool { return true }, @@ -662,19 +662,35 @@ func TestSequentialDecoder(t *testing.T) { } } -// Should be able to have unrepresentable fields (chan, func) as long as they -// are unexported. +// Should be able to have unrepresentable fields (chan, func, *chan etc.); we just ignore them. type Bug2 struct { - A int - b chan int -} - -func TestUnexportedChan(t *testing.T) { - b := Bug2{23, make(chan int)} - var stream bytes.Buffer - enc := NewEncoder(&stream) - if err := enc.Encode(b); err != nil { - t.Fatalf("error encoding unexported channel: %s", err) + A int + C chan int + CP *chan int + F func() + FPP **func() +} + +func TestChanFuncIgnored(t *testing.T) { + c := make(chan int) + f := func() {} + fp := &f + b0 := Bug2{23, c, &c, f, &fp} + var buf bytes.Buffer + enc := NewEncoder(&buf) + if err := enc.Encode(b0); err != nil { + t.Fatal("error encoding:", err) + } + var b1 Bug2 + err := NewDecoder(&buf).Decode(&b1) + if err != nil { + t.Fatal("decode:", err) + } + if b1.A != b0.A { + t.Fatalf("got %d want %d", b1.A, b0.A) + } + if b1.C != nil || b1.CP != nil || b1.F != nil || b1.FPP != nil { + t.Fatal("unexpected value for chan or func") } } diff --git a/libgo/go/encoding/gob/gobencdec_test.go b/libgo/go/encoding/gob/gobencdec_test.go index ddcd80b1a7a..301551db48a 100644 --- a/libgo/go/encoding/gob/gobencdec_test.go +++ b/libgo/go/encoding/gob/gobencdec_test.go @@ -34,6 +34,14 @@ type Gobber int type ValueGobber string // encodes with a value, decodes with a pointer. +type BinaryGobber int + +type BinaryValueGobber string + +type TextGobber int + +type TextValueGobber string + // The relevant methods func (g *ByteStruct) GobEncode() ([]byte, error) { @@ -101,6 +109,24 @@ func (g *Gobber) GobDecode(data []byte) error { return err } +func (g *BinaryGobber) MarshalBinary() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%d", *g)), nil +} + +func (g *BinaryGobber) UnmarshalBinary(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) + return err +} + +func (g *TextGobber) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%d", *g)), nil +} + +func (g *TextGobber) UnmarshalText(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) + return err +} + func (v ValueGobber) GobEncode() ([]byte, error) { return []byte(fmt.Sprintf("VALUE=%s", v)), nil } @@ -110,6 +136,24 @@ func (v *ValueGobber) GobDecode(data []byte) error { return err } +func (v BinaryValueGobber) MarshalBinary() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%s", v)), nil +} + +func (v *BinaryValueGobber) UnmarshalBinary(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) + return err +} + +func (v TextValueGobber) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%s", v)), nil +} + +func (v *TextValueGobber) UnmarshalText(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) + return err +} + // Structs that include GobEncodable fields. type GobTest0 struct { @@ -130,28 +174,42 @@ type GobTest2 struct { type GobTest3 struct { X int // guarantee we have something in common with GobTest* G *Gobber + B *BinaryGobber + T *TextGobber } type GobTest4 struct { - X int // guarantee we have something in common with GobTest* - V ValueGobber + X int // guarantee we have something in common with GobTest* + V ValueGobber + BV BinaryValueGobber + TV TextValueGobber } type GobTest5 struct { - X int // guarantee we have something in common with GobTest* - V *ValueGobber + X int // guarantee we have something in common with GobTest* + V *ValueGobber + BV *BinaryValueGobber + TV *TextValueGobber } type GobTest6 struct { - X int // guarantee we have something in common with GobTest* - V ValueGobber - W *ValueGobber + X int // guarantee we have something in common with GobTest* + V ValueGobber + W *ValueGobber + BV BinaryValueGobber + BW *BinaryValueGobber + TV TextValueGobber + TW *TextValueGobber } type GobTest7 struct { - X int // guarantee we have something in common with GobTest* - V *ValueGobber - W ValueGobber + X int // guarantee we have something in common with GobTest* + V *ValueGobber + W ValueGobber + BV *BinaryValueGobber + BW BinaryValueGobber + TV *TextValueGobber + TW TextValueGobber } type GobTestIgnoreEncoder struct { @@ -198,7 +256,9 @@ func TestGobEncoderField(t *testing.T) { // Now a field that's not a structure. b.Reset() gobber := Gobber(23) - err = enc.Encode(GobTest3{17, &gobber}) + bgobber := BinaryGobber(24) + tgobber := TextGobber(25) + err = enc.Encode(GobTest3{17, &gobber, &bgobber, &tgobber}) if err != nil { t.Fatal("encode error:", err) } @@ -207,7 +267,7 @@ func TestGobEncoderField(t *testing.T) { if err != nil { t.Fatal("decode error:", err) } - if *y.G != 23 { + if *y.G != 23 || *y.B != 24 || *y.T != 25 { t.Errorf("expected '23 got %d", *y.G) } } @@ -357,7 +417,7 @@ func TestGobEncoderValueEncoder(t *testing.T) { // first, string in field to byte in field b := new(bytes.Buffer) enc := NewEncoder(b) - err := enc.Encode(GobTest4{17, ValueGobber("hello")}) + err := enc.Encode(GobTest4{17, ValueGobber("hello"), BinaryValueGobber("Καλημέρα"), TextValueGobber("こんにちは")}) if err != nil { t.Fatal("encode error:", err) } @@ -367,8 +427,8 @@ func TestGobEncoderValueEncoder(t *testing.T) { if err != nil { t.Fatal("decode error:", err) } - if *x.V != "hello" { - t.Errorf("expected `hello` got %s", x.V) + if *x.V != "hello" || *x.BV != "Καλημέρα" || *x.TV != "こんにちは" { + t.Errorf("expected `hello` got %s", *x.V) } } @@ -377,13 +437,17 @@ func TestGobEncoderValueEncoder(t *testing.T) { func TestGobEncoderValueThenPointer(t *testing.T) { v := ValueGobber("forty-two") w := ValueGobber("six-by-nine") + bv := BinaryValueGobber("1nanocentury") + bw := BinaryValueGobber("πseconds") + tv := TextValueGobber("gravitationalacceleration") + tw := TextValueGobber("π²ft/s²") // this was a bug: encoding a GobEncoder by value before a GobEncoder // pointer would cause duplicate type definitions to be sent. b := new(bytes.Buffer) enc := NewEncoder(b) - if err := enc.Encode(GobTest6{42, v, &w}); err != nil { + if err := enc.Encode(GobTest6{42, v, &w, bv, &bw, tv, &tw}); err != nil { t.Fatal("encode error:", err) } dec := NewDecoder(b) @@ -391,6 +455,7 @@ func TestGobEncoderValueThenPointer(t *testing.T) { if err := dec.Decode(x); err != nil { t.Fatal("decode error:", err) } + if got, want := x.V, v; got != want { t.Errorf("v = %q, want %q", got, want) } @@ -399,6 +464,24 @@ func TestGobEncoderValueThenPointer(t *testing.T) { } else if *got != want { t.Errorf("w = %q, want %q", *got, want) } + + if got, want := x.BV, bv; got != want { + t.Errorf("bv = %q, want %q", got, want) + } + if got, want := x.BW, bw; got == nil { + t.Errorf("bw = nil, want %q", want) + } else if *got != want { + t.Errorf("bw = %q, want %q", *got, want) + } + + if got, want := x.TV, tv; got != want { + t.Errorf("tv = %q, want %q", got, want) + } + if got, want := x.TW, tw; got == nil { + t.Errorf("tw = nil, want %q", want) + } else if *got != want { + t.Errorf("tw = %q, want %q", *got, want) + } } // Test that we can use a pointer then a value type of a GobEncoder @@ -406,10 +489,14 @@ func TestGobEncoderValueThenPointer(t *testing.T) { func TestGobEncoderPointerThenValue(t *testing.T) { v := ValueGobber("forty-two") w := ValueGobber("six-by-nine") + bv := BinaryValueGobber("1nanocentury") + bw := BinaryValueGobber("πseconds") + tv := TextValueGobber("gravitationalacceleration") + tw := TextValueGobber("π²ft/s²") b := new(bytes.Buffer) enc := NewEncoder(b) - if err := enc.Encode(GobTest7{42, &v, w}); err != nil { + if err := enc.Encode(GobTest7{42, &v, w, &bv, bw, &tv, tw}); err != nil { t.Fatal("encode error:", err) } dec := NewDecoder(b) @@ -417,14 +504,33 @@ func TestGobEncoderPointerThenValue(t *testing.T) { if err := dec.Decode(x); err != nil { t.Fatal("decode error:", err) } + if got, want := x.V, v; got == nil { t.Errorf("v = nil, want %q", want) } else if *got != want { - t.Errorf("v = %q, want %q", got, want) + t.Errorf("v = %q, want %q", *got, want) } if got, want := x.W, w; got != want { t.Errorf("w = %q, want %q", got, want) } + + if got, want := x.BV, bv; got == nil { + t.Errorf("bv = nil, want %q", want) + } else if *got != want { + t.Errorf("bv = %q, want %q", *got, want) + } + if got, want := x.BW, bw; got != want { + t.Errorf("bw = %q, want %q", got, want) + } + + if got, want := x.TV, tv; got == nil { + t.Errorf("tv = nil, want %q", want) + } else if *got != want { + t.Errorf("tv = %q, want %q", *got, want) + } + if got, want := x.TW, tw; got != want { + t.Errorf("tw = %q, want %q", got, want) + } } func TestGobEncoderFieldTypeError(t *testing.T) { @@ -521,7 +627,9 @@ func TestGobEncoderIgnoreNonStructField(t *testing.T) { // First a field that's a structure. enc := NewEncoder(b) gobber := Gobber(23) - err := enc.Encode(GobTest3{17, &gobber}) + bgobber := BinaryGobber(24) + tgobber := TextGobber(25) + err := enc.Encode(GobTest3{17, &gobber, &bgobber, &tgobber}) if err != nil { t.Fatal("encode error:", err) } diff --git a/libgo/go/encoding/gob/timing_test.go b/libgo/go/encoding/gob/timing_test.go index f589675dd98..9fbb0ac6d5a 100644 --- a/libgo/go/encoding/gob/timing_test.go +++ b/libgo/go/encoding/gob/timing_test.go @@ -6,7 +6,6 @@ package gob import ( "bytes" - "fmt" "io" "os" "runtime" @@ -50,6 +49,9 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) { } func TestCountEncodeMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -66,10 +68,15 @@ func TestCountEncodeMallocs(t *testing.T) { t.Fatal("encode:", err) } }) - fmt.Printf("mallocs per encode of type Bench: %v\n", allocs) + if allocs != 0 { + t.Fatalf("mallocs per encode of type Bench: %v; wanted 0\n", allocs) + } } func TestCountDecodeMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -96,5 +103,7 @@ func TestCountDecodeMallocs(t *testing.T) { t.Fatal("decode:", err) } }) - fmt.Printf("mallocs per decode of type Bench: %v\n", allocs) + if allocs != 3 { + t.Fatalf("mallocs per decode of type Bench: %v; wanted 3\n", allocs) + } } diff --git a/libgo/go/encoding/gob/type.go b/libgo/go/encoding/gob/type.go index 7fa0b499f02..65bf17b7f02 100644 --- a/libgo/go/encoding/gob/type.go +++ b/libgo/go/encoding/gob/type.go @@ -5,6 +5,7 @@ package gob import ( + "encoding" "errors" "fmt" "os" @@ -18,14 +19,21 @@ import ( // to the package. It's computed once and stored in a map keyed by reflection // type. type userTypeInfo struct { - user reflect.Type // the type the user handed us - base reflect.Type // the base type after all indirections - indir int // number of indirections to reach the base type - isGobEncoder bool // does the type implement GobEncoder? - isGobDecoder bool // does the type implement GobDecoder? - encIndir int8 // number of indirections to reach the receiver type; may be negative - decIndir int8 // number of indirections to reach the receiver type; may be negative -} + user reflect.Type // the type the user handed us + base reflect.Type // the base type after all indirections + indir int // number of indirections to reach the base type + externalEnc int // xGob, xBinary, or xText + externalDec int // xGob, xBinary or xText + encIndir int8 // number of indirections to reach the receiver type; may be negative + decIndir int8 // number of indirections to reach the receiver type; may be negative +} + +// externalEncoding bits +const ( + xGob = 1 + iota // GobEncoder or GobDecoder + xBinary // encoding.BinaryMarshaler or encoding.BinaryUnmarshaler + xText // encoding.TextMarshaler or encoding.TextUnmarshaler +) var ( // Protected by an RWMutex because we read it a lot and write @@ -75,15 +83,34 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err error) { } ut.indir++ } - ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderInterfaceType) - ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderInterfaceType) + + if ok, indir := implementsInterface(ut.user, gobEncoderInterfaceType); ok { + ut.externalEnc, ut.encIndir = xGob, indir + } else if ok, indir := implementsInterface(ut.user, binaryMarshalerInterfaceType); ok { + ut.externalEnc, ut.encIndir = xBinary, indir + } else if ok, indir := implementsInterface(ut.user, textMarshalerInterfaceType); ok { + ut.externalEnc, ut.encIndir = xText, indir + } + + if ok, indir := implementsInterface(ut.user, gobDecoderInterfaceType); ok { + ut.externalDec, ut.decIndir = xGob, indir + } else if ok, indir := implementsInterface(ut.user, binaryUnmarshalerInterfaceType); ok { + ut.externalDec, ut.decIndir = xBinary, indir + } else if ok, indir := implementsInterface(ut.user, textUnmarshalerInterfaceType); ok { + ut.externalDec, ut.decIndir = xText, indir + } + userTypeCache[rt] = ut return } var ( - gobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem() - gobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem() + gobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem() + gobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem() + binaryMarshalerInterfaceType = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem() + binaryUnmarshalerInterfaceType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() + textMarshalerInterfaceType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + textUnmarshalerInterfaceType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() ) // implementsInterface reports whether the type implements the @@ -412,7 +439,7 @@ func newStructType(name string) *structType { // works through typeIds and userTypeInfos alone. func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, error) { // Does this type implement GobEncoder? - if ut.isGobEncoder { + if ut.externalEnc != 0 { return newGobEncoderType(name), nil } var err error @@ -499,7 +526,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, err idToType[st.id()] = st for i := 0; i < t.NumField(); i++ { f := t.Field(i) - if !isExported(f.Name) { + if !isSent(&f) { continue } typ := userType(f.Type).base @@ -534,6 +561,25 @@ func isExported(name string) bool { return unicode.IsUpper(rune) } +// isSent reports whether this struct field is to be transmitted. +// It will be transmitted only if it is exported and not a chan or func field +// or pointer to chan or func. +func isSent(field *reflect.StructField) bool { + if !isExported(field.Name) { + return false + } + // If the field is a chan or func or pointer thereto, don't send it. + // That is, treat it like an unexported field. + typ := field.Type + for typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + if typ.Kind() == reflect.Chan || typ.Kind() == reflect.Func { + return false + } + return true +} + // getBaseType returns the Gob type describing the given reflect.Type's base type. // typeLock must be held. func getBaseType(name string, rt reflect.Type) (gobType, error) { @@ -593,11 +639,13 @@ func bootstrapType(name string, e interface{}, expect typeId) typeId { // To maintain binary compatibility, if you extend this type, always put // the new fields last. type wireType struct { - ArrayT *arrayType - SliceT *sliceType - StructT *structType - MapT *mapType - GobEncoderT *gobEncoderType + ArrayT *arrayType + SliceT *sliceType + StructT *structType + MapT *mapType + GobEncoderT *gobEncoderType + BinaryMarshalerT *gobEncoderType + TextMarshalerT *gobEncoderType } func (w *wireType) string() string { @@ -616,6 +664,10 @@ func (w *wireType) string() string { return w.MapT.Name case w.GobEncoderT != nil: return w.GobEncoderT.Name + case w.BinaryMarshalerT != nil: + return w.BinaryMarshalerT.Name + case w.TextMarshalerT != nil: + return w.TextMarshalerT.Name } return unknown } @@ -631,7 +683,7 @@ var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock // typeLock must be held. func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { rt := ut.base - if ut.isGobEncoder { + if ut.externalEnc != 0 { // We want the user type, not the base type. rt = ut.user } @@ -646,12 +698,20 @@ func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { } info.id = gt.id() - if ut.isGobEncoder { + if ut.externalEnc != 0 { userType, err := getType(rt.Name(), ut, rt) if err != nil { return nil, err } - info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)} + gt := userType.id().gobType().(*gobEncoderType) + switch ut.externalEnc { + case xGob: + info.wire = &wireType{GobEncoderT: gt} + case xBinary: + info.wire = &wireType{BinaryMarshalerT: gt} + case xText: + info.wire = &wireType{TextMarshalerT: gt} + } typeInfoMap[ut.user] = info return info, nil } |

