EncodeT{ 1<<63, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81} },
}
+
// Test basic encode/decode routines for unsigned integers
func TestUintCodec(t *testing.T) {
b := new(bytes.Buffer);
b := new(bytes.Buffer);
encode(b, t1);
var _t1 T1;
- decode(b, getTypeInfo(reflect.Typeof(_t1)).typeId, &_t1);
+ decode(b, getTypeInfo(reflect.Typeof(_t1)).id, &_t1);
if !reflect.DeepEqual(t1, &_t1) {
t.Errorf("encode expected %v got %v", *t1, _t1);
}
b := new(bytes.Buffer);
encode(b, rt);
var drt RT;
- decode(b, getTypeInfo(reflect.Typeof(drt)).typeId, &drt);
+ decode(b, getTypeInfo(reflect.Typeof(drt)).id, &drt);
if drt.a != rt.a {
t.Errorf("nesting: encode expected %v got %v", *rt, drt);
}
b := new(bytes.Buffer);
encode(b, t1);
var t0 T0;
- t0Id := getTypeInfo(reflect.Typeof(t0)).typeId;
+ t0Id := getTypeInfo(reflect.Typeof(t0)).id;
decode(b, t0Id, &t0);
if t0.a != 17 || t0.b != 177 || t0.c != 1777 || t0.d != 17777 {
t.Errorf("t1->t0: expected {17 177 1777 17777}; got %v", t0);
b.Reset();
encode(b, t0);
t1 = T1{};
- t1Id := getTypeInfo(reflect.Typeof(t1)).typeId;
+ t1Id := getTypeInfo(reflect.Typeof(t1)).id;
decode(b, t1Id, &t1);
if t1.a != 17 || *t1.b != 177 || **t1.c != 1777 || ***t1.d != 17777 {
t.Errorf("t0->t1 expected {17 177 1777 17777}; got {%d %d %d %d}", t1.a, *t1.b, **t1.c, ***t1.d);
b.Reset();
encode(b, t0);
t2 = T2{};
- t2Id := getTypeInfo(reflect.Typeof(t2)).typeId;
+ t2Id := getTypeInfo(reflect.Typeof(t2)).id;
decode(b, t2Id, &t2);
if ***t2.a != 17 || **t2.b != 177 || *t2.c != 1777 || t2.d != 17777 {
t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.a, **t2.b, *t2.c, t2.d);
rt0.c = 3.14159;
b := new(bytes.Buffer);
encode(b, rt0);
- rt0Id := getTypeInfo(reflect.Typeof(rt0)).typeId;
+ rt0Id := getTypeInfo(reflect.Typeof(rt0)).id;
var rt1 RT1;
// Wire type is RT0, local type is RT1.
decode(b, rt0Id, &rt1);
b := new(bytes.Buffer);
encode(b, it0);
- rt0Id := getTypeInfo(reflect.Typeof(it0)).typeId;
+ rt0Id := getTypeInfo(reflect.Typeof(it0)).id;
var rt1 RT1;
// Wire type is IT0, local type is RT1.
err := decode(b, rt0Id, &rt1);
)
var (
- ErrRange = os.ErrorString("gob: internal error: field numbers out of bounds");
- ErrNotStruct = os.ErrorString("gob: TODO: can only handle structs")
+ errRange = os.ErrorString("gob: internal error: field numbers out of bounds");
+ errNotStruct = os.ErrorString("gob: TODO: can only handle structs")
)
// The global execution state of an instance of the decoder.
}
fieldnum := state.fieldnum + delta;
if fieldnum >= len(engine.instr) {
- state.err = ErrRange;
+ state.err = errRange;
break;
}
instr := &engine.instr[fieldnum];
}
fieldnum := state.fieldnum + delta;
if fieldnum >= len(engine.instr) {
- state.err = ErrRange;
+ state.err = errRange;
break;
}
instr := &engine.instr[fieldnum];
reflect.Typeof((*reflect.StringType)(nil)): decString,
}
-var decIgnoreOpMap = map[TypeId] decOp {
+var decIgnoreOpMap = map[typeId] decOp {
tBool: ignoreUint,
tInt: ignoreUint,
tUint: ignoreUint,
tString: ignoreUint8Array,
}
-func getDecEnginePtr(wireId TypeId, rt reflect.Type) (enginePtr **decEngine, err os.Error)
-func getIgnoreEnginePtr(wireId TypeId) (enginePtr **decEngine, err os.Error)
+func getDecEnginePtr(wireId typeId, rt reflect.Type) (enginePtr **decEngine, err os.Error)
+func getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, err os.Error)
// Return the decoding op for the base type under rt and
// the indirection count to reach it.
-func decOpFor(wireId TypeId, rt reflect.Type) (decOp, int, os.Error) {
+func decOpFor(wireId typeId, rt reflect.Type) (decOp, int, os.Error) {
typ, indir := indirect(rt);
op, ok := decOpMap[reflect.Typeof(typ)];
if !ok {
}
// Return the decoding op for a field that has no destination.
-func decIgnoreOpFor(wireId TypeId) (decOp, os.Error) {
+func decIgnoreOpFor(wireId typeId) (decOp, os.Error) {
op, ok := decIgnoreOpMap[wireId];
if !ok {
// Special cases
// Are these two gob Types compatible?
// Answers the question for basic types, arrays, and slices.
// Structs are considered ok; fields will be checked later.
-func compatibleType(fr reflect.Type, fw TypeId) bool {
+func compatibleType(fr reflect.Type, fw typeId) bool {
for {
if pt, ok := fr.(*reflect.PtrType); ok {
fr = pt.Elem();
return true;
}
-func compileDec(wireId TypeId, rt reflect.Type) (engine *decEngine, err os.Error) {
+func compileDec(wireId typeId, rt reflect.Type) (engine *decEngine, err os.Error) {
srt, ok1 := rt.(*reflect.StructType);
wireStruct, ok2 := wireId.gobType().(*structType);
if !ok1 || !ok2 {
- return nil, ErrNotStruct
+ return nil, errNotStruct
}
engine = new(decEngine);
engine.instr = make([]decInstr, len(wireStruct.field));
localField, present := srt.FieldByName(wireField.name);
// TODO(r): anonymous names
if !present || localField.Anonymous {
- op, err := decIgnoreOpFor(wireField.typeId);
+ op, err := decIgnoreOpFor(wireField.id);
if err != nil {
return nil, err
}
engine.instr[fieldnum] = decInstr{op, fieldnum, 0, 0};
continue;
}
- if !compatibleType(localField.Type, wireField.typeId) {
+ if !compatibleType(localField.Type, wireField.id) {
return nil, os.ErrorString("gob: wrong type for field " + wireField.name + " in type " + wireId.Name());
}
- op, indir, err := decOpFor(wireField.typeId, localField.Type);
+ op, indir, err := decOpFor(wireField.id, localField.Type);
if err != nil {
return nil, err
}
return;
}
-var decoderCache = make(map[reflect.Type] map[TypeId] **decEngine)
-var ignorerCache = make(map[TypeId] **decEngine)
+var decoderCache = make(map[reflect.Type] map[typeId] **decEngine)
+var ignorerCache = make(map[typeId] **decEngine)
// typeLock must be held.
-func getDecEnginePtr(wireId TypeId, rt reflect.Type) (enginePtr **decEngine, err os.Error) {
+func getDecEnginePtr(wireId typeId, rt reflect.Type) (enginePtr **decEngine, err os.Error) {
decoderMap, ok := decoderCache[rt];
if !ok {
- decoderMap = make(map[TypeId] **decEngine);
+ decoderMap = make(map[typeId] **decEngine);
decoderCache[rt] = decoderMap;
}
if enginePtr, ok = decoderMap[wireId]; !ok {
var emptyStructType = reflect.Typeof(emptyStruct{})
// typeLock must be held.
-func getIgnoreEnginePtr(wireId TypeId) (enginePtr **decEngine, err os.Error) {
+func getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, err os.Error) {
var ok bool;
if enginePtr, ok = ignorerCache[wireId]; !ok {
// To handle recursive types, mark this engine as underway before compiling.
return
}
-func decode(b *bytes.Buffer, wireId TypeId, e interface{}) os.Error {
+func decode(b *bytes.Buffer, wireId typeId, e interface{}) os.Error {
// Dereference down to the underlying object.
rt, indir := indirect(reflect.Typeof(e));
v := reflect.NewValue(e);
"sync";
)
+// A Decoder manages the receipt of type and data information read from the
+// remote side of a connection.
type Decoder struct {
- sync.Mutex; // each item must be received atomically
+ mutex sync.Mutex; // each item must be received atomically
r io.Reader; // source of the data
- seen map[TypeId] *wireType; // which types we've already seen described
+ seen map[typeId] *wireType; // which types we've already seen described
state *decodeState; // reads data from in-memory buffer
countState *decodeState; // reads counts from wire
buf []byte;
oneByte []byte;
}
+// NewDecoder returns a new decoder that reads from the io.Reader.
func NewDecoder(r io.Reader) *Decoder {
dec := new(Decoder);
dec.r = r;
- dec.seen = make(map[TypeId] *wireType);
+ dec.seen = make(map[typeId] *wireType);
dec.state = new(decodeState); // buffer set in Decode(); rest is unimportant
dec.oneByte = make([]byte, 1);
return dec;
}
-func (dec *Decoder) recvType(id TypeId) {
+func (dec *Decoder) recvType(id typeId) {
// Have we already seen this type? That's an error
if wt_, alreadySeen := dec.seen[id]; alreadySeen {
dec.state.err = os.ErrorString("gob: duplicate type received");
dec.seen[id] = wire;
}
+// Decode reads the next value from the connection and stores
+// it in the data represented by the empty interface value.
// The value underlying e must be the correct type for the next
-// value to be received for this decoder.
+// data item received.
func (dec *Decoder) Decode(e interface{}) os.Error {
rt, indir := indirect(reflect.Typeof(e));
// Make sure we're single-threaded through here.
- dec.Lock();
- defer dec.Unlock();
+ dec.mutex.Lock();
+ defer dec.mutex.Unlock();
dec.state.err = nil;
for {
}
// Receive a type id.
- id := TypeId(decodeInt(dec.state));
+ id := typeId(decodeInt(dec.state));
if dec.state.err != nil {
break;
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+/*
+ The gob package manages streams of gobs - binary values exchanged between an
+ Encoder (transmitter) and a Decoder (receiver). A typical use is transporting
+ arguments and results of remote procedure calls (RPCs) such as those provided by
+ package "rpc".
+
+ A stream of gobs is self-describing. Each data item in the stream is preceded by
+ a specification of its type, expressed in terms of a small set of predefined
+ types. Pointers are not transmitted, but the things they point to are
+ transmitted; that is, the values are flattened. Recursive types work fine, but
+ recursive values (data with cycles) are problematic. This may change.
+
+ To use gobs, create an Encoder and present it with a series of data items as
+ values or addresses that can be dereferenced to values. (At the moment, these
+ items must be structs (struct, *struct, **struct etc.), but this may change.) The
+ Encoder makes sure all type information is sent before it is needed. At the
+ receive side, a Decoder retrieves values from the encoded stream and unpacks them
+ into local variables.
+
+ The source and destination values/types need not correspond exactly. For structs,
+ fields (identified by name) that are in the source but absent from the receiving
+ variable will be ignored. Fields that are in the receiving variable but missing
+ from the transmitted type or value will be ignored in the destination. If a field
+ with the same name is present in both, their types must be compatible. Both the
+ receiver and transmitter will do all necessary indirection and dereferencing to
+ convert between gobs and actual Go values. For instance, a gob type that is
+ schematically,
+
+ struct { a, b int }
+
+ can be sent from or received into any of these Go types:
+
+ struct { a, b int } // the same
+ *struct { a, b int } // extra indirection of the struct
+ struct { *a, **b int } // extra indirection of the fields
+ struct { a, b int64 } // different concrete value type; see below
+
+ It may also be received into any of these:
+
+ struct { a, b int } // the same
+ struct { b, a int } // ordering doesn't matter; matching is by name
+ struct { a, b, c int } // extra field (c) ignored
+ struct { b int } // missing field (a) ignored; data will be dropped
+ struct { b, c int } // missing field (a) ignored; extra field (c) ignored.
+
+ Attempting to receive into these types will draw a decode error:
+
+ struct { a int; b uint } // change of signedness for b
+ struct { a int; b float } // change of type for b
+ struct { } // no field names in common
+ struct { c, d int } // no field names in common
+
+ Integers are transmitted two ways: arbitrary precision signed integers or
+ arbitrary precision unsigned integers. There is no int8, int16 etc.
+ discrimination in the gob format; there are only signed and unsigned integers. As
+ described below, the transmitter sends the value in a variable-length encoding;
+ the receiver accepts the value and stores it in the destination variable.
+ Floating-point numbers are always sent using IEEE-754 64-bit precision (see
+ below).
+
+ Signed integers may be received into any signed integer variable: int, int16, etc.;
+ unsigned integers may be received into any unsigned integer variable; and floating
+ point values may be received into any floating point variable. However,
+ the destination variable must be able to represent the value or the decode
+ operation will fail. (TODO(r): enforce this.)
+
+ Structs, arrays and slices are also supported. Strings and arrays of bytes are
+ supported with a special, efficient representation (see below).
+
+ Maps are not supported yet, but they will be. Interfaces, functions, and channels
+ cannot be sent in a gob. Attempting to encode a value that contains one will
+ fail. (TODO(r): fix this - it panics now.)
+
+ The rest of this comment documents the encoding, details that are not important
+ for most users. Details are presented bottom-up.
+
+ An unsigned integer is encoded as an arbitrary-precision, variable-length sequence
+ of bytes. It is sent in little-endian order (low bits first), with seven bits per
+ byte. The high bit of each byte is zero, except that the high bit of the final
+ (highest precision) byte of the encoding will be set. Thus 0 is transmitted as
+ (80), 7 is transmitted as (87) and 256=2*128 is transmitted as (00 82).
+
+ A boolean is encoded within an unsigned integer: 0 for false, 1 for true.
+
+ A signed integer, i, is encoded within an unsigned integer, u. Within u, bits 1
+ upward contain the value; bit 0 says whether they should be complemented upon
+ receipt. The encode algorithm looks like this:
+
+ uint u;
+ if i < 0 {
+ u = (^i << 1) | 1 // complement i, bit 0 is 1
+ } else {
+ u = (i << 1) // do not complement i, bit 0 is 0
+ }
+ encodeUnsigned(u)
+
+ The low bit is therefore analogous to a sign bit, but making it the complement bit
+ instead guarantees that the largest negative integer is not a special case. For
+ example, -129=^128=(^256>>1) encodes as (01 82).
+
+ Floating-point numbers are always sent as a representation of a float64 value.
+ That value is converted to a uint64 using math.Float64bits. The uint64 is then
+ byte-reversed and sent as a regular unsigned integer. The byte-reversal means the
+ exponent and high-precision part of the mantissa go first. Since the low bits are
+ often zero, this can save encoding bytes. For instance, 17.0 is encoded in only
+ two bytes (40 e2).
+
+ Strings and slices of bytes are sent as an unsigned count followed by that many
+ uninterpreted bytes of the value.
+
+ All other slices and arrays are sent as an unsigned count followed by that many
+ elements using the standard gob encoding for their type, recursively.
+
+ Structs are sent as a sequence of (field number, field value) pairs. The field
+ value is sent using the standard gob encoding for its type, recursively. If a
+ field has the zero value for its type, it is omitted from the transmission. The
+ field number is defined by the type of the encoded struct: the first field of the
+ encoded type is field 0, the second is field 1, etc. When encoding a value, the
+ field numbers are delta encoded for efficiency and the fields are always sent in
+ order of increasing field number; the deltas are therefore unsigned. The
+ initialization for the delta encoding sets the field number to -1, so an unsigned
+ integer field 0 with value 7 is transmitted as unsigned delta = 1, unsigned value
+ = 7 or (81 87). Finally, after all the fields have been sent a terminating mark
+ denotes the end of the struct. That mark is a delta=0 value, which has
+ representation (80).
+
+ The representation of types is described below. When a type is defined on a given
+ connection between an Encoder and Decoder, it is assigned a signed integer type
+ id. When Encoder.Encode(v) is called, it makes sure there is an id assigned for
+ the type of v and all its elements and then it sends the pair (typeid, encoded-v)
+ where typeid is the type id of the encoded type of v and encoded-v is the gob
+ encoding of the value v.
+
+ To define a type, the encoder chooses an unused, positive type id and sends the
+ pair (-type id, encoded-type) where encoded-type is the gob encoding of a wireType
+ description, constructed from these types:
+
+ type wireType struct {
+ s structType;
+ }
+ type fieldType struct {
+ name string; // the name of the field.
+ id int; // the type id of the field, which must be already defined
+ }
+ type commonType {
+ name string; // the name of the struct type
+ id int; // the id of the type, repeated for so it's inside the type
+ }
+ type structType struct {
+ commonType;
+ field []fieldType; // the fields of the struct.
+ }
+
+ If there are nested type ids, the types for all inner type ids must be defined
+ before the top-level type id is used to describe an encoded-v.
+
+ For simplicity in setup, the connection is defined to understand these types a
+ priori, as well as the basic gob types int, uint, etc. Their ids are:
+
+ bool 1
+ int 2
+ uint 3
+ float 4
+ []byte 5
+ string 6
+ wireType 7
+ structType 8
+ commonType 9
+ fieldType 10
+
+ In summary, a gob stream looks like
+
+ ((-type id, encoding of a wireType)* (type id, encoding of a value))*
+
+ where * signifies zero or more repetitions and the type id of a value must
+ be predefined or be defined before the value in the stream.
+*/
package gob
import (
"sync";
)
+// An Encoder manages the transmission of type and data information to the
+// other side of a connection.
type Encoder struct {
- sync.Mutex; // each item must be sent atomically
+ mutex sync.Mutex; // each item must be sent atomically
w io.Writer; // where to send the data
- sent map[reflect.Type] TypeId; // which types we've already sent
+ sent map[reflect.Type] typeId; // which types we've already sent
state *encoderState; // so we can encode integers, strings directly
countState *encoderState; // stage for writing counts
buf []byte; // for collecting the output.
}
+// NewEncoder returns a new encoder that will transmit on the io.Writer.
func NewEncoder(w io.Writer) *Encoder {
enc := new(Encoder);
enc.w = w;
- enc.sent = make(map[reflect.Type] TypeId);
+ enc.sent = make(map[reflect.Type] typeId);
enc.state = new(encoderState);
enc.state.b = new(bytes.Buffer); // the rest isn't important; all we need is buffer and writer
enc.countState = new(encoderState);
typeLock.Unlock();
// Send the pair (-id, type)
// Id:
- encodeInt(enc.state, -int64(info.typeId));
+ encodeInt(enc.state, -int64(info.id));
// Type:
encode(enc.state.b, info.wire);
enc.send();
// Remember we've sent this type.
- enc.sent[rt] = info.typeId;
+ enc.sent[rt] = info.id;
// Remember we've sent the top-level, possibly indirect type too.
- enc.sent[origt] = info.typeId;
+ enc.sent[origt] = info.id;
// Now send the inner types
st := rt.(*reflect.StructType);
for i := 0; i < st.NumField(); i++ {
}
}
+// Encode transmits the data item represented by the empty interface value,
+// guaranteeing that all necessary type information has been transmitted first.
func (enc *Encoder) Encode(e interface{}) os.Error {
if enc.state.b.Len() > 0 || enc.countState.b.Len() > 0 {
panicln("Encoder: buffer not empty")
rt, indir := indirect(reflect.Typeof(e));
// Make sure we're single-threaded through here.
- enc.Lock();
- defer enc.Unlock();
+ enc.mutex.Lock();
+ defer enc.mutex.Unlock();
// Make sure the type is known to the other side.
// First, have we already sent this type?
t.Fatal("error decoding ET1 type:", err);
}
info := getTypeInfo(reflect.Typeof(ET1{}));
- trueWire1 := &wireType{s: info.typeId.gobType().(*structType)};
+ trueWire1 := &wireType{s: info.id.gobType().(*structType)};
if !reflect.DeepEqual(wire1, trueWire1) {
t.Fatalf("invalid wireType for ET1: expected %+v; got %+v\n", *trueWire1, *wire1);
}
t.Fatal("error decoding ET2 type:", err);
}
info = getTypeInfo(reflect.Typeof(ET2{}));
- trueWire2 := &wireType{s: info.typeId.gobType().(*structType)};
+ trueWire2 := &wireType{s: info.id.gobType().(*structType)};
if !reflect.DeepEqual(wire2, trueWire2) {
t.Fatalf("invalid wireType for ET2: expected %+v; got %+v\n", *trueWire2, *wire2);
}
}
// 8) The value of et1
newEt1 := new(ET1);
- et1Id := getTypeInfo(reflect.Typeof(*newEt1)).typeId;
+ et1Id := getTypeInfo(reflect.Typeof(*newEt1)).id;
err = decode(b, et1Id, newEt1);
if err != nil {
t.Fatal("error decoding ET1 value:", err);
"unicode";
)
-// Types are identified by an integer TypeId. These can be passed on the wire.
-// Internally, they are used as keys to a map to recover the underlying type info.
-type TypeId int32
+// A typeId represents a gob Type as an integer that can be passed on the wire.
+// Internally, typeIds are used as keys to a map to recover the underlying type info.
+type typeId int32
-var nextId TypeId // incremented for each new type we build
+var nextId typeId // incremented for each new type we build
var typeLock sync.Mutex // set while building a type
type gobType interface {
- id() TypeId;
- setId(id TypeId);
+ id() typeId;
+ setId(id typeId);
Name() string;
String() string;
- safeString(seen map[TypeId] bool) string;
+ safeString(seen map[typeId] bool) string;
}
var types = make(map[reflect.Type] gobType)
-var idToType = make(map[TypeId] gobType)
+var idToType = make(map[typeId] gobType)
func setTypeId(typ gobType) {
nextId++;
idToType[nextId] = typ;
}
-func (t TypeId) gobType() gobType {
+func (t typeId) gobType() gobType {
if t == 0 {
return nil
}
return idToType[t]
}
-func (t TypeId) String() string {
+// String returns the string representation of the type associated with the typeId.
+func (t typeId) String() string {
return t.gobType().String()
}
-func (t TypeId) Name() string {
+// Name returns the name of the type associated with the typeId.
+func (t typeId) Name() string {
return t.gobType().Name()
}
// Common elements of all types.
type commonType struct {
name string;
- _id TypeId;
+ _id typeId;
}
-func (t *commonType) id() TypeId {
+func (t *commonType) id() typeId {
return t._id
}
-func (t *commonType) setId(id TypeId) {
+func (t *commonType) setId(id typeId) {
t._id = id
}
}
// Basic type identifiers, predefined.
-var tBool TypeId
-var tInt TypeId
-var tUint TypeId
-var tFloat TypeId
-var tString TypeId
-var tBytes TypeId
+var tBool typeId
+var tInt typeId
+var tUint typeId
+var tFloat typeId
+var tString typeId
+var tBytes typeId
// Predefined because it's needed by the Decoder
-var tWireType TypeId
+var tWireType typeId
// Array type
type arrayType struct {
commonType;
- Elem TypeId;
+ Elem typeId;
Len int;
}
return a;
}
-func (a *arrayType) safeString(seen map[TypeId] bool) string {
+func (a *arrayType) safeString(seen map[typeId] bool) string {
if _, ok := seen[a._id]; ok {
return a.name
}
// Slice type
type sliceType struct {
commonType;
- Elem TypeId;
+ Elem typeId;
}
func newSliceType(name string, elem gobType) *sliceType {
return s;
}
-func (s *sliceType) safeString(seen map[TypeId] bool) string {
+func (s *sliceType) safeString(seen map[typeId] bool) string {
if _, ok := seen[s._id]; ok {
return s.name
}
}
func (s *sliceType) String() string {
- return s.safeString(make(map[TypeId] bool))
+ return s.safeString(make(map[typeId] bool))
}
// Struct type
type fieldType struct {
name string;
- typeId TypeId;
+ id typeId;
}
type structType struct {
field []*fieldType;
}
-func (s *structType) safeString(seen map[TypeId] bool) string {
+func (s *structType) safeString(seen map[typeId] bool) string {
if s == nil {
return "<nil>"
}
seen[s._id] = true;
str := s.name + " = struct { ";
for _, f := range s.field {
- str += fmt.Sprintf("%s %s; ", f.name, f.typeId.gobType().safeString(seen));
+ str += fmt.Sprintf("%s %s; ", f.name, f.id.gobType().safeString(seen));
}
str += "}";
return str;
}
func (s *structType) String() string {
- return s.safeString(make(map[TypeId] bool))
+ return s.safeString(make(map[typeId] bool))
}
func newStructType(name string) *structType {
return t;
}
+func checkId(want, got typeId) {
+ if want != got {
+ panicln("bootstrap type wrong id:", got.Name(), got, "not", want);
+ }
+}
+
// used for building the basic types; called only from init()
-func bootstrapType(name string, e interface{}) TypeId {
+func bootstrapType(name string, e interface{}, expect typeId) typeId {
rt := reflect.Typeof(e);
_, present := types[rt];
if present {
typ := &commonType{ name: name };
types[rt] = typ;
setTypeId(typ);
+ checkId(expect, nextId);
return nextId
}
type decEngine struct // defined in decode.go
type encEngine struct // defined in encode.go
type typeInfo struct {
- typeId TypeId;
+ id typeId;
encoder *encEngine;
wire *wireType;
}
if !ok {
info = new(typeInfo);
name := rt.Name();
- info.typeId = getType(name, rt).id();
+ info.id = getType(name, rt).id();
// assume it's a struct type
- info.wire = &wireType{info.typeId.gobType().(*structType)};
+ info.wire = &wireType{info.id.gobType().(*structType)};
typeInfoMap[rt] = info;
}
return info;
}
func init() {
- tBool = bootstrapType("bool", false);
- tInt = bootstrapType("int", int(0));
- tUint = bootstrapType("uint", uint(0));
- tFloat = bootstrapType("float", float64(0));
+ // Create and check predefined types
+ tBool = bootstrapType("bool", false, 1);
+ tInt = bootstrapType("int", int(0), 2);
+ tUint = bootstrapType("uint", uint(0), 3);
+ tFloat = bootstrapType("float", float64(0), 4);
// The string for tBytes is "bytes" not "[]byte" to signify its specialness.
- tBytes = bootstrapType("bytes", make([]byte, 0));
- tString= bootstrapType("string", "");
- tWireType = getTypeInfo(reflect.Typeof(wireType{})).typeId;
+ tBytes = bootstrapType("bytes", make([]byte, 0), 5);
+ tString= bootstrapType("string", "", 6);
+ tWireType = getTypeInfo(reflect.Typeof(wireType{})).id;
+ checkId(7, tWireType);
+ checkId(8, getTypeInfo(reflect.Typeof(structType{})).id);
+ checkId(9, getTypeInfo(reflect.Typeof(commonType{})).id);
+ checkId(10, getTypeInfo(reflect.Typeof(fieldType{})).id);
}
)
type typeT struct {
- typeId TypeId;
+ id typeId;
str string;
}
var basicTypes = []typeT {
// Sanity checks
func TestBasic(t *testing.T) {
for _, tt := range basicTypes {
- if tt.typeId.String() != tt.str {
- t.Errorf("checkType: expected %q got %s", tt.str, tt.typeId.String())
+ if tt.id.String() != tt.str {
+ t.Errorf("checkType: expected %q got %s", tt.str, tt.id.String())
}
- if tt.typeId == 0 {
+ if tt.id == 0 {
t.Errorf("id for %q is zero", tt.str)
}
}