diff --git a/src/pkg/gob/codec_test.go b/src/pkg/gob/codec_test.go index de2c5d6bc9a02e27c6299a205ed47d67fdd5e242..848a8719473fd160d1e671e05ca971a560a731b9 100644 --- a/src/pkg/gob/codec_test.go +++ b/src/pkg/gob/codec_test.go @@ -36,6 +36,7 @@ var encodeT = []EncodeT { 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); @@ -552,7 +553,7 @@ func TestEndToEnd(t *testing.T) { 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); } @@ -570,7 +571,7 @@ func TestNesting(t *testing.T) { 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); } @@ -612,7 +613,7 @@ func TestAutoIndirection(t *testing.T) { 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); @@ -637,7 +638,7 @@ func TestAutoIndirection(t *testing.T) { 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); @@ -647,7 +648,7 @@ func TestAutoIndirection(t *testing.T) { 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); @@ -685,7 +686,7 @@ func TestReorderedFields(t *testing.T) { 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); @@ -723,7 +724,7 @@ func TestIgnoredFields(t *testing.T) { 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); diff --git a/src/pkg/gob/decode.go b/src/pkg/gob/decode.go index 991b6f03f91907bab9ca659ca837a6ba89faf97e..a9148eb834a6488456e8d6e27fca1f2a95c90632 100644 --- a/src/pkg/gob/decode.go +++ b/src/pkg/gob/decode.go @@ -18,8 +18,8 @@ import ( ) 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. @@ -347,7 +347,7 @@ func decodeStruct(engine *decEngine, rtyp *reflect.StructType, b *bytes.Buffer, } fieldnum := state.fieldnum + delta; if fieldnum >= len(engine.instr) { - state.err = ErrRange; + state.err = errRange; break; } instr := &engine.instr[fieldnum]; @@ -376,7 +376,7 @@ func ignoreStruct(engine *decEngine, b *bytes.Buffer) os.Error { } fieldnum := state.fieldnum + delta; if fieldnum >= len(engine.instr) { - state.err = ErrRange; + state.err = errRange; break; } instr := &engine.instr[fieldnum]; @@ -474,7 +474,7 @@ var decOpMap = map[reflect.Type] decOp { reflect.Typeof((*reflect.StringType)(nil)): decString, } -var decIgnoreOpMap = map[TypeId] decOp { +var decIgnoreOpMap = map[typeId] decOp { tBool: ignoreUint, tInt: ignoreUint, tUint: ignoreUint, @@ -483,12 +483,12 @@ var decIgnoreOpMap = map[TypeId] decOp { 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 { @@ -537,7 +537,7 @@ func decOpFor(wireId TypeId, rt reflect.Type) (decOp, int, os.Error) { } // 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 @@ -583,7 +583,7 @@ func decIgnoreOpFor(wireId TypeId) (decOp, os.Error) { // 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(); @@ -645,11 +645,11 @@ func compatibleType(fr reflect.Type, fw TypeId) bool { 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)); @@ -660,17 +660,17 @@ func compileDec(wireId TypeId, rt reflect.Type) (engine *decEngine, err os.Error 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 } @@ -680,14 +680,14 @@ func compileDec(wireId TypeId, rt reflect.Type) (engine *decEngine, err os.Error 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 { @@ -707,7 +707,7 @@ type emptyStruct struct {} 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. @@ -721,7 +721,7 @@ func getIgnoreEnginePtr(wireId TypeId) (enginePtr **decEngine, err os.Error) { 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); diff --git a/src/pkg/gob/decoder.go b/src/pkg/gob/decoder.go index 609a20484c6c37246a68b5ba4760357d808fde79..7dd99a0762331341339ef708f98a6b59f7cb1b5f 100644 --- a/src/pkg/gob/decoder.go +++ b/src/pkg/gob/decoder.go @@ -13,27 +13,30 @@ import ( "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"); @@ -47,14 +50,16 @@ func (dec *Decoder) recvType(id TypeId) { 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 { @@ -81,7 +86,7 @@ func (dec *Decoder) Decode(e interface{}) os.Error { } // Receive a type id. - id := TypeId(decodeInt(dec.state)); + id := typeId(decodeInt(dec.state)); if dec.state.err != nil { break; } diff --git a/src/pkg/gob/encoder.go b/src/pkg/gob/encoder.go index f75eccd958f54c008ca955e782951f8518cb350d..1182a70c4313b3b3dfaf5f3020296efaa2047473 100644 --- a/src/pkg/gob/encoder.go +++ b/src/pkg/gob/encoder.go @@ -2,6 +2,183 @@ // 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 ( @@ -13,19 +190,22 @@ 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); @@ -91,15 +271,15 @@ func (enc *Encoder) sendType(origt reflect.Type) { 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++ { @@ -107,6 +287,8 @@ func (enc *Encoder) sendType(origt reflect.Type) { } } +// 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") @@ -114,8 +296,8 @@ func (enc *Encoder) Encode(e interface{}) os.Error { 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? diff --git a/src/pkg/gob/encoder_test.go b/src/pkg/gob/encoder_test.go index 4d9258345b1d1888f514947be0bcec4f21e58278..a7e66a57e39a9a0f9f13afc0c29334b3c4ad547f 100644 --- a/src/pkg/gob/encoder_test.go +++ b/src/pkg/gob/encoder_test.go @@ -70,7 +70,7 @@ func TestBasicEncoder(t *testing.T) { 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); } @@ -91,7 +91,7 @@ func TestBasicEncoder(t *testing.T) { 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); } @@ -107,7 +107,7 @@ func TestBasicEncoder(t *testing.T) { } // 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); diff --git a/src/pkg/gob/type.go b/src/pkg/gob/type.go index 006a0e442bca359d8af2b417402242e121c0a106..68d047ffd3fc29e3f2ce81e2fe177c3856a4f0ba 100644 --- a/src/pkg/gob/type.go +++ b/src/pkg/gob/type.go @@ -13,23 +13,23 @@ import ( "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++; @@ -37,32 +37,34 @@ func setTypeId(typ gobType) { 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 } @@ -79,20 +81,20 @@ func (t *commonType) Name() string { } // 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; } @@ -102,7 +104,7 @@ func newArrayType(name string, elem gobType, length int) *arrayType { 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 } @@ -117,7 +119,7 @@ func (a *arrayType) String() string { // Slice type type sliceType struct { commonType; - Elem TypeId; + Elem typeId; } func newSliceType(name string, elem gobType) *sliceType { @@ -126,7 +128,7 @@ 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 } @@ -135,13 +137,13 @@ func (s *sliceType) safeString(seen map[TypeId] bool) string { } 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 { @@ -149,7 +151,7 @@ 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>" } @@ -159,14 +161,14 @@ func (s *structType) safeString(seen map[TypeId] bool) string { 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 { @@ -294,8 +296,14 @@ func getType(name string, rt reflect.Type) gobType { 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 { @@ -304,6 +312,7 @@ func bootstrapType(name string, e interface{}) TypeId { typ := &commonType{ name: name }; types[rt] = typ; setTypeId(typ); + checkId(expect, nextId); return nextId } @@ -329,7 +338,7 @@ func (w *wireType) name() string { 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; } @@ -346,21 +355,26 @@ func getTypeInfo(rt reflect.Type) *typeInfo { 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); } diff --git a/src/pkg/gob/type_test.go b/src/pkg/gob/type_test.go index d190a3045e278f5d1d30d812a56d05d39e50a9fb..2f11ba3fea3518b0d8b48525d0fa7aab412a8400 100644 --- a/src/pkg/gob/type_test.go +++ b/src/pkg/gob/type_test.go @@ -12,7 +12,7 @@ import ( ) type typeT struct { - typeId TypeId; + id typeId; str string; } var basicTypes = []typeT { @@ -33,10 +33,10 @@ func getTypeUnlocked(name string, rt reflect.Type) gobType { // 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) } }