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:
parent
a2a827542a
commit
7986de6e51
231
src/pkg/gob/type.go
Normal file
231
src/pkg/gob/type.go
Normal 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
132
src/pkg/gob/type_test.go
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user