1
0
mirror of https://github.com/golang/go synced 2024-11-24 21:10:04 -07:00

gob: enable the GobEncoder and GobDecoder interfaces.

These allow data items to control their own representation.

For now, the implementation requires that the value passed
to Encode and Decode must be exactly the type of the
methods' receiver; it cannot be, for instance, T if the receiver
is of type *T.  This will be fixed in a later CL.

R=rsc
CC=golang-dev
https://golang.org/cl/4235051
This commit is contained in:
Rob Pike 2011-03-04 14:18:52 -08:00
parent 99f17aa0b8
commit 9442e9518d
4 changed files with 52 additions and 24 deletions

View File

@ -738,7 +738,7 @@ func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value, index
error(err) error(err)
} }
// We know it's a GobDecoder, so just call the method directly. // We know it's a GobDecoder, so just call the method directly.
err = v.Interface().(_GobDecoder)._GobDecode(b) err = v.Interface().(GobDecoder).GobDecode(b)
if err != nil { if err != nil {
error(err) error(err)
} }

View File

@ -449,7 +449,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv *reflect.InterfaceValue)
func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value, index int) { func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value, index int) {
// TODO: should we catch panics from the called method? // TODO: should we catch panics from the called method?
// We know it's a GobEncoder, so just call the method directly. // We know it's a GobEncoder, so just call the method directly.
data, err := v.Interface().(_GobEncoder)._GobEncode() data, err := v.Interface().(GobEncoder).GobEncode()
if err != nil { if err != nil {
error(err) error(err)
} }

View File

@ -30,7 +30,7 @@ type ValueGobber string // encodes with a value, decodes with a pointer.
// The relevant methods // The relevant methods
func (g *ByteStruct) _GobEncode() ([]byte, os.Error) { func (g *ByteStruct) GobEncode() ([]byte, os.Error) {
b := make([]byte, 3) b := make([]byte, 3)
b[0] = g.a b[0] = g.a
b[1] = g.a + 1 b[1] = g.a + 1
@ -38,7 +38,7 @@ func (g *ByteStruct) _GobEncode() ([]byte, os.Error) {
return b, nil return b, nil
} }
func (g *ByteStruct) _GobDecode(data []byte) os.Error { func (g *ByteStruct) GobDecode(data []byte) os.Error {
if g == nil { if g == nil {
return os.ErrorString("NIL RECEIVER") return os.ErrorString("NIL RECEIVER")
} }
@ -55,11 +55,11 @@ func (g *ByteStruct) _GobDecode(data []byte) os.Error {
return nil return nil
} }
func (g *StringStruct) _GobEncode() ([]byte, os.Error) { func (g *StringStruct) GobEncode() ([]byte, os.Error) {
return []byte(g.s), nil return []byte(g.s), nil
} }
func (g *StringStruct) _GobDecode(data []byte) os.Error { func (g *StringStruct) GobDecode(data []byte) os.Error {
// Expect N sequential-valued bytes. // Expect N sequential-valued bytes.
if len(data) == 0 { if len(data) == 0 {
return os.EOF return os.EOF
@ -74,20 +74,20 @@ func (g *StringStruct) _GobDecode(data []byte) os.Error {
return nil return nil
} }
func (g *Gobber) _GobEncode() ([]byte, os.Error) { func (g *Gobber) GobEncode() ([]byte, os.Error) {
return []byte(fmt.Sprintf("VALUE=%d", *g)), nil return []byte(fmt.Sprintf("VALUE=%d", *g)), nil
} }
func (g *Gobber) _GobDecode(data []byte) os.Error { func (g *Gobber) GobDecode(data []byte) os.Error {
_, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g))
return err return err
} }
func (v ValueGobber) _GobEncode() ([]byte, os.Error) { func (v ValueGobber) GobEncode() ([]byte, os.Error) {
return []byte(fmt.Sprintf("VALUE=%s", v)), nil return []byte(fmt.Sprintf("VALUE=%s", v)), nil
} }
func (v *ValueGobber) _GobDecode(data []byte) os.Error { func (v *ValueGobber) GobDecode(data []byte) os.Error {
_, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v))
return err return err
} }
@ -232,7 +232,7 @@ func TestGobEncoderFieldTypeError(t *testing.T) {
x := &GobTest2{} x := &GobTest2{}
err = dec.Decode(x) err = dec.Decode(x)
if err == nil { if err == nil {
t.Fatal("expected decode error for mistmatched fields (encoder to non-decoder)") t.Fatal("expected decode error for mismatched fields (encoder to non-decoder)")
} }
if strings.Index(err.String(), "type") < 0 { if strings.Index(err.String(), "type") < 0 {
t.Fatal("expected type error; got", err) t.Fatal("expected type error; got", err)

View File

@ -18,8 +18,8 @@ type userTypeInfo struct {
user reflect.Type // the type the user handed us user reflect.Type // the type the user handed us
base reflect.Type // the base type after all indirections base reflect.Type // the base type after all indirections
indir int // number of indirections to reach the base type indir int // number of indirections to reach the base type
isGobEncoder bool // does the type implement _GobEncoder? isGobEncoder bool // does the type implement GobEncoder?
isGobDecoder bool // does the type implement _GobDecoder? isGobDecoder bool // does the type implement GobDecoder?
encIndir int8 // number of indirections to reach the receiver type; may be negative 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 decIndir int8 // number of indirections to reach the receiver type; may be negative
} }
@ -86,8 +86,8 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) {
} }
const ( const (
gobEncodeMethodName = "_GobEncode" gobEncodeMethodName = "GobEncode"
gobDecodeMethodName = "_GobDecode" gobDecodeMethodName = "GobDecode"
) )
// implementsGobEncoder reports whether the type implements the interface. It also // implementsGobEncoder reports whether the type implements the interface. It also
@ -104,7 +104,7 @@ func implementsGobEncoder(rt reflect.Type) (implements bool, indir int8) {
// dereferencing to the base type until we find an implementation. // dereferencing to the base type until we find an implementation.
for { for {
if rt.NumMethod() > 0 { // avoid allocations etc. unless there's some chance if rt.NumMethod() > 0 { // avoid allocations etc. unless there's some chance
if _, ok := reflect.MakeZero(rt).Interface().(_GobEncoder); ok { if _, ok := reflect.MakeZero(rt).Interface().(GobEncoder); ok {
return true, indir return true, indir
} }
} }
@ -132,7 +132,7 @@ func implementsGobDecoder(rt reflect.Type) (implements bool, indir int8) {
// dereferencing to the base type until we find an implementation. // dereferencing to the base type until we find an implementation.
for { for {
if rt.NumMethod() > 0 { // avoid allocations etc. unless there's some chance if rt.NumMethod() > 0 { // avoid allocations etc. unless there's some chance
if _, ok := reflect.MakeZero(rt).Interface().(_GobDecoder); ok { if _, ok := reflect.MakeZero(rt).Interface().(GobDecoder); ok {
return true, indir return true, indir
} }
} }
@ -306,7 +306,7 @@ func (a *arrayType) safeString(seen map[typeId]bool) string {
func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) } func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) }
// GobEncoder type (something that implements the _GobEncoder interface) // GobEncoder type (something that implements the GobEncoder interface)
type gobEncoderType struct { type gobEncoderType struct {
CommonType CommonType
} }
@ -695,12 +695,40 @@ func mustGetTypeInfo(rt reflect.Type) *typeInfo {
return t return t
} }
type _GobEncoder interface { // GobEncoder is the interface describing data that provides its own
_GobEncode() ([]byte, os.Error) // representation for encoding values for transmission to a GobDecoder.
} // use _ prefix until we get it working properly // A type that implements GobEncoder and GobDecoder has complete
type _GobDecoder interface { // control over the representation of its data and may therefore
_GobDecode([]byte) os.Error // contain things such as private fields, channels, and functions,
} // use _ prefix until we get it working properly // which are not usually transmissable in gob streams.
//
// Note: Since gobs can be stored permanently, It is good design
// to guarantee the encoding used by a GobEncoder is stable as the
// software evolves. For instance, it might make sense for GobEncode
// to include a version number in the encoding.
//
// Note: At the moment, the type implementing GobEncoder must
// be exactly the type passed to Encode. For example, if *T implements
// GobEncoder, the data item must be of type *T, not T or **T.
type GobEncoder interface {
// GobEncode returns a byte slice representing the encoding of the
// receiver for transmission to a GobDecoder, usually of the same
// concrete type.
GobEncode() ([]byte, os.Error)
}
// GobDecoder is the interface describing data that provides its own
// routine for decoding transmitted values sent by a GobEncoder.
//
// Note: At the moment, the type implementing GobDecoder must
// be exactly the type passed to Decode. For example, if *T implements
// GobDecoder, the data item must be of type *T, not T or **T.
type GobDecoder interface {
// GobDecode overwrites the receiver, which must be a pointer,
// with the value represented by the byte slice, which was written
// by GobEncode, usually for the same concrete type.
GobDecode([]byte) os.Error
}
var ( var (
nameToConcreteType = make(map[string]reflect.Type) nameToConcreteType = make(map[string]reflect.Type)