XEncoder test passes
Change-Id: I8d7744a59971daac8509f025bb29af7e78e6b9a1
diff --git a/vdl/pipe.go b/vdl/pipe.go
index 8f4df48..cebaa18 100644
--- a/vdl/pipe.go
+++ b/vdl/pipe.go
@@ -296,7 +296,7 @@
d.Enc.Mu.Lock()
defer d.Enc.Mu.Unlock()
if d.Enc.DecStarted && d.Enc.State != pipeStateDecoder {
- return d.Close(errInvalidPipeState)
+ return d.Enc.closeLocked(errInvalidPipeState)
}
if d.IgnoringNextStartValue {
d.IgnoringNextStartValue = false
@@ -310,10 +310,10 @@
}
top := d.decTop()
if top == nil {
- return d.Close(errEmptyPipeStack)
+ return d.Enc.closeLocked(errEmptyPipeStack)
}
if top.NextOp != pipeStartDec {
- return d.Close(fmt.Errorf("vdl: pipe expected state %v, but got %v", pipeStartDec, top.NextOp))
+ return d.Enc.closeLocked(fmt.Errorf("vdl: pipe expected state %v, but got %v", pipeStartDec, top.NextOp))
}
top.NextOp = top.NextOp.Next()
return d.Enc.Err
@@ -653,7 +653,7 @@
case numberFloat:
x := d.Enc.ArgFloat
if bitlen <= 32 && (x < -math.MaxFloat32 || x > math.MaxFloat32) {
- return 0, d.Close(fmt.Errorf(errFmt, "float", bitlen, x))
+ return 0, d.Enc.closeLocked(fmt.Errorf(errFmt, "float", bitlen, x))
}
return x, d.Enc.Err
}
@@ -672,10 +672,13 @@
func (d *pipeDecoder) DecodeBytes(fixedLen int, b *[]byte) error {
if !d.Enc.IsBytes {
- return d.Close(bytesVDLRead(fixedLen, b, d))
+ if err := bytesVDLRead(fixedLen, b, d); err != nil {
+ return d.Enc.closeLocked(err)
+ }
+ return nil
}
if fixedLen >= 0 && fixedLen != len(d.Enc.ArgBytes) {
- return d.Close(fmt.Errorf("invalid byte len: got %v, want %v", len(d.Enc.ArgBytes), fixedLen))
+ return d.Enc.closeLocked(fmt.Errorf("invalid byte len: got %v, want %v", len(d.Enc.ArgBytes), fixedLen))
}
if len(*b) < len(d.Enc.ArgBytes) {
*b = make([]byte, len(d.Enc.ArgBytes))
diff --git a/vdl/target.go b/vdl/target.go
index 4e0acef..517ebb3 100644
--- a/vdl/target.go
+++ b/vdl/target.go
@@ -167,6 +167,10 @@
}
return result, nil
}
+ if !rv.IsValid() {
+ // TODO(bprosnitz) Is this the behavior we want?
+ return ZeroValue(AnyType), nil
+ }
var result *Value
err := convertPipe(&result, rv.Interface())
return result, err
diff --git a/vom/.api b/vom/.api
index e22048f..d8bff51 100644
--- a/vom/.api
+++ b/vom/.api
@@ -65,6 +65,8 @@
pkg vom, func RawBytesOf(interface{}) *RawBytes
pkg vom, func VDLReadPrimitive(vdl.Decoder, *Primitive) error
pkg vom, func VersionedEncode(Version, interface{}) ([]byte, error)
+pkg vom, func VersionedXEncode(Version, interface{}) ([]byte, error)
+pkg vom, func XDecode([]byte, interface{}) error
pkg vom, method (*ControlKind) FillVDLTarget(vdl.Target, *vdl.Type) error
pkg vom, method (*ControlKind) MakeVDLTarget() vdl.Target
pkg vom, method (*ControlKind) Set(string) error
diff --git a/vom/new_decoder_test.go b/vom/new_decoder_test.go
index 89e6e28..8fb1e2f 100644
--- a/vom/new_decoder_test.go
+++ b/vom/new_decoder_test.go
@@ -7,7 +7,9 @@
package vom
import (
+ "fmt"
"reflect"
+ "strings"
"testing"
"v.io/v23/vdl"
@@ -31,23 +33,141 @@
vdlValue := vdl.ValueOf(test.Value)
name := test.Name + " [vdl.Value]"
- testDecodeVDL(t, name, binversion+bintype+binvalue, vdlValue)
+ testDecodeVDLNew(t, name, binversion+bintype+binvalue, vdlValue)
name = test.Name + " [vdl.Value] (with TypeDecoder)"
- testDecodeVDLWithTypeDecoder(t, name, binversion, bintype, binvalue, vdlValue)
+ testDecodeVDLWithTypeDecoderNew(t, name, binversion, bintype, binvalue, vdlValue)
name = test.Name + " [vdl.Any]"
- testDecodeVDL(t, name, binversion+bintype+binvalue, vdl.AnyValue(vdlValue))
+ testDecodeVDLNew(t, name, binversion+bintype+binvalue, vdl.AnyValue(vdlValue))
name = test.Name + " [vdl.Any] (with TypeDecoder)"
- testDecodeVDLWithTypeDecoder(t, name, binversion, bintype, binvalue, vdl.AnyValue(vdlValue))
+ testDecodeVDLWithTypeDecoderNew(t, name, binversion, bintype, binvalue, vdl.AnyValue(vdlValue))
// Convert in
name = test.Name + " [go value]"
- testDecodeGo(t, name, binversion+bintype+binvalue, reflect.TypeOf(test.Value), test.Value)
+ testDecodeGoNew(t, name, binversion+bintype+binvalue, reflect.TypeOf(test.Value), test.Value)
name = test.Name + " [go value] (with TypeDecoder)"
- testDecodeGoWithTypeDecoder(t, name, binversion, bintype, binvalue, reflect.TypeOf(test.Value), test.Value)
+ testDecodeGoWithTypeDecoderNew(t, name, binversion, bintype, binvalue, reflect.TypeOf(test.Value), test.Value)
name = test.Name + " [go interface]"
- testDecodeGo(t, name, binversion+bintype+binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), test.Value)
+ testDecodeGoNew(t, name, binversion+bintype+binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), test.Value)
name = test.Name + " [go interface] (with TypeDecoder)"
- testDecodeGoWithTypeDecoder(t, name, binversion, bintype, binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), test.Value)
+ testDecodeGoWithTypeDecoderNew(t, name, binversion, bintype, binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), test.Value)
+ }
+}
+
+func testDecodeVDLNew(t *testing.T, name, bin string, value *vdl.Value) {
+ for _, mode := range allReadModes {
+ head := fmt.Sprintf("%s (%s)", name, mode)
+ decoder := NewXDecoder(mode.testReader(strings.NewReader(bin)))
+ if value == nil {
+ value = vdl.ZeroValue(vdl.AnyType)
+ }
+ got := vdl.ZeroValue(value.Type())
+ if err := decoder.Decode(got); err != nil {
+ t.Errorf("%s: Decode failed: %v", head, err)
+ return
+ }
+ if want := value; !vdl.EqualValue(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
+ return
+ }
+ }
+ // Test single-shot vom.Decode twice, to ensure we test the cache hit case.
+ testDecodeVDLSingleShotNew(t, name, bin, value)
+ testDecodeVDLSingleShotNew(t, name, bin, value)
+}
+
+func testDecodeVDLSingleShotNew(t *testing.T, name, bin string, value *vdl.Value) {
+ // Test the single-shot vom.Decode.
+ head := fmt.Sprintf("%s (single-shot)", name)
+ got := vdl.ZeroValue(value.Type())
+ if err := XDecode([]byte(bin), got); err != nil {
+ t.Errorf("%s: Decode failed: %v", head, err)
+ return
+ }
+ if want := value; !vdl.EqualValue(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
+ return
+ }
+}
+
+func testDecodeVDLWithTypeDecoderNew(t *testing.T, name, binversion, bintype, binvalue string, value *vdl.Value) {
+ for _, mode := range allReadModes {
+ head := fmt.Sprintf("%s (%s)", name, mode)
+ typedec := NewTypeDecoder(mode.testReader(strings.NewReader(binversion + bintype)))
+ typedec.Start()
+ decoder := NewXDecoderWithTypeDecoder(mode.testReader(strings.NewReader(binversion+binvalue)), typedec)
+ if value == nil {
+ value = vdl.ZeroValue(vdl.AnyType)
+ }
+ got := vdl.ZeroValue(value.Type())
+ if err := decoder.Decode(got); err != nil {
+ t.Errorf("%s: Decode failed: %v", head, err)
+ return
+ }
+ if want := value; !vdl.EqualValue(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
+ return
+ }
+ typedec.Stop()
+ }
+}
+
+func testDecodeGoNew(t *testing.T, name, bin string, rt reflect.Type, want interface{}) {
+ for _, mode := range allReadModes {
+ head := fmt.Sprintf("%s (%s)", name, mode)
+ decoder := NewXDecoder(mode.testReader(strings.NewReader(bin)))
+ var got interface{}
+ if rt != nil {
+ got = reflect.New(rt).Elem().Interface()
+ }
+ if err := decoder.Decode(&got); err != nil {
+ t.Errorf("%s: Decode failed: %v", head, err)
+ return
+ }
+ if !vdl.DeepEqual(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %T %+v\nWANT %T %+v", head, got, got, want, want)
+ return
+ }
+ }
+ // Test single-shot vom.Decode twice, to ensure we test the cache hit case.
+ testDecodeGoSingleShotNew(t, name, bin, rt, want)
+ testDecodeGoSingleShotNew(t, name, bin, rt, want)
+}
+
+func testDecodeGoSingleShotNew(t *testing.T, name, bin string, rt reflect.Type, want interface{}) {
+ head := fmt.Sprintf("%s (single-shot)", name)
+ var got interface{}
+ if rt != nil {
+ got = reflect.New(rt).Elem().Interface()
+ }
+ if err := XDecode([]byte(bin), &got); err != nil {
+ t.Errorf("%s: Decode failed: %v", head, err)
+ return
+ }
+ if !vdl.DeepEqual(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %T %+v\nWANT %T %+v", head, got, got, want, want)
+ return
+ }
+}
+
+func testDecodeGoWithTypeDecoderNew(t *testing.T, name, binversion, bintype, binvalue string, rt reflect.Type, want interface{}) {
+ for _, mode := range allReadModes {
+ head := fmt.Sprintf("%s (%s)", name, mode)
+ typedec := NewTypeDecoder(mode.testReader(strings.NewReader(binversion + bintype)))
+ typedec.Start()
+ decoder := NewXDecoderWithTypeDecoder(mode.testReader(strings.NewReader(binversion+binvalue)), typedec)
+ var got interface{}
+ if rt != nil {
+ got = reflect.New(rt).Elem().Interface()
+ }
+ if err := decoder.Decode(&got); err != nil {
+ t.Errorf("%s: Decode failed: %v", head, err)
+ return
+ }
+ if !vdl.DeepEqual(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %T %+v\nWANT %T %+v", head, got, got, want, want)
+ return
+ }
+ typedec.Stop()
}
}
diff --git a/vom/new_encoder_test.go b/vom/new_encoder_test.go
deleted file mode 100644
index 5e95227..0000000
--- a/vom/new_encoder_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build newvdltests
-
-package vom
-
-import (
- "fmt"
- "testing"
- "v.io/v23/vdl"
- "v.io/v23/vom/vomtest"
-)
-
-func TestEncoderNew(t *testing.T) {
- for _, test := range vomtest.Data() {
- version := Version(test.Version)
- hexVersion := fmt.Sprintf("%x", test.Version)
- vdlValue := vdl.ValueOf(test.Value)
- name := test.Name + " [vdl.Value]"
- testEncode(t, version, name, vdlValue, hexVersion+test.HexType+test.HexValue)
- name = test.Name + " [vdl.Value] (with TypeEncoder)"
- testEncodeWithTypeEncoder(t, version, name, vdlValue, hexVersion, test.HexType, test.HexValue)
-
- name = test.Name + " [go value]"
- testEncode(t, version, name, test.Value, hexVersion+test.HexType+test.HexValue)
- name = test.Name + " [go value] (with TypeEncoder)"
- testEncodeWithTypeEncoder(t, version, name, test.Value, hexVersion, test.HexType, test.HexValue)
- }
-}
diff --git a/vom/single_shot.go b/vom/single_shot.go
index f3a1a08..090b960 100644
--- a/vom/single_shot.go
+++ b/vom/single_shot.go
@@ -30,6 +30,14 @@
return buf.Bytes(), nil
}
+func VersionedXEncode(version Version, v interface{}) ([]byte, error) {
+ var buf bytes.Buffer
+ if err := NewVersionedXEncoder(version, &buf).Encode(v); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
// Decode reads the value from the given data, and stores it in value v. The
// semantics of value decoding are described by Decoder.Decode.
//
@@ -76,6 +84,51 @@
return nil
}
+func XDecode(data []byte, v interface{}) error {
+ // The implementation below corresponds (logically) to the following:
+ // return NewDecoder(bytes.NewReader(data)).Decode(valptr)
+ //
+ // However decoding type messages is expensive, so we cache typeDecoders to
+ // skip the decoding in the common case.
+ key, err := computeTypeDecoderCacheKey(data)
+ if err != nil {
+ return err
+ }
+ var buf *decbuf
+ typeDec := singleShotTypeDecoderCache.lookup(key)
+ cacheMiss := false
+ if typeDec == nil {
+ // Cache miss; start decoding at the beginning of all type messages with a
+ // new TypeDecoder.
+ cacheMiss = true
+ buf = newDecbufFromBytes(data)
+ typeDec = newTypeDecoderInternal(buf)
+ } else {
+ version := Version(data[0])
+ data = data[len(key):] // skip the already-read types
+ buf = newDecbufFromBytes(data)
+ buf.version = version
+ typeDec = newDerivedTypeDecoderInternal(buf, typeDec)
+ }
+ // Decode the value message.
+ decoder := &XDecoder{
+ xDecoder{
+ old: &Decoder{
+ buf: buf,
+ typeDec: typeDec,
+ },
+ },
+ }
+ if err := decoder.Decode(v); err != nil {
+ return err
+ }
+ // Populate the typeDecoder cache for future re-use.
+ if cacheMiss {
+ singleShotTypeDecoderCache.insert(key, typeDec)
+ }
+ return nil
+}
+
// singleShotTypeDecoderCache is a global cache of TypeDecoders keyed by the
// bytes of the sequence of type messages before the value message. A sequence
// of type messages that is byte-equal doesn't guarantee the same types in the
diff --git a/vom/xencoder.go b/vom/xencoder.go
index f3a6b86..c936132 100644
--- a/vom/xencoder.go
+++ b/vom/xencoder.go
@@ -144,7 +144,7 @@
nextValueIsAny := e.top().nextValueIsAny()
var anyRef anyStartRef
- if nextValueIsAny {
+ if nextValueIsAny && tt.Kind() == vdl.Optional {
tid, err := e.typeEnc.encode(tt)
if err != nil {
return err
@@ -154,7 +154,7 @@
binaryEncodeUint(e.buf, uint64(anyRef.index))
}
binaryEncodeControl(e.buf, WireCtrlNil)
- if nextValueIsAny {
+ if nextValueIsAny && tt.Kind() == vdl.Optional {
e.anyLens.FinishAny(anyRef, e.buf.Len())
}
diff --git a/vom/xencoder_test.go b/vom/xencoder_test.go
new file mode 100644
index 0000000..a1a721f
--- /dev/null
+++ b/vom/xencoder_test.go
@@ -0,0 +1,90 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build newvdltests
+
+package vom
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "v.io/v23/vdl"
+ "v.io/v23/vom/vomtest"
+)
+
+func TestXEncoder(t *testing.T) {
+ for _, test := range vomtest.Data() {
+ version := Version(test.Version)
+ hexVersion := fmt.Sprintf("%x", test.Version)
+ vdlValue := vdl.ValueOf(test.Value)
+ name := test.Name + " [vdl.Value]"
+ testXEncode(t, version, name, vdlValue, hexVersion+test.HexType+test.HexValue)
+ name = test.Name + " [vdl.Value] (with TypeEncoder)"
+ testXEncodeWithTypeEncoder(t, version, name, vdlValue, hexVersion, test.HexType, test.HexValue)
+
+ name = test.Name + " [go value]"
+ testXEncode(t, version, name, test.Value, hexVersion+test.HexType+test.HexValue)
+ name = test.Name + " [go value] (with TypeEncoder)"
+ testXEncodeWithTypeEncoder(t, version, name, test.Value, hexVersion, test.HexType, test.HexValue)
+ }
+}
+
+func testXEncode(t *testing.T, version Version, name string, value interface{}, hex string) {
+ for _, singleShot := range []bool{false, true} {
+ var bin []byte
+ if !singleShot {
+ var buf bytes.Buffer
+ encoder := NewVersionedXEncoder(version, &buf)
+ if err := encoder.Encode(value); err != nil {
+ t.Errorf("%s: Encode(%#v) failed: %v", name, value, err)
+ return
+ }
+ bin = buf.Bytes()
+ } else {
+ name += " (single-shot)"
+ var err error
+ bin, err = VersionedXEncode(version, value)
+ if err != nil {
+ t.Errorf("%s: Encode(%#v) failed: %v", name, value, err)
+ return
+ }
+ }
+ got, want := fmt.Sprintf("%x", bin), hex
+ match, err := matchHexPat(got, want)
+ if err != nil {
+ t.Error(err)
+ }
+ if !match {
+ t.Errorf("%s: Encode(%#v)\n GOT %s\nWANT %s", name, value, got, want)
+ }
+ }
+}
+
+func testXEncodeWithTypeEncoder(t *testing.T, version Version, name string, value interface{}, hexversion, hextype, hexvalue string) {
+ var buf, typebuf bytes.Buffer
+ typeenc := NewVersionedTypeEncoder(version, &typebuf)
+ encoder := NewVersionedXEncoderWithTypeEncoder(version, &buf, typeenc)
+ if err := encoder.Encode(value); err != nil {
+ t.Errorf("%s: Encode(%#v) failed: %v", name, value, err)
+ return
+ }
+ got, want := fmt.Sprintf("%x", typebuf.Bytes()), hexversion+hextype
+ match, err := matchHexPat(got, want)
+ if err != nil {
+ t.Error(err)
+ }
+ if !match && len(hextype) > 0 {
+ t.Errorf("%s: EncodeType(%#v)\nGOT %s\nWANT %s", name, value, got, want)
+ }
+ got, want = fmt.Sprintf("%x", buf.Bytes()), hexversion+hexvalue
+ match, err = matchHexPat(got, want)
+ if err != nil {
+ t.Error(err)
+ }
+ if !match {
+ t.Errorf("%s: Encode(%#v)\nGOT %s\nWANT %s", name, value, got, want)
+ }
+}