Commit 1778f50d authored by Rob Pike's avatar Rob Pike

gob: decode into nil, this time for sure.

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
parent 1723fbe1
...@@ -481,6 +481,19 @@ func (dec *Decoder) ignoreStruct(engine *decEngine) (err os.Error) { ...@@ -481,6 +481,19 @@ func (dec *Decoder) ignoreStruct(engine *decEngine) (err os.Error) {
return nil 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) { 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} instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl}
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
...@@ -653,8 +666,8 @@ func (dec *Decoder) ignoreInterface(state *decodeState) { ...@@ -653,8 +666,8 @@ func (dec *Decoder) ignoreInterface(state *decodeState) {
if id < 0 { if id < 0 {
error(dec.err) error(dec.err)
} }
// At this point, the decoder buffer contains the value. Just toss it. // At this point, the decoder buffer contains a delimited value. Just toss it.
state.b.Reset() state.b.Next(int(state.decodeUint()))
} }
// Index by Go types. // Index by Go types.
...@@ -901,6 +914,16 @@ func (dec *Decoder) compileSingle(remoteId typeId, rt reflect.Type) (engine *dec ...@@ -901,6 +914,16 @@ func (dec *Decoder) compileSingle(remoteId typeId, rt reflect.Type) (engine *dec
return 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? // Is this an exported - upper case - name?
func isExported(name string) bool { func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name) rune, _ := utf8.DecodeRuneInString(name)
...@@ -984,7 +1007,12 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er ...@@ -984,7 +1007,12 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er
// To handle recursive types, mark this engine as underway before compiling. // To handle recursive types, mark this engine as underway before compiling.
enginePtr = new(*decEngine) enginePtr = new(*decEngine)
dec.ignorerCache[wireId] = enginePtr 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 { if err != nil {
dec.ignorerCache[wireId] = nil, false dec.ignorerCache[wireId] = nil, false
} }
...@@ -993,6 +1021,10 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er ...@@ -993,6 +1021,10 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er
} }
func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) os.Error { 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. // Dereference down to the underlying struct type.
rt, indir := indirect(val.Type()) rt, indir := indirect(val.Type())
enginePtr, err := dec.getDecEnginePtr(wireId, rt) enginePtr, err := dec.getDecEnginePtr(wireId, rt)
...@@ -1010,6 +1042,18 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) os.Error { ...@@ -1010,6 +1042,18 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) os.Error {
return dec.decodeSingle(engine, rt, uintptr(val.Addr()), indir) 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() { func init() {
var iop, uop decOp var iop, uop decOp
switch reflect.Typeof(int(0)).Bits() { switch reflect.Typeof(int(0)).Bits() {
......
...@@ -183,12 +183,8 @@ func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { ...@@ -183,12 +183,8 @@ func (dec *Decoder) DecodeValue(value reflect.Value) os.Error {
dec.buf.Reset() // In case data lingers from previous invocation. dec.buf.Reset() // In case data lingers from previous invocation.
dec.err = nil dec.err = nil
id := dec.decodeTypeSequence(false) id := dec.decodeTypeSequence(false)
if id >= 0 { if dec.err == nil {
// A nil value means "ignore the data". Since it's already read into dec.err = dec.decodeValue(id, value)
// the decoder's buffer, all we need to do is not bother to decode it.
if value != nil {
dec.err = dec.decodeValue(id, value)
}
} }
return dec.err return dec.err
} }
......
...@@ -6,6 +6,7 @@ package gob ...@@ -6,6 +6,7 @@ package gob
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"os" "os"
"reflect" "reflect"
...@@ -120,7 +121,7 @@ func corruptDataCheck(s string, err os.Error, t *testing.T) { ...@@ -120,7 +121,7 @@ func corruptDataCheck(s string, err os.Error, t *testing.T) {
dec := NewDecoder(b) dec := NewDecoder(b)
err1 := dec.Decode(new(ET2)) err1 := dec.Decode(new(ET2))
if err1 != err { 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) { ...@@ -384,54 +385,72 @@ func TestInterfaceIndirect(t *testing.T) {
} }
} }
func TestDecodeIntoEmptyStruct(t *testing.T) { // Now follow various tests that decode into things that can't represent the
type Empty struct{} // encoded value, all of which should be legal.
empty := &Empty{}
b := new(bytes.Buffer) // Also, when the ignored object contains an interface value, it may define
enc := NewEncoder(b) // types. Make sure that skipping the value still defines the types by using
err := enc.Encode(&struct{ A int }{23}) // the encoder/decoder pair to send a value afterwards. If an interface
if err != nil { // is sent, its type in the test is always NewType0, so this checks that the
t.Fatal("encode error:", err) // encoder and decoder don't skew with respect to type definitions.
}
dec := NewDecoder(b) type Struct0 struct {
err = dec.Decode(empty) I interface{}
if err != nil {
t.Fatal("encode error:", err)
}
} }
func TestStructDecodeIntoNil(t *testing.T) { type NewType0 struct {
nonempty := &struct{ A int }{23} S string
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())
}
} }
func TestSingletonDecodeIntoNil(t *testing.T) { type ignoreTest struct {
b := new(bytes.Buffer) in, out interface{}
enc := NewEncoder(b) }
err := enc.Encode("hello world")
if err != nil { var ignoreTests = []ignoreTest{
t.Fatal("encode error:", err) // Decode normal struct into an empty struct
} {&struct{ A int }{23}, &struct{}{}},
dec := NewDecoder(b) // Decode normal struct into a nil.
err = dec.Decode(nil) {&struct{ A int }{23}, nil},
if err != nil { // Decode singleton string into a nil.
t.Fatal("encode error:", err) {"hello, world", nil},
} // Decode singleton slice into a nil.
if b.Len() != 0 { {[]int{1, 2, 3, 4}, nil},
t.Fatalf("%d bytes remain after decode", b.Len()) // 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)
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment