]> Cypherpunks repositories - gostls13.git/commitdiff
gob: decode into nil, this time for sure.
authorRob Pike <r@golang.org>
Mon, 14 Feb 2011 18:17:30 +0000 (10:17 -0800)
committerRob Pike <r@golang.org>
Mon, 14 Feb 2011 18:17:30 +0000 (10:17 -0800)
Yesterday's change was too simple-minded and failed if an
interface value was being discarded.  We need to parse the
data stream and remember any type information that arrives.

Also fix a minor bug when ignoring an interface: toss only what
we know about, not everything.

R=rsc
CC=golang-dev
https://golang.org/cl/4179045

src/pkg/gob/decode.go
src/pkg/gob/decoder.go
src/pkg/gob/encoder_test.go

index db8b968700a8d5c3e4550582ae276dc7a8ab5c1e..9667f6157ec1ce306c154e86593503054599c634 100644 (file)
@@ -481,6 +481,19 @@ func (dec *Decoder) ignoreStruct(engine *decEngine) (err os.Error) {
        return nil
 }
 
+func (dec *Decoder) ignoreSingle(engine *decEngine) (err os.Error) {
+       defer catchError(&err)
+       state := newDecodeState(dec, &dec.buf)
+       state.fieldnum = singletonField
+       delta := int(state.decodeUint())
+       if delta != 0 {
+               errorf("gob decode: corrupted data: non-zero delta for singleton")
+       }
+       instr := &engine.instr[singletonField]
+       instr.op(instr, state, unsafe.Pointer(nil))
+       return nil
+}
+
 func (dec *Decoder) decodeArrayHelper(state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl os.ErrorString) {
        instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl}
        for i := 0; i < length; i++ {
@@ -653,8 +666,8 @@ func (dec *Decoder) ignoreInterface(state *decodeState) {
        if id < 0 {
                error(dec.err)
        }
-       // At this point, the decoder buffer contains the value. Just toss it.
-       state.b.Reset()
+       // At this point, the decoder buffer contains a delimited value. Just toss it.
+       state.b.Next(int(state.decodeUint()))
 }
 
 // Index by Go types.
@@ -901,6 +914,16 @@ func (dec *Decoder) compileSingle(remoteId typeId, rt reflect.Type) (engine *dec
        return
 }
 
+func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err os.Error) {
+       engine = new(decEngine)
+       engine.instr = make([]decInstr, 1) // one item
+       op := dec.decIgnoreOpFor(remoteId)
+       ovfl := overflow(dec.typeString(remoteId))
+       engine.instr[0] = decInstr{op, 0, 0, 0, ovfl}
+       engine.numInstr = 1
+       return
+}
+
 // Is this an exported - upper case - name?
 func isExported(name string) bool {
        rune, _ := utf8.DecodeRuneInString(name)
@@ -984,7 +1007,12 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er
                // To handle recursive types, mark this engine as underway before compiling.
                enginePtr = new(*decEngine)
                dec.ignorerCache[wireId] = enginePtr
-               *enginePtr, err = dec.compileDec(wireId, emptyStructType)
+               wire := dec.wireType[wireId]
+               if wire != nil && wire.StructT != nil {
+                       *enginePtr, err = dec.compileDec(wireId, emptyStructType)
+               } else {
+                       *enginePtr, err = dec.compileIgnoreSingle(wireId)
+               }
                if err != nil {
                        dec.ignorerCache[wireId] = nil, false
                }
@@ -993,6 +1021,10 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er
 }
 
 func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) os.Error {
+       // If the value is nil, it means we should just ignore this item.
+       if val == nil {
+               return dec.decodeIgnoredValue(wireId)
+       }
        // Dereference down to the underlying struct type.
        rt, indir := indirect(val.Type())
        enginePtr, err := dec.getDecEnginePtr(wireId, rt)
@@ -1010,6 +1042,18 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) os.Error {
        return dec.decodeSingle(engine, rt, uintptr(val.Addr()), indir)
 }
 
+func (dec *Decoder) decodeIgnoredValue(wireId typeId) os.Error {
+       enginePtr, err := dec.getIgnoreEnginePtr(wireId)
+       if err != nil {
+               return err
+       }
+       wire := dec.wireType[wireId]
+       if wire != nil && wire.StructT != nil {
+               return dec.ignoreStruct(*enginePtr)
+       }
+       return dec.ignoreSingle(*enginePtr)
+}
+
 func init() {
        var iop, uop decOp
        switch reflect.Typeof(int(0)).Bits() {
index 922794ea8325a64de2fc8e2b0a83aa3ab8c04a31..f7c994ffa7844ec5d3e99be41deffce168d5647a 100644 (file)
@@ -183,12 +183,8 @@ func (dec *Decoder) DecodeValue(value reflect.Value) os.Error {
        dec.buf.Reset() // In case data lingers from previous invocation.
        dec.err = nil
        id := dec.decodeTypeSequence(false)
-       if id >= 0 {
-               // A nil value means "ignore the data".  Since it's already read into
-               // the decoder's buffer, all we need to do is not bother to decode it.
-               if value != nil {
-                       dec.err = dec.decodeValue(id, value)
-               }
+       if dec.err == nil {
+               dec.err = dec.decodeValue(id, value)
        }
        return dec.err
 }
index 8825fe15d0b73446f280ac6dd0c98b75a4b40ceb..3e06db72720a7bdb32816d64f3f3771278e144f0 100644 (file)
@@ -6,6 +6,7 @@ package gob
 
 import (
        "bytes"
+       "fmt"
        "io"
        "os"
        "reflect"
@@ -120,7 +121,7 @@ func corruptDataCheck(s string, err os.Error, t *testing.T) {
        dec := NewDecoder(b)
        err1 := dec.Decode(new(ET2))
        if err1 != err {
-               t.Error("expected error", err, "got", err1)
+               t.Errorf("from %q expected error %s; got %s", s, err, err1)
        }
 }
 
@@ -384,54 +385,72 @@ func TestInterfaceIndirect(t *testing.T) {
        }
 }
 
-func TestDecodeIntoEmptyStruct(t *testing.T) {
-       type Empty struct{}
-       empty := &Empty{}
-       b := new(bytes.Buffer)
-       enc := NewEncoder(b)
-       err := enc.Encode(&struct{ A int }{23})
-       if err != nil {
-               t.Fatal("encode error:", err)
-       }
-       dec := NewDecoder(b)
-       err = dec.Decode(empty)
-       if err != nil {
-               t.Fatal("encode error:", err)
-       }
+// Now follow various tests that decode into things that can't represent the
+// encoded value, all of which should be legal.
+
+// Also, when the ignored object contains an interface value, it may define
+// types. Make sure that skipping the value still defines the types by using
+// the encoder/decoder pair to send a value afterwards.  If an interface
+// is sent, its type in the test is always NewType0, so this checks that the
+// encoder and decoder don't skew with respect to type definitions.
+
+type Struct0 struct {
+       I interface{}
 }
 
-func TestStructDecodeIntoNil(t *testing.T) {
-       nonempty := &struct{ A int }{23}
-       b := new(bytes.Buffer)
-       enc := NewEncoder(b)
-       err := enc.Encode(nonempty)
-       if err != nil {
-               t.Fatal("encode error:", err)
-       }
-       dec := NewDecoder(b)
-       err = dec.Decode(nil)
-       if err != nil {
-               t.Fatal("encode error:", err)
-       }
-       if b.Len() != 0 {
-               t.Fatalf("%d bytes remain after decode", b.Len())
-       }
+type NewType0 struct {
+       S string
 }
 
-func TestSingletonDecodeIntoNil(t *testing.T) {
-       b := new(bytes.Buffer)
-       enc := NewEncoder(b)
-       err := enc.Encode("hello world")
-       if err != nil {
-               t.Fatal("encode error:", err)
-       }
-       dec := NewDecoder(b)
-       err = dec.Decode(nil)
-       if err != nil {
-               t.Fatal("encode error:", err)
-       }
-       if b.Len() != 0 {
-               t.Fatalf("%d bytes remain after decode", b.Len())
+type ignoreTest struct {
+       in, out interface{}
+}
+
+var ignoreTests = []ignoreTest{
+       // Decode normal struct into an empty struct
+       {&struct{ A int }{23}, &struct{}{}},
+       // Decode normal struct into a nil.
+       {&struct{ A int }{23}, nil},
+       // Decode singleton string into a nil.
+       {"hello, world", nil},
+       // Decode singleton slice into a nil.
+       {[]int{1, 2, 3, 4}, nil},
+       // Decode struct containing an interface into a nil.
+       {&Struct0{&NewType0{"value0"}}, nil},
+       // Decode singleton slice of interfaces into a nil.
+       {[]interface{}{"hi", &NewType0{"value1"}, 23}, nil},
+}
+
+func TestDecodeIntoNothing(t *testing.T) {
+       Register(new(NewType0))
+       for i, test := range ignoreTests {
+               b := new(bytes.Buffer)
+               enc := NewEncoder(b)
+               err := enc.Encode(test.in)
+               if err != nil {
+                       t.Errorf("%d: encode error %s:", i, err)
+                       continue
+               }
+               dec := NewDecoder(b)
+               err = dec.Decode(test.out)
+               if err != nil {
+                       t.Errorf("%d: decode error: %s", i, err)
+                       continue
+               }
+               // Now see if the encoder and decoder are in a consistent state.
+               str := fmt.Sprintf("Value %d", i)
+               err = enc.Encode(&NewType0{str})
+               if err != nil {
+                       t.Fatalf("%d: NewType0 encode error: %s", i, err)
+               }
+               ns := new(NewType0)
+               err = dec.Decode(ns)
+               if err != nil {
+                       t.Fatalf("%d: NewType0 decode error: %s", i, err)
+               }
+               if ns.S != str {
+                       t.Fatalf("%d: expected %q got %q", i, str, ns.S)
+               }
        }
 }