]> Cypherpunks repositories - gostls13.git/commitdiff
gobs part 1: types.
authorRob Pike <r@golang.org>
Fri, 26 Jun 2009 05:08:51 +0000 (22:08 -0700)
committerRob Pike <r@golang.org>
Fri, 26 Jun 2009 05:08:51 +0000 (22:08 -0700)
not ready to be part of the standard build yet; this is just a checkpoint.

R=rsc
DELTA=361  (361 added, 0 deleted, 0 changed)
OCL=30782
CL=30785

src/pkg/gob/type.go [new file with mode: 0644]
src/pkg/gob/type_test.go [new file with mode: 0644]

diff --git a/src/pkg/gob/type.go b/src/pkg/gob/type.go
new file mode 100644 (file)
index 0000000..7bf06b0
--- /dev/null
@@ -0,0 +1,231 @@
+// 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", "");
+}
diff --git a/src/pkg/gob/type_test.go b/src/pkg/gob/type_test.go
new file mode 100644 (file)
index 0000000..4629443
--- /dev/null
@@ -0,0 +1,132 @@
+// 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);
+       }
+}