vdl: Add codegen for Any, and fix some VDLRead bugs.
After this CL, if you manually replace vom.Decoder with
vom.XDecoder, all tests pass, except for a single failure
involving a nil vom.RawBytes.
MultiPart: 1/2
Change-Id: I6d96d7e6f655e859dde9dc2189f621fe6f125866
diff --git a/security/security.vdl.go b/security/security.vdl.go
index 956284a..7c3deb2 100644
--- a/security/security.vdl.go
+++ b/security/security.vdl.go
@@ -1729,7 +1729,9 @@
return dec.FinishValue()
}
var elem *vom.RawBytes
- // TODO(toddw): implement any
+ if err = elem.VDLRead(dec); err != nil {
+ return err
+ }
*x = append(*x, elem)
}
}
diff --git a/services/syncbase/syncbase.vdl.go b/services/syncbase/syncbase.vdl.go
index 52eb615..5285937 100644
--- a/services/syncbase/syncbase.vdl.go
+++ b/services/syncbase/syncbase.vdl.go
@@ -5098,7 +5098,9 @@
case "":
return dec.FinishValue()
case "Value":
- // TODO(toddw): implement any
+ if err = x.Value.VDLRead(dec); err != nil {
+ return err
+ }
case "FromSync":
if err = dec.StartValue(); err != nil {
return err
diff --git a/services/watch/watch.vdl.go b/services/watch/watch.vdl.go
index a53a3cf..901d0d3 100644
--- a/services/watch/watch.vdl.go
+++ b/services/watch/watch.vdl.go
@@ -624,7 +624,9 @@
return err
}
case "Value":
- // TODO(toddw): implement any
+ if err = x.Value.VDLRead(dec); err != nil {
+ return err
+ }
case "ResumeMarker":
if err = x.ResumeMarker.VDLRead(dec); err != nil {
return err
diff --git a/vdl/.api b/vdl/.api
index 4b4d25f..c45abe2 100644
--- a/vdl/.api
+++ b/vdl/.api
@@ -34,6 +34,7 @@
pkg vdl, func Compatible(*Type, *Type) bool
pkg vdl, func Convert(interface{}, interface{}) error
pkg vdl, func CopyValue(*Value) *Value
+pkg vdl, func DeepEqual(interface{}, interface{}) bool
pkg vdl, func EnumType(...string) *Type
pkg vdl, func EqualValue(*Value, *Value) bool
pkg vdl, func Float32Value(float32) *Value
@@ -446,6 +447,7 @@
pkg vdl, method (*Value) TypeObject() *Type
pkg vdl, method (*Value) Uint() uint64
pkg vdl, method (*Value) UnionField() (int, *Value)
+pkg vdl, method (*Value) VDLEqual(interface{}) bool
pkg vdl, method (*Value) VDLRead(Decoder) error
pkg vdl, method (*WireError) FillVDLTarget(Target, *Type) error
pkg vdl, method (*WireError) MakeVDLTarget() Target
@@ -540,6 +542,8 @@
pkg vdl, type Encoder interface, FinishValue() error
pkg vdl, type Encoder interface, StartComposite() error
pkg vdl, type Encoder interface, StartValue(*Type) error
+pkg vdl, type Equaler interface { VDLEqual }
+pkg vdl, type Equaler interface, VDLEqual(interface{}) bool
pkg vdl, type Field struct
pkg vdl, type Field struct, Name string
pkg vdl, type Field struct, Type *Type
diff --git a/vdl/coder.go b/vdl/coder.go
index 347b324..b6749e5 100644
--- a/vdl/coder.go
+++ b/vdl/coder.go
@@ -8,9 +8,9 @@
// Reader is the interface that wraps the VDLRead method.
//
-// VDLRead fills in the the underlying value (that implements this method) from
-// the Decoder. This method is auto-generated for all types defined in vdl. It
-// may be implemented for regular Go types not defined in vdl, to customize the
+// VDLRead fills in the the receiver that implements this method from the
+// Decoder. This method is auto-generated for all types defined in vdl. It may
+// be implemented for regular Go types not defined in vdl, to customize the
// decoding.
type Reader interface {
VDLRead(dec Decoder) error
@@ -18,9 +18,9 @@
// Writer is the interface that wraps the VDLWrite method.
//
-// VDLWrite writes out the underlying value (that implements this method) to the
-// Encoder. This method is auto-generated for all types defined in vdl. It may
-// be implemented for regular Go types not defined in vdl, to customize the
+// VDLWrite writes out the receiver that implements this method to the Encoder.
+// This method is auto-generated for all types defined in vdl. It may be
+// implemented for regular Go types not defined in vdl, to customize the
// encoding.
type Writer interface {
VDLWrite(enc Encoder) error
@@ -125,7 +125,7 @@
func decoderCompatible(dec Decoder, tt *Type) error {
if (dec.StackDepth() == 1 || dec.IsAny()) && !Compatible(tt, dec.Type()) {
- return fmt.Errorf("incompatible %v %v, from %v", tt.Kind(), tt, dec.Type())
+ return fmt.Errorf("incompatible %v, from %v", tt, dec.Type())
}
return nil
}
diff --git a/vdl/deep_equal.go b/vdl/deep_equal.go
new file mode 100644
index 0000000..ad748e0
--- /dev/null
+++ b/vdl/deep_equal.go
@@ -0,0 +1,188 @@
+// Copyright 2016 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.
+
+package vdl
+
+import (
+ "fmt"
+ "reflect"
+ "unsafe"
+)
+
+// TODO(toddw): Add tests.
+
+// Equaler is the interface that wraps the VDLEqual method.
+//
+// VDLEqual returns true iff the receiver that implements this method is equal
+// to v. The semantics of the equality must abide by VDL equality rules. The
+// caller of this method must ensure that the type of the receiver is the same
+// as the type of v, and v is never nil.
+type Equaler interface {
+ VDLEqual(v interface{}) bool
+}
+
+// DeepEqual is like reflect.DeepEqual, with the following differences:
+// 1. If a value is encountered that implements Equaler, we will use that for
+// the comparison.
+// 2. If cyclic values are encountered, we require that the cyclic structure
+// of the two values is the same.
+func DeepEqual(a, b interface{}) bool {
+ return deepEqual(reflect.ValueOf(a), reflect.ValueOf(b), nil, nil)
+}
+
+func findPathIndex(path []unsafe.Pointer, target unsafe.Pointer) int {
+ for index, item := range path {
+ if item == target {
+ return index
+ }
+ }
+ return -1
+}
+
+func deepEqual(a, b reflect.Value, pathA, pathB []unsafe.Pointer) bool {
+ if !a.IsValid() || !b.IsValid() {
+ return a.IsValid() == b.IsValid()
+ }
+ if a.Type() != b.Type() {
+ return false
+ }
+
+ // Handle VDLEqual comparisons.
+ if a.Kind() != reflect.Ptr || (!a.IsNil() && !b.IsNil()) {
+ // It would be nice to use a.Interface() to get the actual value, and then
+ // call the VDLEqual method directly. But a.Interface() panics if a is an
+ // unexported struct field. We might actually encounter this case, if we
+ // change our codegen to include an unexported "unknown bytes" field in
+ // structs, in order to avoid read-modify-write slicing.
+ //
+ // TODO(toddw): Verify the logic below actually allows us to find and call
+ // the VDLEqual method, if a is an unexported struct field.
+ if rvEqual := a.MethodByName("VDLEqual"); rvEqual.IsValid() {
+ return rvEqual.Call([]reflect.Value{b})[0].Bool()
+ }
+ }
+
+ // In order to handle cyclic values, we keep the path of possible "pointees"
+ // as we traverse the value, where the "pointee" is the address that a pointer
+ // could point to. The pointer handling case below uses this information to
+ // detect and handle cycles.
+ //
+ // We must convert the result of reflect.Value.UnsafeAddr() to unsafe.Pointer
+ // in the same expression. See https://golang.org/pkg/unsafe/#Pointer
+ switch canA, canB := a.CanAddr(), b.CanAddr(); {
+ case canA && canB:
+ pathA = append(pathA, unsafe.Pointer(a.UnsafeAddr()))
+ pathB = append(pathB, unsafe.Pointer(b.UnsafeAddr()))
+ case canA:
+ pathA = append(pathA, unsafe.Pointer(a.UnsafeAddr()))
+ pathB = append(pathB, unsafe.Pointer(uintptr(0)))
+ case canB:
+ pathA = append(pathA, unsafe.Pointer(uintptr(0)))
+ pathB = append(pathB, unsafe.Pointer(b.UnsafeAddr()))
+ }
+
+ switch a.Kind() {
+ case reflect.Ptr:
+ if a.IsNil() || b.IsNil() {
+ return a.IsNil() == b.IsNil()
+ }
+ // We must convert the result of reflect.Value.Pointer() to unsafe.Pointer
+ // in the same expression. See https://golang.org/pkg/unsafe/#Pointer
+ pa, pb := unsafe.Pointer(a.Pointer()), unsafe.Pointer(b.Pointer())
+ if pa == pb {
+ // If the pointers are equal, the values are equal.
+ return true
+ }
+ switch indexA, indexB := findPathIndex(pathA, pa), findPathIndex(pathB, pb); {
+ case indexA != indexB:
+ // The index is -1 if the pointer doesn't exist in the path, meaning this
+ // isn't a cyclic value. Otherwise the index tells us the which item the
+ // cycle points back to. Either way, if they are different, the values
+ // are not equal.
+ return false
+ case indexA != -1:
+ // If both values have cycles pointing back to the same relative item, we
+ // need to stop, otherwise there is an infinite loop. All previous items
+ // in the path were equal, so we return true.
+ return true
+ }
+ return deepEqual(a.Elem(), b.Elem(), pathA, pathB)
+ case reflect.Array:
+ if a.Len() != b.Len() {
+ return false
+ }
+ for ix := 0; ix < a.Len(); ix++ {
+ if !deepEqual(a.Index(ix), b.Index(ix), pathA, pathB) {
+ return false
+ }
+ }
+ return true
+ case reflect.Slice:
+ if a.IsNil() || b.IsNil() {
+ return a.IsNil() == b.IsNil()
+ }
+ if a.Len() != b.Len() {
+ return false
+ }
+ for ix := 0; ix < a.Len(); ix++ {
+ if !deepEqual(a.Index(ix), b.Index(ix), pathA, pathB) {
+ return false
+ }
+ }
+ return true
+ case reflect.Map:
+ if a.IsNil() || b.IsNil() {
+ return a.IsNil() == b.IsNil()
+ }
+ if a.Len() != b.Len() {
+ return false
+ }
+ for _, key := range a.MapKeys() {
+ if !deepEqual(a.MapIndex(key), b.MapIndex(key), pathA, pathB) {
+ return false
+ }
+ }
+ return true
+ case reflect.Struct:
+ for ix := 0; ix < a.NumField(); ix++ {
+ if !deepEqual(a.Field(ix), b.Field(ix), pathA, pathB) {
+ return false
+ }
+ }
+ return true
+ case reflect.Interface:
+ if a.IsNil() || b.IsNil() {
+ return a.IsNil() == b.IsNil()
+ }
+ return deepEqual(a.Elem(), b.Elem(), pathA, pathB)
+
+ // Ideally we would add a default clause here that would just return
+ // a.Interface() == b.Interface(), but that panics if we're dealing with
+ // unexported fields. Instead we check each case manually.
+
+ case reflect.Bool:
+ return a.Bool() == b.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return a.Int() == b.Int()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return a.Uint() == b.Uint()
+ case reflect.Float32, reflect.Float64:
+ return a.Float() == b.Float()
+ case reflect.Complex64, reflect.Complex128:
+ return a.Complex() == b.Complex()
+ case reflect.String:
+ return a.String() == b.String()
+ case reflect.UnsafePointer:
+ return a.Pointer() == b.Pointer()
+ case reflect.Func:
+ // Same as regular Go comparisons; non-nil functions can't be compared.
+ return a.IsNil() && b.IsNil()
+ case reflect.Chan:
+ // We must convert the result of reflect.Value.Pointer() to unsafe.Pointer
+ // in the same expression. See https://golang.org/pkg/unsafe/#Pointer
+ return unsafe.Pointer(a.Pointer()) == unsafe.Pointer(b.Pointer())
+ default:
+ panic(fmt.Errorf("DeepEqual unhandled kind %v type %q", a.Kind(), a.Type()))
+ }
+}
diff --git a/vdl/reflect_reader.go b/vdl/reflect_reader.go
index 8260e6b..749bad5 100644
--- a/vdl/reflect_reader.go
+++ b/vdl/reflect_reader.go
@@ -183,7 +183,7 @@
// Walk pointers and check for faster non-reflect support, which handles
// vdl.Value and vom.RawBytes, and any other special-cases.
rv = readWalkPointers(rv)
- if err := readNonReflect(dec, true, rv.Addr().Interface()); err != errReadMustReflect {
+ if err := readNonReflect(dec, false, rv.Addr().Interface()); err != errReadMustReflect {
return err
}
// The only case left is to handle interfaces. We allow decoding into
@@ -206,10 +206,16 @@
// TODO(toddw): Replace typeToReflectFixed with TypeToReflect, after we've
// fixed it to treat the error type correctly.
rtDecode := typeToReflectFixed(dec.Type())
- switch {
- case rtDecode == nil:
+ if rtDecode == nil {
return fmt.Errorf("vdl: %v not registered, call vdl.Register, or use vdl.Value or vom.RawBytes instead", dec.Type())
- case !rtDecode.Implements(rv.Type()):
+ }
+ // If we decoded an optional type, ensure that it is a pointer. Note that if
+ // we decoded a nil, dec.Type() is already optional, so rtDecode will already
+ // be a pointer.
+ if dec.IsOptional() && !dec.IsNil() {
+ rtDecode = reflect.PtrTo(rtDecode)
+ }
+ if !rtDecode.Implements(rv.Type()) {
return fmt.Errorf("vdl: %v doesn't implement %v", rtDecode, rv.Type())
}
// Handle decoding optional(nil), by setting rv to a nil pointer of the
@@ -377,7 +383,7 @@
if err := readReflect(dec, false, elem, tt.Elem()); err != nil {
return err
}
- rv = reflect.Append(rv, elem)
+ rv.Set(reflect.Append(rv, elem))
}
}
@@ -433,11 +439,8 @@
func readStruct(dec Decoder, rv reflect.Value, tt *Type) error {
rt := rv.Type()
- // Reset to zero struct, since fields may be missing.
- //
- // TODO(toddw): We need something like rvSettableZeroValue here, unless we can
- // ensure go zero values represent VDL zero values as well.
- rv.Set(reflect.Zero(rt))
+ // Reset to the zero struct, since fields may be missing.
+ rv.Set(rvSettableZeroValue(rt, tt))
for {
name, err := dec.NextField()
switch {
diff --git a/vdl/value.go b/vdl/value.go
index 49415d5..0434f60 100644
--- a/vdl/value.go
+++ b/vdl/value.go
@@ -331,6 +331,11 @@
// Type returns the type of v. All valid values have a non-nil type.
func (v *Value) Type() *Type { return v.t }
+// VDLEqual implements the Equaler interface method.
+func (v *Value) VDLEqual(x interface{}) bool {
+ return EqualValue(v, x.(*Value))
+}
+
// Bool returns the underlying value of a Bool.
func (v *Value) Bool() bool {
v.t.checkKind("Bool", Bool)
diff --git a/vdl/value_reader.go b/vdl/value_reader.go
index e6a98f6..251927b 100644
--- a/vdl/value_reader.go
+++ b/vdl/value_reader.go
@@ -8,7 +8,7 @@
"fmt"
)
-// VDLRead uses dec to decode a value in vv. If vv isn't valid (i.e. has no
+// VDLRead uses dec to decode a value into vv. If vv isn't valid (i.e. has no
// type), it will be filled in the exact type of value read from the decoder.
// Otherwise the type of vv must be compatible with the type of the value read
// from the decoder.
@@ -21,16 +21,23 @@
}
if !vv.IsValid() {
// Initialize vv to the zero value of the exact type read from the decoder.
- // It is as if vv were initialized to that type to begin with.
+ // It is as if vv were initialized with that type to begin with. The
+ // top-level any type is dropped.
switch {
- case dec.IsAny():
- *vv = *ZeroValue(AnyType)
- case dec.IsOptional():
+ case dec.IsOptional() && !dec.IsNil():
*vv = *ZeroValue(OptionalType(dec.Type()))
default:
*vv = *ZeroValue(dec.Type())
}
}
+ dec.IgnoreNextStartValue()
+ return vv.read(dec)
+}
+
+func (vv *Value) read(dec Decoder) error {
+ if err := dec.StartValue(); err != nil {
+ return err
+ }
if err := decoderCompatible(dec, vv.Type()); err != nil {
return err
}
@@ -177,7 +184,7 @@
case done:
return nil
}
- if err := vv.Index(index).VDLRead(dec); err != nil {
+ if err := vv.Index(index).read(dec); err != nil {
return err
}
index++
@@ -218,7 +225,7 @@
vv.AssignLen(cap)
vv.AssignLen(needLen)
}
- if err := vv.Index(index).VDLRead(dec); err != nil {
+ if err := vv.Index(index).read(dec); err != nil {
return err
}
index++
@@ -237,7 +244,7 @@
return nil
}
key := ZeroValue(vv.Type().Key())
- if err := key.VDLRead(dec); err != nil {
+ if err := key.read(dec); err != nil {
return err
}
vv.AssignSetKey(key)
@@ -256,11 +263,11 @@
return nil
}
key := ZeroValue(vv.Type().Key())
- if err := key.VDLRead(dec); err != nil {
+ if err := key.read(dec); err != nil {
return err
}
elem := ZeroValue(vv.Type().Elem())
- if err := elem.VDLRead(dec); err != nil {
+ if err := elem.read(dec); err != nil {
return err
}
vv.AssignMapIndex(key, elem)
@@ -283,7 +290,7 @@
}
switch field := vv.StructFieldByName(name); {
case field != nil:
- if err := field.VDLRead(dec); err != nil {
+ if err := field.read(dec); err != nil {
return err
}
default:
@@ -310,7 +317,7 @@
return fmt.Errorf("field %q not in union %v, from %v", name, vv.Type(), dec.Type())
}
elem := ZeroValue(field.Type)
- if err := elem.VDLRead(dec); err != nil {
+ if err := elem.read(dec); err != nil {
return err
}
vv.AssignUnionField(index, elem)
diff --git a/vdl/vdl.vdl.go b/vdl/vdl.vdl.go
index c4b7a58..c6f118e 100644
--- a/vdl/vdl.vdl.go
+++ b/vdl/vdl.vdl.go
@@ -432,7 +432,10 @@
return dec.FinishValue()
}
var elem *Value
- // TODO(toddw): implement any
+ elem = new(Value)
+ if err = elem.VDLRead(dec); err != nil {
+ return err
+ }
*x = append(*x, elem)
}
}
diff --git a/vdlroot/signature/signature.vdl.go b/vdlroot/signature/signature.vdl.go
index 76892ba..d74f682 100644
--- a/vdlroot/signature/signature.vdl.go
+++ b/vdlroot/signature/signature.vdl.go
@@ -977,7 +977,10 @@
return dec.FinishValue()
}
var elem *vdl.Value
- // TODO(toddw): implement any
+ elem = new(vdl.Value)
+ if err = elem.VDLRead(dec); err != nil {
+ return err
+ }
*x = append(*x, elem)
}
}
diff --git a/vom/.api b/vom/.api
index f67ad3d..6350bb2 100644
--- a/vom/.api
+++ b/vom/.api
@@ -101,6 +101,7 @@
pkg vom, method (*TypeDecoder) Stop()
pkg vom, method (*XDecoder) Decode(interface{}) error
pkg vom, method (*XDecoder) Decoder() vdl.Decoder
+pkg vom, method (*XDecoder) Ignore() error
pkg vom, method (ControlKind) String() string
pkg vom, method (DumpAtom) String() string
pkg vom, method (DumpKind) String() string
diff --git a/vom/decoder_test.go b/vom/decoder_test.go
index 2f4c237..b488c54 100644
--- a/vom/decoder_test.go
+++ b/vom/decoder_test.go
@@ -81,7 +81,7 @@
return
}
if want := value; !vdl.EqualValue(got, want) {
- t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
+ t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
return
}
}
@@ -99,7 +99,7 @@
return
}
if want := value; !vdl.EqualValue(got, want) {
- t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
+ t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
return
}
}
@@ -119,7 +119,7 @@
return
}
if want := value; !vdl.EqualValue(got, want) {
- t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
+ t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
return
}
typedec.Stop()
@@ -138,8 +138,8 @@
t.Errorf("%s: Decode failed: %v", head, err)
return
}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
+ if !vdl.DeepEqual(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %T %+v\nWANT %T %+v", head, got, got, want, want)
return
}
}
@@ -158,8 +158,8 @@
t.Errorf("%s: Decode failed: %v", head, err)
return
}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
+ if !vdl.DeepEqual(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %T %+v\nWANT %T %+v", head, got, got, want, want)
return
}
}
@@ -178,8 +178,8 @@
t.Errorf("%s: Decode failed: %v", head, err)
return
}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
+ if !vdl.DeepEqual(got, want) {
+ t.Errorf("%s: Decode mismatch\nGOT %T %+v\nWANT %T %+v", head, got, got, want, want)
return
}
typedec.Stop()
@@ -272,7 +272,7 @@
defer wg.Done()
for _, n := range rand.Perm(len(tests) * 10) {
test := tests[n%len(tests)]
- name := fmt.Sprintf("[%d]:%#v,%#v", n, test.In, test.Want)
+ name := fmt.Sprintf("[%d]:%+v,%+v", n, test.In, test.Want)
var (
encoder *Encoder
@@ -288,7 +288,7 @@
}
if err := encoder.Encode(test.In); err != nil {
- t.Errorf("%s: Encode(%#v) failed: %v", name, test.In, err)
+ t.Errorf("%s: Encode(%+v) failed: %v", name, test.In, err)
return
}
rv := reflect.New(reflect.TypeOf(test.Want))
@@ -296,8 +296,8 @@
t.Errorf("%s: Decode failed: %v", name, err)
return
}
- if got := rv.Elem().Interface(); !reflect.DeepEqual(got, test.Want) {
- t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", name, got, got, test.Want, test.Want)
+ if got := rv.Elem().Interface(); !vdl.DeepEqual(got, test.Want) {
+ t.Errorf("%s: Decode mismatch\nGOT %T %+v\nWANT %T %+v", name, got, got, test.Want, test.Want)
return
}
}
@@ -377,9 +377,9 @@
// Test that no EOF is returned from Decode() if the type stream finished before the value stream.
func TestTypeStreamEndsFirst(t *testing.T) {
- hexversion := "80"
- hextype := "5137060029762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e537472756374416e7901010003416e79010fe1e1533502002d762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e4e41727261793255696e74363401060202e1"
- hexvalue := "5206002a000001e1"
+ hexversion := "81"
+ hextype := "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1533b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1"
+ hexvalue := "52012a0103070000000001e1e1"
binversion := string(hex2Bin(t, hexversion))
bintype := string(hex2Bin(t, hextype))
binvalue := string(hex2Bin(t, hexvalue))
diff --git a/vom/testdata/types/types.vdl.go b/vom/testdata/types/types.vdl.go
index 1840320..00c48f8 100644
--- a/vom/testdata/types/types.vdl.go
+++ b/vom/testdata/types/types.vdl.go
@@ -302,7 +302,10 @@
return err
}
case "Value":
- // TODO(toddw): implement any
+ x.Value = new(vdl.Value)
+ if err = x.Value.VDLRead(dec); err != nil {
+ return err
+ }
case "TypeString":
if err = dec.StartValue(); err != nil {
return err
@@ -632,7 +635,10 @@
return dec.FinishValue()
}
var elem *vdl.Value
- // TODO(toddw): implement any
+ elem = new(vdl.Value)
+ if err = elem.VDLRead(dec); err != nil {
+ return err
+ }
*x = append(*x, elem)
}
}
@@ -3548,7 +3554,10 @@
return err
}
case "F":
- // TODO(toddw): implement any
+ x.F = new(vdl.Value)
+ if err = x.F.VDLRead(dec); err != nil {
+ return err
+ }
default:
if err = dec.SkipValue(); err != nil {
return err
@@ -6369,7 +6378,10 @@
return err
}
case "D":
- // TODO(toddw): implement any
+ x.D = new(vdl.Value)
+ if err = x.D.VDLRead(dec); err != nil {
+ return err
+ }
case "E":
if err = dec.StartValue(); err != nil {
return err
@@ -7824,7 +7836,10 @@
}
var elem *vdl.Value
{
- // TODO(toddw): implement any
+ elem = new(vdl.Value)
+ if err = elem.VDLRead(dec); err != nil {
+ return err
+ }
}
if tmpMap == nil {
tmpMap = make(SometimesSetMap)
@@ -9104,7 +9119,10 @@
*x = field
case "D":
var field BdeUnionD
- // TODO(toddw): implement any
+ field.Value = new(vdl.Value)
+ if err = field.Value.VDLRead(dec); err != nil {
+ return err
+ }
*x = field
case "E":
var field BdeUnionE
@@ -9529,7 +9547,10 @@
case "":
return dec.FinishValue()
case "Any":
- // TODO(toddw): implement any
+ x.Any = new(vdl.Value)
+ if err = x.Any.VDLRead(dec); err != nil {
+ return err
+ }
default:
if err = dec.SkipValue(); err != nil {
return err
@@ -10776,7 +10797,10 @@
return dec.FinishValue()
}
var elem *vdl.Value
- // TODO(toddw): implement any
+ elem = new(vdl.Value)
+ if err = elem.VDLRead(dec); err != nil {
+ return err
+ }
*x = append(*x, elem)
}
}
@@ -11140,7 +11164,10 @@
case "":
return dec.FinishValue()
case "Payload":
- // TODO(toddw): implement any
+ x.Payload = new(vdl.Value)
+ if err = x.Payload.VDLRead(dec); err != nil {
+ return err
+ }
case "Next":
if err = dec.StartValue(); err != nil {
return err
diff --git a/vom/type_decoder.go b/vom/type_decoder.go
index 654f328..9a93979 100644
--- a/vom/type_decoder.go
+++ b/vom/type_decoder.go
@@ -35,7 +35,7 @@
buildCond *sync.Cond
err error // GUARDED_BY(buildMu)
idToWire map[typeId]wireType // GUARDED_BY(buildMu)
- dec *Decoder // GUARDED_BY(buildMu)
+ dec *xDecoder // GUARDED_BY(buildMu)
processingControlMu sync.Mutex
goroutineRunning bool // GUARDED_BY(processingControlMu)
@@ -52,10 +52,7 @@
td := &TypeDecoder{
idToType: make(map[typeId]*vdl.Type),
idToWire: make(map[typeId]wireType),
- dec: &Decoder{
- buf: buf,
- typeDec: nil,
- },
+ dec: &xDecoder{old: &Decoder{buf: buf}},
}
td.buildCond = sync.NewCond(&td.buildMu)
return td
@@ -65,10 +62,7 @@
td := &TypeDecoder{
idToType: orig.idToType,
idToWire: orig.idToWire,
- dec: &Decoder{
- buf: buf,
- typeDec: nil,
- },
+ dec: &xDecoder{old: &Decoder{buf: buf}},
}
td.buildCond = sync.NewCond(&td.buildMu)
return td
@@ -130,8 +124,8 @@
return err
}
- if !d.dec.typeIncomplete {
- if err := d.buildType(curTypeID); d.dec.buf.version >= Version81 && err != nil {
+ if !d.dec.old.typeIncomplete {
+ if err := d.buildType(curTypeID); d.dec.old.buf.version >= Version81 && err != nil {
return err
}
}
diff --git a/vom/xdecoder.go b/vom/xdecoder.go
index b302e45..c30b501 100644
--- a/vom/xdecoder.go
+++ b/vom/xdecoder.go
@@ -71,6 +71,10 @@
return vdl.Read(d.dec, v)
}
+func (d *XDecoder) Ignore() error {
+ return d.dec.old.Ignore()
+}
+
func (d *xDecoder) IgnoreNextStartValue() {
d.ignoreNextStartValue = true
}
@@ -79,9 +83,34 @@
return len(d.stack)
}
-// readRawBytes fills in rb with the next value. It can be called for both
+func (d *xDecoder) decodeWireType(wt *wireType) (typeId, error) {
+ // TODO(toddw): Flip useOldDecoder=false to enable XDecoder.
+ const useOldDecoder = true
+ if useOldDecoder {
+ return d.old.decodeWireType(wt)
+ }
+ // Type messages are just a regularly encoded wireType, which is a union. To
+ // decode we pre-populate the stack with an entry for the wire type, and run
+ // the code-generated vdlReadWireType method.
+ tid, err := d.old.nextMessage()
+ if err != nil {
+ return 0, err
+ }
+ d.stack = append(d.stack, decoderStackEntry{
+ Type: wireTypeType,
+ Index: -1,
+ LenHint: 1, // wireType is a union
+ })
+ d.ignoreNextStartValue = true
+ if err := vdlReadWireType(d, wt); err != nil {
+ return 0, err
+ }
+ return tid, nil
+}
+
+// readRawBytes fills in raw with the next value. It can be called for both
// top-level and internal values.
-func (d *xDecoder) readRawBytes(rb *RawBytes) error {
+func (d *xDecoder) readRawBytes(raw *RawBytes) error {
if d.ignoreNextStartValue {
// If the user has already called StartValue on the decoder, it's harder to
// capture all the raw bytes, since the optional flag and length hints have
@@ -99,7 +128,7 @@
if err != nil {
return err
}
- if err := d.old.decodeRaw(tt, anyLen, rb); err != nil {
+ if err := d.old.decodeRaw(tt, anyLen, raw); err != nil {
return err
}
return d.old.endMessage()
@@ -119,14 +148,14 @@
if ttElem == nil {
// This is a nil any value, which has already been read by readAnyHeader.
// We simply fill in RawBytes with the single WireCtrlNil byte.
- rb.Version = d.old.buf.version
- rb.Type = vdl.AnyType
- rb.RefTypes = nil
- rb.AnyLengths = nil
- rb.Data = []byte{WireCtrlNil}
+ raw.Version = d.old.buf.version
+ raw.Type = vdl.AnyType
+ raw.RefTypes = nil
+ raw.AnyLengths = nil
+ raw.Data = []byte{WireCtrlNil}
return nil
}
- return d.old.decodeRaw(ttElem, anyLen, rb)
+ return d.old.decodeRaw(ttElem, anyLen, raw)
}
func (d *xDecoder) StartValue() error {