]> Cypherpunks repositories - gostls13.git/commitdiff
first cut at gob decoder.
authorRob Pike <r@golang.org>
Sat, 11 Jul 2009 22:45:54 +0000 (15:45 -0700)
committerRob Pike <r@golang.org>
Sat, 11 Jul 2009 22:45:54 +0000 (15:45 -0700)
R=rsc
DELTA=184  (181 added, 1 deleted, 2 changed)
OCL=31474
CL=31486

src/pkg/gob/Makefile
src/pkg/gob/decoder.go [new file with mode: 0644]
src/pkg/gob/encoder_test.go
src/pkg/gob/type.go

index 42383ba05ccf22c9db075313eec9b427b38e1ec4..54bcf813c03a2066285ac874a468ee2699e889fa 100644 (file)
@@ -40,6 +40,7 @@ O2=\
        encode.$O\
 
 O3=\
+       decoder.$O\
        encoder.$O\
 
 
@@ -55,7 +56,7 @@ a2: $(O2)
        rm -f $(O2)
 
 a3: $(O3)
-       $(AR) grc _obj$D/gob.a encoder.$O
+       $(AR) grc _obj$D/gob.a decoder.$O encoder.$O
        rm -f $(O3)
 
 
diff --git a/src/pkg/gob/decoder.go b/src/pkg/gob/decoder.go
new file mode 100644 (file)
index 0000000..4941a78
--- /dev/null
@@ -0,0 +1,85 @@
+// 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 (
+       "gob";
+       "io";
+       "os";
+       "reflect";
+       "sync";
+)
+
+type Decoder struct {
+       sync.Mutex;     // each item must be received atomically
+       seen    map[TypeId] *wireType;  // which types we've already seen described
+       state   *DecState;      // so we can encode integers, strings directly
+}
+
+func NewDecoder(r io.Reader) *Decoder {
+       dec := new(Decoder);
+       dec.seen = make(map[TypeId] *wireType);
+       dec.state = new(DecState);
+       dec.state.r = r;        // the rest isn't important; all we need is buffer and reader
+
+       return dec;
+}
+
+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");
+               return
+       }
+
+       // Type:
+       wire := new(wireType);
+       Decode(dec.state.r, wire);
+       // Remember we've seen this type.
+       dec.seen[id] = wire;
+}
+
+// The value underlying e must be the correct type for the next
+// value to be received for this decoder.
+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();
+
+       var id TypeId;
+       for dec.state.err == nil {
+               // Receive a type id.
+               id = TypeId(DecodeInt(dec.state));
+
+               // If the id is positive, we have a value.  0 is the error state
+               if id >= 0 {
+                       break;
+               }
+
+               // The id is negative; a type descriptor follows.
+               dec.recvType(-id);
+       }
+       if dec.state.err != nil {
+               return dec.state.err
+       }
+
+       info := getTypeInfo(rt);
+
+       // Check type compatibility.
+       // TODO(r): need to make the decoder work correctly if the wire type is compatible
+       // but not equal to the local type (e.g, extra fields).
+       if info.wire.name != dec.seen[id].name {
+               dec.state.err = os.ErrorString("gob decode: incorrect type for wire value");
+               return dec.state.err
+       }
+
+       // Receive a value.
+       Decode(dec.state.r, e);
+
+       // Release and return.
+       return dec.state.err
+}
index ad37e2b2b3453a1766f3fd5279cb64a3cb8334d1..56f6151dbbf86ac2dfc08862b9b70cfdc695181e 100644 (file)
@@ -6,7 +6,6 @@ package gob
 
 import (
        "bytes";
-"fmt";         // DELETE
        "gob";
        "os";
        "reflect";
@@ -25,6 +24,27 @@ type ET1 struct {
        next *ET1;
 }
 
+// Like ET1 but with a different name for a field
+type ET3 struct {
+       a int;
+       et2 *ET2;
+       differentNext *ET1;
+}
+
+// Like ET1 but with a different type for a field
+type ET4 struct {
+       a int;
+       et2 *ET1;
+       next *ET2;
+}
+
+// Like ET1 but with a different type for a self-referencing field
+type ET5 struct {
+       a int;
+       et2 *ET2;
+       next *ET1;
+}
+
 func TestBasicEncoder(t *testing.T) {
        b := new(bytes.Buffer);
        enc := NewEncoder(b);
@@ -116,3 +136,78 @@ func TestBasicEncoder(t *testing.T) {
                t.Error("2nd round: not at eof;", b.Len(), "bytes left")
        }
 }
+
+func TestEncoderDecoder(t *testing.T) {
+       b := new(bytes.Buffer);
+       enc := NewEncoder(b);
+       et1 := new(ET1);
+       et1.a = 7;
+       et1.et2 = new(ET2);
+       enc.Encode(et1);
+       if enc.state.err != nil {
+               t.Error("encoder fail:", enc.state.err)
+       }
+       dec := NewDecoder(b);
+       newEt1 := new(ET1);
+       dec.Decode(newEt1);
+       if dec.state.err != nil {
+               t.Fatalf("error decoding ET1:", dec.state.err);
+       }
+
+       if !reflect.DeepEqual(et1, newEt1) {
+               t.Fatalf("invalid data for et1: expected %+v; got %+v\n", *et1, *newEt1);
+       }
+       if b.Len() != 0 {
+               t.Error("not at eof;", b.Len(), "bytes left")
+       }
+
+       enc.Encode(et1);
+       newEt1 = new(ET1);
+       dec.Decode(newEt1);
+       if dec.state.err != nil {
+               t.Fatalf("round 2: error decoding ET1:", dec.state.err);
+       }
+       if !reflect.DeepEqual(et1, newEt1) {
+               t.Fatalf("round 2: invalid data for et1: expected %+v; got %+v\n", *et1, *newEt1);
+       }
+       if b.Len() != 0 {
+               t.Error("round 2: not at eof;", b.Len(), "bytes left")
+       }
+
+       // Now test with a running encoder/decoder pair that we recognize a type mismatch.
+       enc.Encode(et1);
+       if enc.state.err != nil {
+               t.Error("round 3: encoder fail:", enc.state.err)
+       }
+       newEt2 := new(ET2);
+       dec.Decode(newEt2);
+       if dec.state.err == nil {
+               t.Fatalf("round 3: expected `bad type' error decoding ET2");
+       }
+}
+
+// Run one value through the encoder/decoder, but use the wrong type.
+func badTypeCheck(e interface{}, msg string, t *testing.T) {
+       b := new(bytes.Buffer);
+       enc := NewEncoder(b);
+       et1 := new(ET1);
+       et1.a = 7;
+       et1.et2 = new(ET2);
+       enc.Encode(et1);
+       if enc.state.err != nil {
+               t.Error("encoder fail:", enc.state.err)
+       }
+       dec := NewDecoder(b);
+       dec.Decode(e);
+       if dec.state.err == nil {
+               t.Error("expected error for", msg);
+       }
+}
+
+// Test that we recognize a bad type the first time.
+func TestWrongTypeDecoder(t *testing.T) {
+       badTypeCheck(new(ET2), "different number of fields", t);
+       badTypeCheck(new(ET3), "different name of field", t);
+       badTypeCheck(new(ET4), "different type of field", t);
+       badTypeCheck(new(ET5), "different type of self-reference field", t);
+}
index 66636a4d44b2a58a8980dd3b82400043795acd5d..cd05a390bafb6fcb2fedf55bce25bce8d674112f 100644 (file)
@@ -15,7 +15,7 @@ import (
 
 // 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 uint32
+type TypeId int32
 
 var id TypeId  // incremented for each new type we build
 var typeLock   sync.Mutex      // set while building a type