--- /dev/null
+// 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 (
+ "fmt";
+ "os";
+ "reflect";
+ "sync";
+)
+
+var id uint32 // incremented for each new type we build
+var typeLock sync.Mutex // set while building a type
+
+type Type interface {
+ id() uint32;
+ setId(id uint32);
+ String() string;
+ safeString(seen map[uint32] bool) string;
+}
+var types = make(map[reflect.Type] Type)
+
+// Common elements of all types.
+type commonType struct {
+ name string;
+ _id uint32;
+}
+
+func (t *commonType) id() uint32 {
+ return t._id
+}
+
+func (t *commonType) setId(id uint32) {
+ t._id = id
+}
+
+func (t *commonType) String() string {
+ return t.name
+}
+
+func (t *commonType) safeString(seen map[uint32] bool) string {
+ return t.name
+}
+
+func (t *commonType) Name() string {
+ return t.name
+}
+
+// Basic type identifiers, predefined.
+var tBool Type
+var tInt Type
+var tUint Type
+var tFloat32 Type
+var tFloat64 Type
+var tString Type
+
+// Array type
+type arrayType struct {
+ commonType;
+ Elem Type;
+ Len int;
+}
+
+func newArrayType(name string, elem Type, length int) *arrayType {
+ a := &arrayType{ commonType{ name: name }, elem, length };
+ return a;
+}
+
+func (a *arrayType) safeString(seen map[uint32] 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));
+}
+
+func (a *arrayType) String() string {
+ return a.safeString(make(map[uint32] bool))
+}
+
+// Slice type
+type sliceType struct {
+ commonType;
+ Elem Type;
+}
+
+func newSliceType(name string, elem Type) *sliceType {
+ s := &sliceType{ commonType{ name: name }, elem };
+ return s;
+}
+
+func (s *sliceType) safeString(seen map[uint32] bool) string {
+ if _, ok := seen[s._id]; ok {
+ return s.name
+ }
+ seen[s._id] = true;
+ return fmt.Sprintf("[]%s", s.Elem.safeString(seen));
+}
+
+func (s *sliceType) String() string {
+ return s.safeString(make(map[uint32] bool))
+}
+
+// Struct type
+type fieldType struct {
+ name string;
+ typ Type;
+}
+
+type structType struct {
+ commonType;
+ field []*fieldType;
+}
+
+func (s *structType) safeString(seen map[uint32] bool) string {
+ if _, ok := seen[s._id]; ok {
+ return s.name
+ }
+ seen[s._id] = true;
+ str := "struct { ";
+ for _, f := range s.field {
+ str += fmt.Sprintf("%s %s; ", f.name, f.typ.safeString(seen));
+ }
+ str += "}";
+ return str;
+}
+
+func (s *structType) String() string {
+ return s.safeString(make(map[uint32] bool))
+}
+
+func newStructType(name string) *structType {
+ s := &structType{ commonType{ name: name }, nil };
+ return s;
+}
+
+// Construction
+func newType(name string, rt reflect.Type) Type
+
+func newTypeObject(name string, rt reflect.Type) Type {
+ switch rt.Kind() {
+ // All basic types are easy: they are predefined.
+ case reflect.BoolKind:
+ return tBool
+ case reflect.IntKind, reflect.Int32Kind, reflect.Int64Kind:
+ return tInt
+ case reflect.UintKind, reflect.Uint32Kind, reflect.Uint64Kind:
+ return tUint
+ case reflect.FloatKind, reflect.Float32Kind:
+ return tFloat32
+ case reflect.Float64Kind:
+ return tFloat64
+ case reflect.StringKind:
+ return tString
+ case reflect.ArrayKind:
+ // TODO(r): worth a special case for array of bytes?
+ at := rt.(reflect.ArrayType);
+ if at.IsSlice() {
+ return newSliceType(name, newType("", at.Elem()));
+ } else {
+ return newArrayType(name, newType("", at.Elem()), at.Len());
+ }
+ case reflect.StructKind:
+ // Install the struct type itself before the fields so recursive
+ // structures can be constructed safely.
+ strType := newStructType(name);
+ types[rt] = strType;
+ st := rt.(reflect.StructType);
+ field := make([]*fieldType, st.Len());
+ for i := 0; i < st.Len(); i++ {
+ name, typ, tag, offset := st.Field(i);
+ field[i] = &fieldType{ name, newType("", typ) };
+ }
+ strType.field = field;
+ return strType;
+ default:
+ panicln("gob NewTypeObject can't handle type", rt.String()); // TODO(r): panic?
+ }
+ return nil
+}
+
+func newType(name string, rt reflect.Type) Type {
+ // Flatten the data structure by collapsing out pointers
+ for rt.Kind() == reflect.PtrKind {
+ rt = rt.(reflect.PtrType).Sub();
+ }
+ typ, present := types[rt];
+ if present {
+ 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);
+ // 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 {
+ 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
+}
+
+func init() {
+ tBool= bootstrapType("bool", false);
+ tInt = bootstrapType("int", int(0));
+ tUint = bootstrapType("uint", uint(0));
+ tFloat32 = bootstrapType("float32", float32(0));
+ tFloat64 = bootstrapType("float64", float64(0));
+ tString= bootstrapType("string", "");
+}
--- /dev/null
+// 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 (
+"fmt";
+ "gob";
+ "os";
+ "testing";
+)
+
+func checkType(ti Type, expected string, t *testing.T) {
+ if ti.String() != expected {
+ t.Errorf("checkType: expected %q got %s", expected, ti.String())
+ }
+ if ti.id() == 0 {
+ t.Errorf("id for %q is zero", expected)
+ }
+}
+
+type typeT struct {
+ typ Type;
+ str string;
+}
+var basicTypes = []typeT {
+ typeT { tBool, "bool" },
+ typeT { tInt, "int" },
+ typeT { tUint, "uint" },
+ typeT { tFloat32, "float32" },
+ typeT { tFloat64, "float64" },
+ typeT { tString, "string" },
+}
+
+// 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.typ.id() == 0 {
+ t.Errorf("id for %q is zero", tt.str)
+ }
+ }
+}
+
+// Reregister some basic types to check registration is idempotent.
+func TestReregistration(t *testing.T) {
+ newtyp := GetType("int", 0);
+ if newtyp != tInt {
+ t.Errorf("reregistration of %s got new type", newtyp.String())
+ }
+ newtyp = GetType("uint", uint(0));
+ if newtyp != tUint {
+ t.Errorf("reregistration of %s got new type", newtyp.String())
+ }
+ newtyp = GetType("string", "hello");
+ if newtyp != tString {
+ t.Errorf("reregistration of %s got new type", newtyp.String())
+ }
+}
+
+func TestArrayType(t *testing.T) {
+ var a3 [3]int;
+ a3int := GetType("foo", a3);
+ var newa3 [3]int;
+ newa3int := GetType("bar", a3);
+ if a3int != newa3int {
+ t.Errorf("second registration of [3]int creates new type");
+ }
+ var a4 [4]int;
+ a4int := GetType("goo", a4);
+ if a3int == a4int {
+ t.Errorf("registration of [3]int creates same type as [4]int");
+ }
+ var b3 [3]bool;
+ a3bool := GetType("", b3);
+ if a3int == a3bool {
+ t.Errorf("registration of [3]bool creates same type as [3]int");
+ }
+ str := a3bool.String();
+ expected := "[3]bool";
+ if str != expected {
+ t.Errorf("array printed as %q; expected %q", str, expected);
+ }
+}
+
+func TestSliceType(t *testing.T) {
+ var s []int;
+ sint := GetType("slice", s);
+ var news []int;
+ newsint := GetType("slice1", news);
+ if sint != newsint {
+ t.Errorf("second registration of []int creates new type");
+ }
+ var b []bool;
+ sbool := GetType("", b);
+ if sbool == sint {
+ t.Errorf("registration of []bool creates same type as []int");
+ }
+ str := sbool.String();
+ expected := "[]bool";
+ if str != expected {
+ t.Errorf("slice printed as %q; expected %q", str, expected);
+ }
+}
+
+type Bar struct {
+ x string
+}
+
+// This structure has pointers and refers to itself, making it a good test case.
+type Foo struct {
+ a int;
+ b int32; // will become int
+ c string;
+ d *float; // will become float32
+ e ****float64; // will become float64
+ f *Bar;
+ g *Foo; // will not explode
+}
+
+func TestStructType(t *testing.T) {
+ sstruct := GetType("Foo", Foo{});
+ str := sstruct.String();
+ // If we can print it correctly, we built it correctly.
+ expected := "struct { a int; b int; c string; d float32; e float64; f struct { x string; }; g Foo; }";
+ if str != expected {
+ t.Errorf("struct printed as %q; expected %q", str, expected);
+ }
+}