1
0
mirror of https://github.com/golang/go synced 2024-11-18 02:54:47 -07:00

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
This commit is contained in:
Rob Pike 2009-06-25 22:08:51 -07:00
parent a2a827542a
commit 7986de6e51
2 changed files with 363 additions and 0 deletions

231
src/pkg/gob/type.go Normal file
View File

@ -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", "");
}

132
src/pkg/gob/type_test.go Normal file
View File

@ -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);
}
}