mirror of
https://github.com/golang/go
synced 2024-11-25 12:57:58 -07:00
store ids rather than Types in the structs so they can be encoded.
change Type to gobType. fix some bugs around recursive structures. lots of cleanup. add the first cut at a type encoder. R=rsc DELTA=400 (287 added, 11 deleted, 102 changed) OCL=31401 CL=31406
This commit is contained in:
parent
7472f4c951
commit
ec23467e65
@ -36,10 +36,11 @@ O1=\
|
||||
type.$O\
|
||||
|
||||
O2=\
|
||||
decode.$O\
|
||||
encode.$O\
|
||||
|
||||
O3=\
|
||||
decode.$O\
|
||||
encoder.$O\
|
||||
|
||||
|
||||
phases: a1 a2 a3
|
||||
@ -50,11 +51,11 @@ a1: $(O1)
|
||||
rm -f $(O1)
|
||||
|
||||
a2: $(O2)
|
||||
$(AR) grc _obj$D/gob.a encode.$O
|
||||
$(AR) grc _obj$D/gob.a decode.$O encode.$O
|
||||
rm -f $(O2)
|
||||
|
||||
a3: $(O3)
|
||||
$(AR) grc _obj$D/gob.a decode.$O
|
||||
$(AR) grc _obj$D/gob.a encoder.$O
|
||||
rm -f $(O3)
|
||||
|
||||
|
||||
|
@ -13,7 +13,6 @@ import (
|
||||
"testing";
|
||||
"unsafe";
|
||||
)
|
||||
import "fmt" // TODO DELETE
|
||||
|
||||
// Guarantee encoding format by comparing some encodings to hand-written values
|
||||
type EncodeT struct {
|
||||
@ -560,6 +559,30 @@ func TestEndToEnd(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNesting(t *testing.T) {
|
||||
type RT struct {
|
||||
a string;
|
||||
next *RT
|
||||
}
|
||||
rt := new(RT);
|
||||
rt.a = "level1";
|
||||
rt.next = new(RT);
|
||||
rt.next.a = "level2";
|
||||
b := new(bytes.Buffer);
|
||||
Encode(b, rt);
|
||||
var drt RT;
|
||||
Decode(b, &drt);
|
||||
if drt.a != rt.a {
|
||||
t.Errorf("nesting: encode expected %v got %v", *rt, drt);
|
||||
}
|
||||
if drt.next == nil {
|
||||
t.Errorf("nesting: recursion failed");
|
||||
}
|
||||
if drt.next.a != rt.next.a {
|
||||
t.Errorf("nesting: encode expected %v got %v", *rt.next, *drt.next);
|
||||
}
|
||||
}
|
||||
|
||||
// These three structures have the same data with different indirections
|
||||
type T0 struct {
|
||||
a int;
|
||||
|
@ -199,6 +199,16 @@ func decUint64(i *decInstr, state *DecState, p unsafe.Pointer) {
|
||||
*(*uint64)(p) = uint64(DecodeUint(state));
|
||||
}
|
||||
|
||||
func decUintptr(i *decInstr, state *DecState, p unsafe.Pointer) {
|
||||
if i.indir > 0 {
|
||||
if *(*unsafe.Pointer)(p) == nil {
|
||||
*(*unsafe.Pointer)(p) = unsafe.Pointer(new(uintptr));
|
||||
}
|
||||
p = *(*unsafe.Pointer)(p);
|
||||
}
|
||||
*(*uintptr)(p) = uintptr(DecodeUint(state));
|
||||
}
|
||||
|
||||
// Floating-point numbers are transmitted as uint64s holding the bits
|
||||
// of the underlying representation. They are sent byte-reversed, with
|
||||
// the exponent end coming out first, so integer floating point numbers
|
||||
@ -367,7 +377,6 @@ func decodeSlice(atyp *reflect.SliceType, state *DecState, p uintptr, elemOp dec
|
||||
return decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, int(length), elemIndir);
|
||||
}
|
||||
|
||||
var decEngineMap = make(map[reflect.Type] *decEngine)
|
||||
var decOpMap = map[reflect.Type] decOp {
|
||||
reflect.Typeof((*reflect.BoolType)(nil)): decBool,
|
||||
reflect.Typeof((*reflect.IntType)(nil)): decInt,
|
||||
@ -380,6 +389,7 @@ var decOpMap = map[reflect.Type] decOp {
|
||||
reflect.Typeof((*reflect.Uint16Type)(nil)): decUint16,
|
||||
reflect.Typeof((*reflect.Uint32Type)(nil)): decUint32,
|
||||
reflect.Typeof((*reflect.Uint64Type)(nil)): decUint64,
|
||||
reflect.Typeof((*reflect.UintptrType)(nil)): decUintptr,
|
||||
reflect.Typeof((*reflect.FloatType)(nil)): decFloat,
|
||||
reflect.Typeof((*reflect.Float32Type)(nil)): decFloat32,
|
||||
reflect.Typeof((*reflect.Float64Type)(nil)): decFloat64,
|
||||
@ -415,8 +425,10 @@ func decOpFor(rt reflect.Type) (decOp, int) {
|
||||
case *reflect.StructType:
|
||||
// Generate a closure that calls out to the engine for the nested type.
|
||||
engine := getDecEngine(typ);
|
||||
info := getTypeInfo(typ);
|
||||
op = func(i *decInstr, state *DecState, p unsafe.Pointer) {
|
||||
state.err = decodeStruct(engine, t, state.r, uintptr(p), i.indir)
|
||||
// indirect through info to delay evaluation for recursive structs
|
||||
state.err = decodeStruct(info.decoder, t, state.r, uintptr(p), i.indir)
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -426,7 +438,7 @@ func decOpFor(rt reflect.Type) (decOp, int) {
|
||||
return op, indir
|
||||
}
|
||||
|
||||
func compileDec(rt reflect.Type, typ Type) *decEngine {
|
||||
func compileDec(rt reflect.Type, typ gobType) *decEngine {
|
||||
srt, ok1 := rt.(*reflect.StructType);
|
||||
styp, ok2 := typ.(*structType);
|
||||
if !ok1 || !ok2 {
|
||||
@ -436,8 +448,8 @@ func compileDec(rt reflect.Type, typ Type) *decEngine {
|
||||
engine.instr = make([]decInstr, len(styp.field));
|
||||
for fieldnum := 0; fieldnum < len(styp.field); fieldnum++ {
|
||||
field := styp.field[fieldnum];
|
||||
// TODO(r): verify compatibility with corresponding field of data.
|
||||
// For now, assume perfect correspondence between struct and gob.
|
||||
// Assumes perfect correspondence between struct and gob,
|
||||
// which is safe to assume since typ was compiled from rt.
|
||||
f := srt.Field(fieldnum);
|
||||
op, indir := decOpFor(f.Type);
|
||||
engine.instr[fieldnum] = decInstr{op, fieldnum, indir, uintptr(f.Offset)};
|
||||
@ -446,14 +458,19 @@ func compileDec(rt reflect.Type, typ Type) *decEngine {
|
||||
}
|
||||
|
||||
|
||||
// typeLock must be held.
|
||||
func getDecEngine(rt reflect.Type) *decEngine {
|
||||
engine, ok := decEngineMap[rt];
|
||||
if !ok {
|
||||
pkg, name := rt.Name();
|
||||
engine = compileDec(rt, newType(name, rt));
|
||||
decEngineMap[rt] = engine;
|
||||
info := getTypeInfo(rt);
|
||||
if info.decoder == nil {
|
||||
if info.typeId.gobType() == nil {
|
||||
_pkg, name := rt.Name();
|
||||
info.typeId = newType(name, rt).id();
|
||||
}
|
||||
// mark this engine as underway before compiling to handle recursive types.
|
||||
info.decoder = new(decEngine);
|
||||
info.decoder = compileDec(rt, info.typeId.gobType());
|
||||
}
|
||||
return engine;
|
||||
return info.decoder;
|
||||
}
|
||||
|
||||
func Decode(r io.Reader, e interface{}) os.Error {
|
||||
|
@ -183,6 +183,14 @@ func encUint64(i *encInstr, state *EncState, p unsafe.Pointer) {
|
||||
}
|
||||
}
|
||||
|
||||
func encUintptr(i *encInstr, state *EncState, p unsafe.Pointer) {
|
||||
v := uint64(*(*uintptr)(p));
|
||||
if v != 0 {
|
||||
state.update(i);
|
||||
EncodeUint(state, v);
|
||||
}
|
||||
}
|
||||
|
||||
// Floating-point numbers are transmitted as uint64s holding the bits
|
||||
// of the underlying representation. They are sent byte-reversed, with
|
||||
// the exponent end coming out first, so integer floating point numbers
|
||||
@ -285,21 +293,21 @@ func encodeArray(w io.Writer, p uintptr, op encOp, elemWid uintptr, length int,
|
||||
state.fieldnum = -1;
|
||||
EncodeUint(state, uint64(length));
|
||||
for i := 0; i < length && state.err == nil; i++ {
|
||||
up := unsafe.Pointer(p);
|
||||
elemp := p;
|
||||
up := unsafe.Pointer(elemp);
|
||||
if elemIndir > 0 {
|
||||
if up = encIndirect(up, elemIndir); up == nil {
|
||||
state.err = os.ErrorString("encodeArray: nil element");
|
||||
break
|
||||
}
|
||||
p = uintptr(up);
|
||||
elemp = uintptr(up);
|
||||
}
|
||||
op(nil, state, unsafe.Pointer(p));
|
||||
op(nil, state, unsafe.Pointer(elemp));
|
||||
p += uintptr(elemWid);
|
||||
}
|
||||
return state.err
|
||||
}
|
||||
|
||||
var encEngineMap = make(map[reflect.Type] *encEngine)
|
||||
var encOpMap = map[reflect.Type] encOp {
|
||||
reflect.Typeof((*reflect.BoolType)(nil)): encBool,
|
||||
reflect.Typeof((*reflect.IntType)(nil)): encInt,
|
||||
@ -312,6 +320,7 @@ var encOpMap = map[reflect.Type] encOp {
|
||||
reflect.Typeof((*reflect.Uint16Type)(nil)): encUint16,
|
||||
reflect.Typeof((*reflect.Uint32Type)(nil)): encUint32,
|
||||
reflect.Typeof((*reflect.Uint64Type)(nil)): encUint64,
|
||||
reflect.Typeof((*reflect.UintptrType)(nil)): encUintptr,
|
||||
reflect.Typeof((*reflect.FloatType)(nil)): encFloat,
|
||||
reflect.Typeof((*reflect.Float32Type)(nil)): encFloat32,
|
||||
reflect.Typeof((*reflect.Float64Type)(nil)): encFloat64,
|
||||
@ -354,9 +363,11 @@ func encOpFor(rt reflect.Type) (encOp, int) {
|
||||
case *reflect.StructType:
|
||||
// Generate a closure that calls out to the engine for the nested type.
|
||||
engine := getEncEngine(typ);
|
||||
info := getTypeInfo(typ);
|
||||
op = func(i *encInstr, state *EncState, p unsafe.Pointer) {
|
||||
state.update(i);
|
||||
state.err = encodeStruct(engine, state.w, uintptr(p));
|
||||
// indirect through info to delay evaluation for recursive structs
|
||||
state.err = encodeStruct(info.encoder, state.w, uintptr(p));
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -366,10 +377,8 @@ func encOpFor(rt reflect.Type) (encOp, int) {
|
||||
return op, indir
|
||||
}
|
||||
|
||||
// The local Type was compiled from the actual value, so we know
|
||||
// it's compatible.
|
||||
// TODO(r): worth checking? typ is unused here.
|
||||
func compileEnc(rt reflect.Type, typ Type) *encEngine {
|
||||
// The local Type was compiled from the actual value, so we know it's compatible.
|
||||
func compileEnc(rt reflect.Type) *encEngine {
|
||||
srt, ok := rt.(*reflect.StructType);
|
||||
if !ok {
|
||||
panicln("TODO: can't handle non-structs");
|
||||
@ -385,15 +394,16 @@ func compileEnc(rt reflect.Type, typ Type) *encEngine {
|
||||
return engine;
|
||||
}
|
||||
|
||||
// typeLock must be held.
|
||||
// typeLock must be held (or we're in initialization and guaranteed single-threaded).
|
||||
// The reflection type must have all its indirections processed out.
|
||||
func getEncEngine(rt reflect.Type) *encEngine {
|
||||
engine, ok := encEngineMap[rt];
|
||||
if !ok {
|
||||
pkg, name := rt.Name();
|
||||
engine = compileEnc(rt, newType(name, rt));
|
||||
encEngineMap[rt] = engine;
|
||||
info := getTypeInfo(rt);
|
||||
if info.encoder == nil {
|
||||
// mark this engine as underway before compiling to handle recursive types.
|
||||
info.encoder = new(encEngine);
|
||||
info.encoder = compileEnc(rt);
|
||||
}
|
||||
return engine
|
||||
return info.encoder;
|
||||
}
|
||||
|
||||
func Encode(w io.Writer, e interface{}) os.Error {
|
||||
|
108
src/pkg/gob/encoder.go
Normal file
108
src/pkg/gob/encoder.go
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gob
|
||||
|
||||
import (
|
||||
"gob";
|
||||
"io";
|
||||
"os";
|
||||
"reflect";
|
||||
"sync";
|
||||
)
|
||||
|
||||
import "fmt" // TODO DELETE
|
||||
|
||||
type Encoder struct {
|
||||
sync.Mutex; // each item must be sent atomically
|
||||
sent map[reflect.Type] uint; // which types we've already sent
|
||||
state *EncState; // so we can encode integers, strings directly
|
||||
}
|
||||
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
enc := new(Encoder);
|
||||
enc.sent = make(map[reflect.Type] uint);
|
||||
enc.state = new(EncState);
|
||||
enc.state.w = w; // the rest isn't important; all we need is buffer and writer
|
||||
return enc;
|
||||
}
|
||||
|
||||
func (enc *Encoder) badType(rt reflect.Type) {
|
||||
enc.state.err = os.ErrorString("can't encode type " + rt.String());
|
||||
}
|
||||
|
||||
func (enc *Encoder) sendType(rt reflect.Type) {
|
||||
// Drill down to the base type.
|
||||
for {
|
||||
pt, ok := rt.(*reflect.PtrType);
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
rt = pt.Elem();
|
||||
}
|
||||
|
||||
// We only send structs - everything else is basic or an error
|
||||
switch t := rt.(type) {
|
||||
case *reflect.StructType:
|
||||
break; // we handle these
|
||||
case *reflect.ChanType:
|
||||
enc.badType(rt);
|
||||
return;
|
||||
case *reflect.MapType:
|
||||
enc.badType(rt);
|
||||
return;
|
||||
case *reflect.FuncType:
|
||||
enc.badType(rt);
|
||||
return;
|
||||
case *reflect.InterfaceType:
|
||||
enc.badType(rt);
|
||||
return;
|
||||
default:
|
||||
return; // basic, array, etc; not a type to be sent.
|
||||
}
|
||||
|
||||
// Have we already sent this type?
|
||||
id, alreadySent := enc.sent[rt];
|
||||
if alreadySent {
|
||||
return
|
||||
}
|
||||
|
||||
// Need to send it.
|
||||
info := getTypeInfo(rt);
|
||||
// Send the pair (-id, type)
|
||||
// Id:
|
||||
EncodeInt(enc.state, -int64(info.typeId));
|
||||
// Type:
|
||||
Encode(enc.state.w, info.wire);
|
||||
// Remember we've sent this type.
|
||||
enc.sent[rt] = id;
|
||||
// Now send the inner types
|
||||
st := rt.(*reflect.StructType);
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
enc.sendType(st.Field(i).Type);
|
||||
}
|
||||
}
|
||||
|
||||
func (enc *Encoder) Encode(e interface{}) os.Error {
|
||||
rt, indir := indirect(reflect.Typeof(e));
|
||||
|
||||
// Make sure we're single-threaded through here.
|
||||
enc.Lock();
|
||||
defer enc.Unlock();
|
||||
|
||||
// Make sure the type is known to the other side.
|
||||
enc.sendType(rt);
|
||||
if enc.state.err != nil {
|
||||
return enc.state.err
|
||||
}
|
||||
|
||||
// Identify the type of this top-level value.
|
||||
EncodeInt(enc.state, int64(enc.sent[rt]));
|
||||
|
||||
// Finally, send the data
|
||||
Encode(enc.state.w, e);
|
||||
|
||||
// Release and return.
|
||||
return enc.state.err
|
||||
}
|
37
src/pkg/gob/encoder_test.go
Normal file
37
src/pkg/gob/encoder_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gob
|
||||
|
||||
import (
|
||||
"bytes";
|
||||
"gob";
|
||||
"os";
|
||||
"reflect";
|
||||
"strings";
|
||||
"testing";
|
||||
"unsafe";
|
||||
)
|
||||
|
||||
type ET2 struct {
|
||||
x string;
|
||||
}
|
||||
|
||||
type ET1 struct {
|
||||
a int;
|
||||
et2 *ET2;
|
||||
next *ET1;
|
||||
}
|
||||
|
||||
func TestBasicEncoder(t *testing.T) {
|
||||
b := new(bytes.Buffer);
|
||||
enc := NewEncoder(b);
|
||||
et1 := new(ET1);
|
||||
et1.a = 7;
|
||||
et1.et2 = new(ET2);
|
||||
enc.Encode(et1);
|
||||
if enc.state.err != nil {
|
||||
t.Error("encoder fail:", enc.state.err)
|
||||
}
|
||||
}
|
@ -13,28 +13,51 @@ import (
|
||||
"unicode";
|
||||
)
|
||||
|
||||
var id uint32 // incremented for each new type we build
|
||||
// Types are identified by an integer TypeId. These can be passed on the wire.
|
||||
// Internally, they are used as keys to a map to recover the underlying type info.
|
||||
type TypeId uint32
|
||||
|
||||
var id TypeId // incremented for each new type we build
|
||||
var typeLock sync.Mutex // set while building a type
|
||||
|
||||
type Type interface {
|
||||
id() uint32;
|
||||
setId(id uint32);
|
||||
type gobType interface {
|
||||
id() TypeId;
|
||||
setId(id TypeId);
|
||||
String() string;
|
||||
safeString(seen map[uint32] bool) string;
|
||||
safeString(seen map[TypeId] bool) string;
|
||||
}
|
||||
|
||||
var types = make(map[reflect.Type] gobType)
|
||||
var idToType = make(map[TypeId] gobType)
|
||||
|
||||
func setTypeId(typ gobType) {
|
||||
id++;
|
||||
typ.setId(id);
|
||||
idToType[id] = typ;
|
||||
}
|
||||
|
||||
func (t TypeId) gobType() gobType {
|
||||
if t == 0 {
|
||||
return nil
|
||||
}
|
||||
return idToType[t]
|
||||
}
|
||||
|
||||
func (t TypeId) String() string {
|
||||
return t.gobType().String()
|
||||
}
|
||||
var types = make(map[reflect.Type] Type)
|
||||
|
||||
// Common elements of all types.
|
||||
type commonType struct {
|
||||
name string;
|
||||
_id uint32;
|
||||
_id TypeId;
|
||||
}
|
||||
|
||||
func (t *commonType) id() uint32 {
|
||||
func (t *commonType) id() TypeId {
|
||||
return t._id
|
||||
}
|
||||
|
||||
func (t *commonType) setId(id uint32) {
|
||||
func (t *commonType) setId(id TypeId) {
|
||||
t._id = id
|
||||
}
|
||||
|
||||
@ -51,31 +74,32 @@ func (t *commonType) Name() string {
|
||||
}
|
||||
|
||||
// Basic type identifiers, predefined.
|
||||
var tBool Type
|
||||
var tInt Type
|
||||
var tUint Type
|
||||
var tFloat Type
|
||||
var tString Type
|
||||
var tBytes Type
|
||||
var tBool TypeId
|
||||
var tInt TypeId
|
||||
var tUint TypeId
|
||||
var tFloat TypeId
|
||||
var tString TypeId
|
||||
var tBytes TypeId
|
||||
|
||||
// Array type
|
||||
type arrayType struct {
|
||||
commonType;
|
||||
Elem Type;
|
||||
Elem TypeId;
|
||||
Len int;
|
||||
}
|
||||
|
||||
func newArrayType(name string, elem Type, length int) *arrayType {
|
||||
a := &arrayType{ commonType{ name: name }, elem, length };
|
||||
func newArrayType(name string, elem gobType, length int) *arrayType {
|
||||
a := &arrayType{ commonType{ name: name }, elem.id(), length };
|
||||
setTypeId(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
func (a *arrayType) safeString(seen map[uint32] bool) string {
|
||||
func (a *arrayType) safeString(seen map[TypeId] bool) string {
|
||||
if _, ok := seen[a._id]; ok {
|
||||
return a.name
|
||||
}
|
||||
seen[a._id] = true;
|
||||
return fmt.Sprintf("[%d]%s", a.Len, a.Elem.safeString(seen));
|
||||
return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen));
|
||||
}
|
||||
|
||||
func (a *arrayType) String() string {
|
||||
@ -85,30 +109,31 @@ func (a *arrayType) String() string {
|
||||
// Slice type
|
||||
type sliceType struct {
|
||||
commonType;
|
||||
Elem Type;
|
||||
Elem TypeId;
|
||||
}
|
||||
|
||||
func newSliceType(name string, elem Type) *sliceType {
|
||||
s := &sliceType{ commonType{ name: name }, elem };
|
||||
func newSliceType(name string, elem gobType) *sliceType {
|
||||
s := &sliceType{ commonType{ name: name }, elem.id() };
|
||||
setTypeId(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
func (s *sliceType) safeString(seen map[uint32] bool) string {
|
||||
func (s *sliceType) safeString(seen map[TypeId] bool) string {
|
||||
if _, ok := seen[s._id]; ok {
|
||||
return s.name
|
||||
}
|
||||
seen[s._id] = true;
|
||||
return fmt.Sprintf("[]%s", s.Elem.safeString(seen));
|
||||
return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen));
|
||||
}
|
||||
|
||||
func (s *sliceType) String() string {
|
||||
return s.safeString(make(map[uint32] bool))
|
||||
return s.safeString(make(map[TypeId] bool))
|
||||
}
|
||||
|
||||
// Struct type
|
||||
type fieldType struct {
|
||||
name string;
|
||||
typ Type;
|
||||
typeId TypeId;
|
||||
}
|
||||
|
||||
type structType struct {
|
||||
@ -116,30 +141,31 @@ type structType struct {
|
||||
field []*fieldType;
|
||||
}
|
||||
|
||||
func (s *structType) safeString(seen map[uint32] bool) string {
|
||||
func (s *structType) safeString(seen map[TypeId] bool) string {
|
||||
if _, ok := seen[s._id]; ok {
|
||||
return s.name
|
||||
}
|
||||
seen[s._id] = true;
|
||||
str := s.name + " = struct { ";
|
||||
for _, f := range s.field {
|
||||
str += fmt.Sprintf("%s %s; ", f.name, f.typ.safeString(seen));
|
||||
str += fmt.Sprintf("%s %s; ", f.name, f.typeId.gobType().safeString(seen));
|
||||
}
|
||||
str += "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
func (s *structType) String() string {
|
||||
return s.safeString(make(map[uint32] bool))
|
||||
return s.safeString(make(map[TypeId] bool))
|
||||
}
|
||||
|
||||
func newStructType(name string) *structType {
|
||||
s := &structType{ commonType{ name: name }, nil };
|
||||
setTypeId(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Construction
|
||||
func newType(name string, rt reflect.Type) Type
|
||||
func newType(name string, rt reflect.Type) gobType
|
||||
|
||||
// Step through the indirections on a type to discover the base type.
|
||||
// Return the number of indirections.
|
||||
@ -156,35 +182,45 @@ func indirect(t reflect.Type) (rt reflect.Type, count int) {
|
||||
return;
|
||||
}
|
||||
|
||||
func newTypeObject(name string, rt reflect.Type) Type {
|
||||
func newTypeObject(name string, rt reflect.Type) gobType {
|
||||
switch t := rt.(type) {
|
||||
// All basic types are easy: they are predefined.
|
||||
case *reflect.BoolType:
|
||||
return tBool
|
||||
return tBool.gobType()
|
||||
|
||||
case *reflect.IntType:
|
||||
return tInt
|
||||
return tInt.gobType()
|
||||
case *reflect.Int8Type:
|
||||
return tInt.gobType()
|
||||
case *reflect.Int16Type:
|
||||
return tInt.gobType()
|
||||
case *reflect.Int32Type:
|
||||
return tInt
|
||||
return tInt.gobType()
|
||||
case *reflect.Int64Type:
|
||||
return tInt
|
||||
return tInt.gobType()
|
||||
|
||||
case *reflect.UintType:
|
||||
return tUint
|
||||
return tUint.gobType()
|
||||
case *reflect.Uint8Type:
|
||||
return tUint.gobType()
|
||||
case *reflect.Uint16Type:
|
||||
return tUint.gobType()
|
||||
case *reflect.Uint32Type:
|
||||
return tUint
|
||||
return tUint.gobType()
|
||||
case *reflect.Uint64Type:
|
||||
return tUint
|
||||
return tUint.gobType()
|
||||
case *reflect.UintptrType:
|
||||
return tUint.gobType()
|
||||
|
||||
case *reflect.FloatType:
|
||||
return tFloat
|
||||
return tFloat.gobType()
|
||||
case *reflect.Float32Type:
|
||||
return tFloat
|
||||
return tFloat.gobType()
|
||||
case *reflect.Float64Type:
|
||||
return tFloat
|
||||
return tFloat.gobType()
|
||||
|
||||
case *reflect.StringType:
|
||||
return tString
|
||||
return tString.gobType()
|
||||
|
||||
case *reflect.ArrayType:
|
||||
return newArrayType(name, newType("", t.Elem()), t.Len());
|
||||
@ -192,7 +228,7 @@ func newTypeObject(name string, rt reflect.Type) Type {
|
||||
case *reflect.SliceType:
|
||||
// []byte == []uint8 is a special case
|
||||
if _, ok := t.Elem().(*reflect.Uint8Type); ok {
|
||||
return tBytes
|
||||
return tBytes.gobType()
|
||||
}
|
||||
return newSliceType(name, newType("", t.Elem()));
|
||||
|
||||
@ -201,6 +237,7 @@ func newTypeObject(name string, rt reflect.Type) Type {
|
||||
// structures can be constructed safely.
|
||||
strType := newStructType(name);
|
||||
types[rt] = strType;
|
||||
idToType[strType.id()] = strType;
|
||||
field := make([]*fieldType, t.NumField());
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i);
|
||||
@ -209,7 +246,7 @@ func newTypeObject(name string, rt reflect.Type) Type {
|
||||
if tname == "" {
|
||||
tname = f.Type.String();
|
||||
}
|
||||
field[i] = &fieldType{ f.Name, newType(tname, f.Type) };
|
||||
field[i] = &fieldType{ f.Name, newType(tname, f.Type).id() };
|
||||
}
|
||||
strType.field = field;
|
||||
return strType;
|
||||
@ -220,7 +257,7 @@ func newTypeObject(name string, rt reflect.Type) Type {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newType(name string, rt reflect.Type) Type {
|
||||
func newType(name string, rt reflect.Type) gobType {
|
||||
// Flatten the data structure by collapsing out pointers
|
||||
for {
|
||||
pt, ok := rt.(*reflect.PtrType);
|
||||
@ -234,34 +271,72 @@ func newType(name string, rt reflect.Type) Type {
|
||||
return typ
|
||||
}
|
||||
typ = newTypeObject(name, rt);
|
||||
id++;
|
||||
typ.setId(id);
|
||||
types[rt] = typ;
|
||||
return typ
|
||||
}
|
||||
|
||||
// GetType returns the Gob type describing the interface value.
|
||||
func GetType(name string, e interface{}) Type {
|
||||
rt := reflect.Typeof(e);
|
||||
// getType returns the Gob type describing the given reflect.Type.
|
||||
// typeLock must be held.
|
||||
func getType(name string, rt reflect.Type) gobType {
|
||||
// Set lock; all code running under here is synchronized.
|
||||
typeLock.Lock();
|
||||
t := newType(name, rt);
|
||||
typeLock.Unlock();
|
||||
return t;
|
||||
}
|
||||
|
||||
// used for building the basic types; called only from init()
|
||||
func bootstrapType(name string, e interface{}) Type {
|
||||
func bootstrapType(name string, e interface{}) TypeId {
|
||||
rt := reflect.Typeof(e);
|
||||
_, present := types[rt];
|
||||
if present {
|
||||
panicln("bootstrap type already present:", name);
|
||||
}
|
||||
typ := &commonType{ name: name };
|
||||
id++;
|
||||
typ.setId(id);
|
||||
types[rt] = typ;
|
||||
return typ
|
||||
setTypeId(typ);
|
||||
return id
|
||||
}
|
||||
|
||||
// Representation of the information we send and receive about this type.
|
||||
// Each value we send is preceded by its type definition: an encoded int.
|
||||
// However, the very first time we send the value, we first send the pair
|
||||
// (-id, wireType).
|
||||
// For bootstrapping purposes, we assume that the recipient knows how
|
||||
// to decode a wireType; it is exactly the wireType struct here, interpreted
|
||||
// using the gob rules for sending a structure, except that we assume the
|
||||
// ids for wireType and structType are known. The relevant pieces
|
||||
// are built in encode.go's init() function.
|
||||
|
||||
type wireType struct {
|
||||
name string;
|
||||
s *structType;
|
||||
}
|
||||
|
||||
type decEngine struct // defined in decode.go
|
||||
type encEngine struct // defined in encode.go
|
||||
type typeInfo struct {
|
||||
typeId TypeId;
|
||||
decoder *decEngine;
|
||||
encoder *encEngine;
|
||||
wire *wireType;
|
||||
}
|
||||
|
||||
var typeInfoMap = make(map[reflect.Type] *typeInfo) // protected by typeLock
|
||||
|
||||
// The reflection type must have all its indirections processed out.
|
||||
func getTypeInfo(rt reflect.Type) *typeInfo {
|
||||
if pt, ok := rt.(*reflect.PtrType); ok {
|
||||
panicln("pointer type in getTypeInfo:", rt.String())
|
||||
}
|
||||
info, ok := typeInfoMap[rt];
|
||||
if !ok {
|
||||
info = new(typeInfo);
|
||||
path, name := rt.Name();
|
||||
info.typeId = getType(name, rt).id();
|
||||
// assume it's a struct type
|
||||
info.wire = &wireType{name, info.typeId.gobType().(*structType)};
|
||||
typeInfoMap[rt] = info;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -7,11 +7,12 @@ package gob
|
||||
import (
|
||||
"gob";
|
||||
"os";
|
||||
"reflect";
|
||||
"testing";
|
||||
)
|
||||
|
||||
type typeT struct {
|
||||
typ Type;
|
||||
typeId TypeId;
|
||||
str string;
|
||||
}
|
||||
var basicTypes = []typeT {
|
||||
@ -23,13 +24,19 @@ var basicTypes = []typeT {
|
||||
typeT { tString, "string" },
|
||||
}
|
||||
|
||||
func getTypeUnlocked(name string, rt reflect.Type) gobType {
|
||||
typeLock.Lock();
|
||||
defer typeLock.Unlock();
|
||||
return getType(name, rt);
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
func TestBasic(t *testing.T) {
|
||||
for _, tt := range basicTypes {
|
||||
if tt.typ.String() != tt.str {
|
||||
t.Errorf("checkType: expected %q got %s", tt.str, tt.typ.String())
|
||||
if tt.typeId.String() != tt.str {
|
||||
t.Errorf("checkType: expected %q got %s", tt.str, tt.typeId.String())
|
||||
}
|
||||
if tt.typ.id() == 0 {
|
||||
if tt.typeId == 0 {
|
||||
t.Errorf("id for %q is zero", tt.str)
|
||||
}
|
||||
}
|
||||
@ -37,35 +44,35 @@ func TestBasic(t *testing.T) {
|
||||
|
||||
// Reregister some basic types to check registration is idempotent.
|
||||
func TestReregistration(t *testing.T) {
|
||||
newtyp := GetType("int", 0);
|
||||
if newtyp != tInt {
|
||||
newtyp := getTypeUnlocked("int", reflect.Typeof(int(0)));
|
||||
if newtyp != tInt.gobType() {
|
||||
t.Errorf("reregistration of %s got new type", newtyp.String())
|
||||
}
|
||||
newtyp = GetType("uint", uint(0));
|
||||
if newtyp != tUint {
|
||||
newtyp = getTypeUnlocked("uint", reflect.Typeof(uint(0)));
|
||||
if newtyp != tUint.gobType() {
|
||||
t.Errorf("reregistration of %s got new type", newtyp.String())
|
||||
}
|
||||
newtyp = GetType("string", "hello");
|
||||
if newtyp != tString {
|
||||
newtyp = getTypeUnlocked("string", reflect.Typeof("hello"));
|
||||
if newtyp != tString.gobType() {
|
||||
t.Errorf("reregistration of %s got new type", newtyp.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestArrayType(t *testing.T) {
|
||||
var a3 [3]int;
|
||||
a3int := GetType("foo", a3);
|
||||
a3int := getTypeUnlocked("foo", reflect.Typeof(a3));
|
||||
var newa3 [3]int;
|
||||
newa3int := GetType("bar", a3);
|
||||
newa3int := getTypeUnlocked("bar", reflect.Typeof(a3));
|
||||
if a3int != newa3int {
|
||||
t.Errorf("second registration of [3]int creates new type");
|
||||
}
|
||||
var a4 [4]int;
|
||||
a4int := GetType("goo", a4);
|
||||
a4int := getTypeUnlocked("goo", reflect.Typeof(a4));
|
||||
if a3int == a4int {
|
||||
t.Errorf("registration of [3]int creates same type as [4]int");
|
||||
}
|
||||
var b3 [3]bool;
|
||||
a3bool := GetType("", b3);
|
||||
a3bool := getTypeUnlocked("", reflect.Typeof(b3));
|
||||
if a3int == a3bool {
|
||||
t.Errorf("registration of [3]bool creates same type as [3]int");
|
||||
}
|
||||
@ -78,14 +85,14 @@ func TestArrayType(t *testing.T) {
|
||||
|
||||
func TestSliceType(t *testing.T) {
|
||||
var s []int;
|
||||
sint := GetType("slice", s);
|
||||
sint := getTypeUnlocked("slice", reflect.Typeof(s));
|
||||
var news []int;
|
||||
newsint := GetType("slice1", news);
|
||||
newsint := getTypeUnlocked("slice1", reflect.Typeof(news));
|
||||
if sint != newsint {
|
||||
t.Errorf("second registration of []int creates new type");
|
||||
}
|
||||
var b []bool;
|
||||
sbool := GetType("", b);
|
||||
sbool := getTypeUnlocked("", reflect.Typeof(b));
|
||||
if sbool == sint {
|
||||
t.Errorf("registration of []bool creates same type as []int");
|
||||
}
|
||||
@ -114,7 +121,7 @@ type Foo struct {
|
||||
}
|
||||
|
||||
func TestStructType(t *testing.T) {
|
||||
sstruct := GetType("Foo", Foo{});
|
||||
sstruct := getTypeUnlocked("Foo", reflect.Typeof(Foo{}));
|
||||
str := sstruct.String();
|
||||
// If we can print it correctly, we built it correctly.
|
||||
expected := "Foo = struct { a int; b int; c string; d bytes; e float; f float; g Bar = struct { x string; }; h Bar; i Foo; }";
|
||||
|
Loading…
Reference in New Issue
Block a user