From 7986de6e516883c050c00f473d994c26d201af39 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 25 Jun 2009 22:08:51 -0700 Subject: [PATCH] gobs part 1: types. 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 | 231 +++++++++++++++++++++++++++++++++++++++ src/pkg/gob/type_test.go | 132 ++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 src/pkg/gob/type.go create mode 100644 src/pkg/gob/type_test.go diff --git a/src/pkg/gob/type.go b/src/pkg/gob/type.go new file mode 100644 index 0000000000..7bf06b0495 --- /dev/null +++ b/src/pkg/gob/type.go @@ -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 index 0000000000..4629443953 --- /dev/null +++ b/src/pkg/gob/type_test.go @@ -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); + } +} -- 2.48.1