Vom cleanups leftover from the transition to new coders.
This CL just moves and renames code and tests around into more
logical places. No functionality has changed.
Change-Id: I1b58a4367eae8d91fc44dba202b1a3039fa74403
diff --git a/vdl/convert_test.go b/vdl/convert_test.go
index 07bd8dc..e95d85c 100644
--- a/vdl/convert_test.go
+++ b/vdl/convert_test.go
@@ -17,6 +17,7 @@
"testing"
"v.io/v23/vdl"
+ "v.io/v23/vdl/vdltest"
"v.io/v23/verror"
)
@@ -1225,3 +1226,24 @@
t.Errorf("got %#v, want %#v", got, want)
}
}
+
+func TestConvertNew(t *testing.T) {
+ for _, entry := range vdltest.AllPass() {
+ rvTargetPtr := reflect.New(entry.Target.Type())
+ if err := vdl.Convert(rvTargetPtr.Interface(), entry.Source.Interface()); err != nil {
+ t.Errorf("%s: Convert failed: %v", entry.Name(), err)
+ }
+ if got, want := rvTargetPtr.Elem(), entry.Target; !vdl.DeepEqualReflect(got, want) {
+ t.Errorf("%s\nGOT %v\nWANT %v", entry.Name(), got, want)
+ }
+ }
+}
+
+func TestConvertFailNew(t *testing.T) {
+ for _, entry := range vdltest.AllFail() {
+ rvTargetPtr := reflect.New(entry.Target.Type())
+ if err := vdl.Convert(rvTargetPtr.Interface(), entry.Source.Interface()); err == nil {
+ t.Errorf("%s: Convert passed, wanted failure", entry.Name())
+ }
+ }
+}
diff --git a/vdl/new_convert_test.go b/vdl/new_convert_test.go
deleted file mode 100644
index 57573a5..0000000
--- a/vdl/new_convert_test.go
+++ /dev/null
@@ -1,36 +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 vdl_test
-
-import (
- "reflect"
- "testing"
-
- "v.io/v23/vdl"
- "v.io/v23/vdl/vdltest"
-)
-
-func TestConvertNew(t *testing.T) {
- for _, entry := range vdltest.AllPass() {
- rvTargetPtr := reflect.New(entry.Target.Type())
- if err := vdl.Convert(rvTargetPtr.Interface(), entry.Source.Interface()); err != nil {
- t.Errorf("%s: Convert failed: %v", entry.Name(), err)
- }
- if got, want := rvTargetPtr.Elem(), entry.Target; !vdl.DeepEqualReflect(got, want) {
- t.Errorf("%s\nGOT %v\nWANT %v", entry.Name(), got, want)
- }
- }
-}
-
-func TestConvertFailNew(t *testing.T) {
- for _, entry := range vdltest.AllFail() {
- rvTargetPtr := reflect.New(entry.Target.Type())
- if err := vdl.Convert(rvTargetPtr.Interface(), entry.Source.Interface()); err == nil {
- t.Errorf("%s: Convert passed, wanted failure", entry.Name())
- }
- }
-}
diff --git a/vdl/new_value_reader_test.go b/vdl/new_value_reader_test.go
deleted file mode 100644
index fed2366..0000000
--- a/vdl/new_value_reader_test.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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.
-
-// +build newvdltests
-
-package vdl_test
-
-import (
- "testing"
-
- "v.io/v23/vdl"
- "v.io/v23/vdl/vdltest"
-)
-
-func TestValueReader(t *testing.T) {
- for _, entry := range vdltest.ToEntryValues(vdltest.AllPass()) {
- out := vdl.ZeroValue(entry.Target.Type())
- if err := out.VDLRead(entry.Source.Decoder()); err != nil {
- t.Errorf("%s: VDLRead failed: %v", entry.Name(), err)
- continue
- }
- if got, want := out, entry.Target; !vdl.EqualValue(got, want) {
- t.Errorf("%s\nGOT %v\nWANT %v", entry.Name(), got, want)
- }
- }
-}
diff --git a/vdl/value_reader_test.go b/vdl/value_reader_test.go
index 03bee7d..d21926c 100644
--- a/vdl/value_reader_test.go
+++ b/vdl/value_reader_test.go
@@ -8,18 +8,18 @@
"testing"
"v.io/v23/vdl"
- "v.io/v23/vom/testdata/data81"
+ "v.io/v23/vdl/vdltest"
)
-func TestValueRead(t *testing.T) {
- for _, test := range data81.Tests {
- out := vdl.ZeroValue(test.Value.Type())
- if err := out.VDLRead(test.Value.Decoder()); err != nil {
- t.Errorf("%s: error in ValueRead: %v", test.Name, err)
+func TestValueReader(t *testing.T) {
+ for _, entry := range vdltest.ToEntryValues(vdltest.AllPass()) {
+ out := vdl.ZeroValue(entry.Target.Type())
+ if err := out.VDLRead(entry.Source.Decoder()); err != nil {
+ t.Errorf("%s: VDLRead failed: %v", entry.Name(), err)
continue
}
- if !vdl.EqualValue(test.Value, out) {
- t.Errorf("%s: got %v, want %v", test.Name, out, test.Value)
+ if got, want := out, entry.Target; !vdl.EqualValue(got, want) {
+ t.Errorf("%s\nGOT %v\nWANT %v", entry.Name(), got, want)
}
}
}
diff --git a/vom/.api b/vom/.api
index 30322e3..f7c4048 100644
--- a/vom/.api
+++ b/vom/.api
@@ -59,7 +59,6 @@
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 XRawBytesFromValue(interface{}) (*RawBytes, error)
pkg vom, method (*ControlKind) Set(string) error
pkg vom, method (*ControlKind) VDLRead(vdl.Decoder) error
pkg vom, method (*Decoder) Decode(interface{}) error
diff --git a/vom/binary_util_test.go b/vom/binary_util_test.go
index 70ed50a..1d98ab1 100644
--- a/vom/binary_util_test.go
+++ b/vom/binary_util_test.go
@@ -11,14 +11,6 @@
"testing"
)
-func hex2Bin(t *testing.T, hex string) []byte {
- binStr, err := binFromHexPat(hex)
- if err != nil {
- t.Fatalf("error converting %q to binary: %v", hex, err)
- }
- return []byte(binStr)
-}
-
func TestBinaryEncodeDecode(t *testing.T) {
tests := []struct {
v interface{}
diff --git a/vom/convert_test.go b/vom/convert_test.go
index 06ffb52..f7c89e6 100644
--- a/vom/convert_test.go
+++ b/vom/convert_test.go
@@ -7,7 +7,6 @@
import (
"bytes"
"fmt"
- "io"
"reflect"
"strings"
"sync"
@@ -139,43 +138,3 @@
}
return nil
}
-
-// In concurrent modes, one goroutine may try to read vom types before they are
-// actually sent by other goroutine. We use a simple buffered pipe to provide
-// blocking read since bytes.Buffer will return EOF in this case.
-type pipe struct {
- b bytes.Buffer
- m sync.Mutex
- c sync.Cond
- closed bool
-}
-
-func newPipe() (io.ReadCloser, io.WriteCloser) {
- p := &pipe{}
- p.c.L = &p.m
- return p, p
-}
-
-func (r *pipe) Read(p []byte) (n int, err error) {
- r.m.Lock()
- defer r.m.Unlock()
- for r.b.Len() == 0 || r.closed {
- r.c.Wait()
- }
- return r.b.Read(p)
-}
-
-func (p *pipe) Close() error {
- p.m.Lock()
- p.closed = true
- p.c.Broadcast()
- p.m.Unlock()
- return nil
-}
-
-func (w *pipe) Write(p []byte) (n int, err error) {
- w.m.Lock()
- defer w.m.Unlock()
- defer w.c.Signal()
- return w.b.Write(p)
-}
diff --git a/vom/decoder.go b/vom/decoder.go
index 1515735..bcbb932 100644
--- a/vom/decoder.go
+++ b/vom/decoder.go
@@ -1,414 +1,800 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
+// 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 vom
import (
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "os"
+
"v.io/v23/vdl"
"v.io/v23/verror"
)
-// TODO(toddw): Merge this file with xdecoder.go
-
-var (
- errDecodeZeroTypeID = verror.Register(pkgPath+".errDecodeZeroTypeID", verror.NoRetry, "{1:}{2:} vom: zero type id{:_}")
- errIndexOutOfRange = verror.Register(pkgPath+".errIndexOutOfRange", verror.NoRetry, "{1:}{2:} vom: index out of range{:_}")
- errLeftOverBytes = verror.Register(pkgPath+".errLeftOverBytes", verror.NoRetry, "{1:}{2:} vom: {3} leftover bytes{:_}")
- errUnexpectedControlByte = verror.Register(pkgPath+".errUnexpectedControlByte", verror.NoRetry, "{1:}{2:} vom: unexpected control byte {3}{:_}")
- errDecodeValueUnhandledType = verror.Register(pkgPath+".errDecodeValueUnhandledType", verror.NoRetry, "{1:}{2:} vom: decodeValue unhandled type {3}{:_}")
- errIgnoreValueUnhandledType = verror.Register(pkgPath+".errIgnoreValueUnhandledType", verror.NoRetry, "{1:}{2:} vom: ignoreValue unhandled type {3}{:_}")
- errInvalidTypeIdIndex = verror.Register(pkgPath+".errInvalidTypeIdIndex", verror.NoRetry, "{1:}{2:} vom: value referenced invalid index into type id table {:_}")
- errInvalidAnyIndex = verror.Register(pkgPath+".errInvalidAnyIndex", verror.NoRetry, "{1:}{2:} vom: value referenced invalid index into anyLen table {:_}")
+const (
+ // IEEE 754 represents float64 using 52 bits to represent the mantissa, with
+ // an extra implied leading bit. That gives us 53 bits to store integers
+ // without overflow - i.e. [0, (2^53)-1]. And since 2^53 is a small power of
+ // two, it can also be stored without loss via mantissa=1 exponent=53. Thus
+ // we have our max and min values. Ditto for float32, which uses 23 bits with
+ // an extra implied leading bit.
+ float64MaxInt = (1 << 53)
+ float64MinInt = -(1 << 53)
+ float32MaxInt = (1 << 24)
+ float32MinInt = -(1 << 24)
)
-func (d *decoder81) decodeTypeDefs() error {
- for {
- typeNext, err := d.typeIsNext()
+var (
+ errEmptyDecoderStack = errors.New("vom: empty decoder stack")
+ errReadRawBytesAlreadyStarted = errors.New("vom: read into vom.RawBytes after StartValue called")
+ errReadRawBytesFromNonAny = errors.New("vom: read into vom.RawBytes only supported on any values")
+)
+
+// This is only used for debugging; add this as the first line of NewDecoder to
+// dump formatted vom bytes to stdout:
+// r = teeDump(r)
+func teeDump(r io.Reader) io.Reader {
+ return io.TeeReader(r, NewDumper(NewDumpWriter(os.Stdout)))
+}
+
+// Decoder manages the receipt and unmarshalling of typed values from the other
+// side of a connection.
+type Decoder struct {
+ dec decoder81
+}
+
+type decoder81 struct {
+ buf *decbuf
+ flag decFlag
+ stack []decStackEntry
+ refTypes referencedTypes
+ refAnyLens referencedAnyLens
+ typeDec *TypeDecoder
+}
+
+type decStackEntry struct {
+ Type *vdl.Type // Type of the value that we're decoding.
+ Index int // Index of the key, elem or field.
+ LenHint int // Length of the value, or -1 for unknown.
+ NextEntryType *vdl.Type // type for NextEntryValue* methods
+ Flag decStackFlag // properties of this stack entry
+ NextEntryData nextEntryData // properties of the next entry
+}
+
+// decFlag holds properties of the decoder.
+type decFlag uint
+
+const (
+ decFlagIgnoreNextStartValue decFlag = 0x1 // ignore the next call to StartValue
+ decFlagIsParentBytes decFlag = 0x2 // parent type is []byte or [N]byte
+ decFlagTypeIncomplete decFlag = 0x4 // type has dependencies on unsent types
+ decFlagSeparateTypeDec decFlag = 0x8 // type decoder is separate
+
+ // In FinishValue we need to clear both of these bits.
+ decFlagFinishValue decFlag = decFlagIgnoreNextStartValue | decFlagIsParentBytes
+)
+
+func (f decFlag) Set(bits decFlag) decFlag { return f | bits }
+func (f decFlag) Clear(bits decFlag) decFlag { return f &^ bits }
+
+func (f decFlag) IgnoreNextStartValue() bool { return f&decFlagIgnoreNextStartValue != 0 }
+func (f decFlag) IsParentBytes() bool { return f&decFlagIsParentBytes != 0 }
+func (f decFlag) TypeIncomplete() bool { return f&decFlagTypeIncomplete != 0 }
+func (f decFlag) SeparateTypeDec() bool { return f&decFlagSeparateTypeDec != 0 }
+
+// decStackFlag holds type or value properties of the stack entry.
+type decStackFlag uint
+
+const (
+ decStackFlagIsMapKey decStackFlag = 0x1 // key or elem for dfsNextType
+ decStackFlagIsAny decStackFlag = 0x2 // the static type is Any
+ decStackFlagIsOptional decStackFlag = 0x4 // the static type is Optional
+ decStackFlagFastRead decStackFlag = 0x8 // subtypes use ReadValue fastpath
+)
+
+func (f decStackFlag) FlipIsMapKey() decStackFlag { return f ^ decStackFlagIsMapKey }
+func (f decStackFlag) IsMapKey() bool { return f&decStackFlagIsMapKey != 0 }
+func (f decStackFlag) IsAny() bool { return f&decStackFlagIsAny != 0 }
+func (f decStackFlag) IsOptional() bool { return f&decStackFlagIsOptional != 0 }
+func (f decStackFlag) FastRead() bool { return f&decStackFlagFastRead != 0 }
+
+// NewDecoder returns a new Decoder that reads from the given reader. The
+// Decoder understands all formats generated by the Encoder.
+func NewDecoder(r io.Reader) *Decoder {
+ buf := newDecbuf(r)
+ typeDec := newTypeDecoderInternal(buf)
+ return &Decoder{decoder81{
+ buf: buf,
+ typeDec: typeDec,
+ }}
+}
+
+// NewDecoderWithTypeDecoder returns a new Decoder that reads from the given
+// reader. Types are decoded separately through the typeDec.
+func NewDecoderWithTypeDecoder(r io.Reader, typeDec *TypeDecoder) *Decoder {
+ return &Decoder{decoder81{
+ buf: newDecbuf(r),
+ typeDec: typeDec,
+ flag: decFlagSeparateTypeDec,
+ }}
+}
+
+// Decoder returns d as a vdl.Decoder.
+func (d *Decoder) Decoder() vdl.Decoder {
+ return &d.dec
+}
+
+// Decode reads the next value and stores it in value v. The type of v need not
+// exactly match the type of the originally encoded value; decoding succeeds as
+// long as the values are convertible.
+func (d *Decoder) Decode(v interface{}) error {
+ return vdl.Read(&d.dec, v)
+}
+
+func (d *decoder81) IgnoreNextStartValue() {
+ d.flag = d.flag.Set(decFlagIgnoreNextStartValue)
+}
+
+func (d *decoder81) decodeWireType(wt *wireType) (TypeId, error) {
+ // 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 __VDLRead_wireType method.
+ tid, err := d.nextMessage()
+ if err != nil {
+ return 0, err
+ }
+ d.stack = append(d.stack, decStackEntry{
+ Type: wireTypeType,
+ Index: -1,
+ LenHint: 1, // wireType is a union
+ })
+ d.flag = d.flag.Set(decFlagIgnoreNextStartValue)
+ if err := __VDLRead_wireType(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 *decoder81) readRawBytes(raw *RawBytes) error {
+ if d.flag.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
+ // already been decoded. So we simply disallow this from happening.
+ return errReadRawBytesAlreadyStarted
+ }
+ tt, err := d.dfsNextType()
+ if err != nil {
+ return err
+ }
+ // Handle top-level values. All types of values are supported, since we can
+ // simply copy the message bytes.
+ if len(d.stack) == 0 {
+ anyLen, err := d.peekValueByteLen(tt)
if err != nil {
return err
}
- if !typeNext {
- return nil
- }
- if err := d.typeDec.readSingleType(); err != nil {
+ if err := d.decodeRaw(tt, anyLen, raw); err != nil {
return err
}
+ return d.endMessage()
}
-}
-
-// peekValueByteLen returns the byte length of the next value.
-func (d *decoder81) peekValueByteLen(tt *vdl.Type) (int, error) {
- if hasChunkLen(tt) {
- // Use the explicit message length.
- return d.buf.lim, nil
+ // Handle internal values. Only any values are supported at the moment, since
+ // they come with a header that tells us the exact length to read.
+ //
+ // TODO(toddw): Handle other types, either by reading and skipping bytes based
+ // on the type, or by falling back to a decode / re-encode slowpath.
+ if tt.Kind() != vdl.Any {
+ return errReadRawBytesFromNonAny
}
- // No explicit message length, but the length can be computed.
- switch {
- case tt.Kind() == vdl.Array && tt.IsBytes():
- // Byte arrays are exactly their length and encoded with 1-byte header.
- return tt.Len() + 1, nil
- case tt.Kind() == vdl.String || tt.IsBytes():
- // Strings and byte lists are encoded with a length header.
- strlen, bytelen, err := binaryPeekUint(d.buf)
- switch {
- case err != nil:
- return 0, err
- case strlen > maxBinaryMsgLen:
- return 0, verror.New(errMsgLen, nil, maxBinaryMsgLen)
- }
- return int(strlen) + bytelen, nil
- default:
- // Must be a primitive, which is encoded as an underlying uint.
- return binaryPeekUintByteLen(d.buf)
- }
-}
-
-func (d *decoder81) decodeRaw(tt *vdl.Type, valLen int, raw *RawBytes) error {
- raw.Version = d.buf.version
- raw.Type = tt
- raw.Data = make([]byte, valLen)
- if err := d.buf.ReadIntoBuf(raw.Data); err != nil {
+ ttElem, anyLen, err := d.readAnyHeader()
+ if err != nil {
return err
}
- refTypeLen := len(d.refTypes.tids)
- if cap(raw.RefTypes) >= refTypeLen {
- raw.RefTypes = raw.RefTypes[:refTypeLen]
- } else {
- raw.RefTypes = make([]*vdl.Type, refTypeLen)
+ 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.
+ raw.Version = d.buf.version
+ raw.Type = vdl.AnyType
+ raw.RefTypes = nil
+ raw.AnyLengths = nil
+ raw.Data = []byte{WireCtrlNil}
+ return nil
}
- for i, tid := range d.refTypes.tids {
- var err error
- if raw.RefTypes[i], err = d.typeDec.lookupType(tid); err != nil {
- return err
- }
+ return d.decodeRaw(ttElem, anyLen, raw)
+}
+
+func (d *decoder81) StartValue(want *vdl.Type) error {
+ if d.flag.IgnoreNextStartValue() {
+ d.flag = d.flag.Clear(decFlagIgnoreNextStartValue)
+ return nil
}
- raw.AnyLengths = d.refAnyLens.lens
+ tt, err := d.dfsNextType()
+ if err != nil {
+ return err
+ }
+ tt, lenHint, flag, err := d.setupType(tt, want)
+ if err != nil {
+ return err
+ }
+ d.stack = append(d.stack, decStackEntry{
+ Type: tt,
+ Index: -1,
+ LenHint: lenHint,
+ Flag: flag,
+ })
return nil
}
-func (d *decoder81) readAnyHeader() (*vdl.Type, int, error) {
- // Handle WireCtrlNil.
- switch ok, err := binaryDecodeControlOnly(d.buf, WireCtrlNil); {
- case err != nil:
- return nil, 0, err
- case ok:
- return nil, 0, nil // nil any
- }
- // Read the index of the referenced type id.
- typeIndex, err := binaryDecodeUint(d.buf)
- if err != nil {
- return nil, 0, err
- }
- var tid TypeId
- if d.buf.version == Version80 {
- tid = TypeId(typeIndex)
- } else if tid, err = d.refTypes.ReferencedTypeId(typeIndex); err != nil {
- return nil, 0, err
- }
- // Look up the referenced type id.
- ttElem, err := d.typeDec.lookupType(tid)
- if err != nil {
- return nil, 0, err
- }
- var anyLen int
- if d.buf.version != Version80 {
- // Read and lookup the index of the any byte length. Reference the any len,
- // even if it isn't used, to report missing references.
- lenIndex, err := binaryDecodeUint(d.buf)
- if err != nil {
- return nil, 0, err
- }
- if anyLen, err = d.refAnyLens.ReferencedAnyLen(lenIndex); err != nil {
- return nil, 0, err
- }
- }
- return ttElem, anyLen, nil
-}
-
-func (d *decoder81) skipValue(tt *vdl.Type) error {
- if tt.IsBytes() {
- len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
- if err != nil {
- return err
- }
- return d.buf.Skip(len)
- }
- switch kind := tt.Kind(); kind {
- case vdl.Bool:
- return d.buf.Skip(1)
- case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64, vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64, vdl.Float32, vdl.Float64, vdl.Enum, vdl.TypeObject:
- // The underlying encoding of all these types is based on uint.
- return binarySkipUint(d.buf)
- case vdl.String:
- return binarySkipString(d.buf)
- case vdl.Array, vdl.List, vdl.Set, vdl.Map:
- len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
- if err != nil {
- return err
- }
- for ix := 0; ix < len; ix++ {
- if kind == vdl.Set || kind == vdl.Map {
- if err := d.skipValue(tt.Key()); err != nil {
- return err
- }
- }
- if kind == vdl.Array || kind == vdl.List || kind == vdl.Map {
- if err := d.skipValue(tt.Elem()); err != nil {
- return err
- }
- }
- }
- return nil
- case vdl.Struct:
- // Loop through decoding the 0-based field index and corresponding field.
- for {
- switch ok, err := binaryDecodeControlOnly(d.buf, WireCtrlEnd); {
- case err != nil:
- return err
- case ok:
- return nil // end of struct
- }
- switch index, err := binaryDecodeUint(d.buf); {
- case err != nil:
- return err
- case index >= uint64(tt.NumField()):
- return verror.New(errIndexOutOfRange, nil)
- default:
- ttfield := tt.Field(int(index))
- if err := d.skipValue(ttfield.Type); err != nil {
- return err
- }
- }
- }
- case vdl.Union:
- switch index, err := binaryDecodeUint(d.buf); {
+func (d *decoder81) setupType(tt, want *vdl.Type) (_ *vdl.Type, lenHint int, flag decStackFlag, _ error) {
+ // Handle any, which may be nil. We "dereference" non-nil any to the inner
+ // type. If that happens to be an optional, it's handled below.
+ if tt.Kind() == vdl.Any {
+ flag |= decStackFlagIsAny
+ var err error
+ switch tt, _, err = d.readAnyHeader(); {
case err != nil:
- return err
- case index >= uint64(tt.NumField()):
- return verror.New(errIndexOutOfRange, nil)
- default:
- ttfield := tt.Field(int(index))
- return d.skipValue(ttfield.Type)
+ return nil, 0, 0, err
+ case tt == nil:
+ tt = vdl.AnyType // nil any
}
- case vdl.Optional:
+ }
+ // Handle optional, which may be nil. Similar to any, we "dereference"
+ // non-nil optional to the inner type, which is never allowed to be another
+ // optional or any type.
+ if tt.Kind() == vdl.Optional {
+ flag |= decStackFlagIsOptional
// Read the WireCtrlNil code, but if it's not WireCtrlNil we need to keep
// the buffer as-is, since it's the first byte of the value, which may
// itself be another control code.
switch ctrl, err := binaryPeekControl(d.buf); {
case err != nil:
- return err
+ return nil, 0, 0, err
case ctrl == WireCtrlNil:
d.buf.SkipAvailable(1) // nil optional
- return nil
default:
- return d.skipValue(tt.Elem()) // non-nil optional
+ tt = tt.Elem() // non-nil optional
}
- case vdl.Any:
- switch ok, err := binaryDecodeControlOnly(d.buf, WireCtrlNil); {
- case err != nil:
- return err
- case ok:
- return nil // nil any
- }
- switch index, err := binaryDecodeUint(d.buf); {
- case err != nil:
- return err
- default:
- tid, err := d.refTypes.ReferencedTypeId(index)
- if err != nil {
- return err
+ }
+ // Check compatibility between the actual type and the want type. Since
+ // compatibility applies to the entire static type, we only need to perform
+ // this check for top-level decoded values, and subsequently for decoded any
+ // values. We skip checking non-composite want types, since those will be
+ // naturally caught by the Decode* calls anyways.
+ if want != nil && (len(d.stack) == 0 || flag.IsAny()) {
+ switch want.Kind() {
+ case vdl.Optional, vdl.Array, vdl.List, vdl.Set, vdl.Map, vdl.Struct, vdl.Union:
+ if tt == want {
+ // Set FastRead flag, which will let us use the fastpath for ReadValue*
+ // in common cases. We can only use this fastpath if tt and want are
+ // identical, which ensures we don't need to perform any conversions.
+ if !flag.IsAny() && isFastReadParent(tt) {
+ flag |= decStackFlagFastRead
+ }
+ // Regardless of whether we can use the fastpath, there's no need to
+ // check compatibility if tt and want are identical.
+ } else {
+ if !vdl.Compatible(tt, want) {
+ return nil, 0, 0, errIncompatibleDecode(tt, want)
+ }
}
- ttElem, err := d.typeDec.lookupType(tid)
- if err != nil {
- return err
- }
- return d.skipValue(ttElem)
}
+ }
+ // Initialize LenHint for composite types.
+ switch tt.Kind() {
+ case vdl.Array, vdl.List, vdl.Set, vdl.Map:
+ // TODO(toddw): Handle sentry-terminated collections without a length hint.
+ len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
+ if err != nil {
+ return nil, 0, 0, err
+ }
+ lenHint = len
+ case vdl.Union:
+ // Union shouldn't have a LenHint, but we abuse it in NextField as a
+ // convenience for detecting when fields are done, so we initialize it here.
+ // It has to be at least 1, since 0 will cause NextField to think that the
+ // union field has already been decoded.
+ lenHint = 1
+ case vdl.Struct:
+ // Struct shouldn't have a LenHint, but we abuse it in NextField as a
+ // convenience for detecting when fields are done, so we initialize it here.
+ lenHint = tt.NumField()
default:
- return verror.New(errIgnoreValueUnhandledType, nil, tt)
+ lenHint = -1
}
+ if top := d.top(); top != nil && top.Type.IsBytes() {
+ d.flag = d.flag.Set(decFlagIsParentBytes)
+ } else {
+ d.flag = d.flag.Clear(decFlagIsParentBytes)
+ }
+ return tt, lenHint, flag, nil
}
-func (d *decoder81) nextMessage() (TypeId, error) {
- if leftover := d.buf.RemoveLimit(); leftover > 0 {
- return 0, verror.New(errLeftOverBytes, nil, leftover)
- }
- // Decode version byte, if not already decoded.
- if d.buf.version == 0 {
- version, err := d.buf.ReadByte()
- if err != nil {
- return 0, verror.New(errEndedBeforeVersionByte, nil, err)
- }
- d.buf.version = Version(version)
- if !isAllowedVersion(d.buf.version) {
- return 0, verror.New(errBadVersionByte, nil, d.buf.version)
- }
- }
- // Decode the next message id.
- incomplete, err := binaryDecodeControlOnly(d.buf, WireCtrlTypeIncomplete)
- if err != nil {
- return 0, err
- }
- mid, err := binaryDecodeInt(d.buf)
- if err != nil {
- return 0, err
- }
- if incomplete {
- if mid >= 0 {
- // TypeIncomplete must be followed with a type message.
- return 0, verror.New(errInvalid, nil)
- }
- d.flag = d.flag.Set(decFlagTypeIncomplete)
- } else if mid < 0 {
- d.flag = d.flag.Clear(decFlagTypeIncomplete)
- }
- // TODO(toddw): Clean up the logic below.
- var tid TypeId
- var hasAny, hasTypeObject, hasLength bool
- switch {
- case mid < 0:
- tid = TypeId(-mid)
- hasLength = true
- hasAny = false
- hasTypeObject = false
- case mid > 0:
- tid = TypeId(mid)
- t, err := d.typeDec.lookupType(tid)
- if err != nil {
- return 0, err
- }
- hasLength = hasChunkLen(t)
- hasAny = containsAny(t)
- hasTypeObject = containsTypeObject(t)
- default:
- return 0, verror.New(errDecodeZeroTypeID, nil)
- }
-
- if (hasAny || hasTypeObject) && d.buf.version != Version80 {
- l, err := binaryDecodeUint(d.buf)
- if err != nil {
- return 0, err
- }
- for i := 0; i < int(l); i++ {
- refId, err := binaryDecodeUint(d.buf)
- if err != nil {
- return 0, err
- }
- d.refTypes.AddTypeID(TypeId(refId))
- }
- }
- if hasAny && d.buf.version != Version80 {
- l, err := binaryDecodeUint(d.buf)
- if err != nil {
- return 0, err
- }
- for i := 0; i < int(l); i++ {
- refAnyLen, err := binaryDecodeLen(d.buf)
- if err != nil {
- return 0, err
- }
- d.refAnyLens.AddAnyLen(refAnyLen)
- }
- }
-
- if hasLength {
- chunkLen, err := binaryDecodeUint(d.buf)
- if err != nil {
- return 0, err
- }
- d.buf.SetLimit(int(chunkLen))
- }
-
- return tid, nil
+func errIncompatibleDecode(tt *vdl.Type, want interface{}) error {
+ return fmt.Errorf("vom: incompatible decode from %v into %v", tt, want)
}
-func (d *decoder81) typeIsNext() (bool, error) {
- if d.buf.version == 0 {
- version, err := d.buf.ReadByte()
- if err != nil {
- return false, verror.New(errEndedBeforeVersionByte, nil, err)
- }
- d.buf.version = Version(version)
- if !isAllowedVersion(d.buf.version) {
- return false, verror.New(errBadVersionByte, nil, d.buf.version)
- }
+func (d *decoder81) FinishValue() error {
+ d.flag = d.flag.Clear(decFlagFinishValue)
+ stackTop := len(d.stack) - 1
+ if stackTop == -1 {
+ return errEmptyDecoderStack
}
- switch ctrl, err := binaryPeekControl(d.buf); {
- case err != nil:
- return false, err
- case ctrl == WireCtrlTypeIncomplete:
- return true, nil
- case ctrl != 0:
- return false, verror.New(errBadControlCode, nil, ctrl)
- }
- mid, _, err := binaryPeekInt(d.buf)
- if err != nil {
- return false, err
- }
- return mid < 0, nil
-}
-
-func (d *decoder81) endMessage() error {
- if leftover := d.buf.RemoveLimit(); leftover > 0 {
- return verror.New(errLeftOverBytes, nil, leftover)
- }
- if err := d.refTypes.Reset(); err != nil {
- return err
- }
- if err := d.refAnyLens.Reset(); err != nil {
- return err
+ d.stack = d.stack[:stackTop]
+ if stackTop == 0 {
+ return d.endMessage()
}
return nil
}
-type referencedTypes struct {
- tids []TypeId
- marker int
-}
-
-func (refTypes *referencedTypes) Reset() (err error) {
- refTypes.tids = refTypes.tids[:0]
- refTypes.marker = 0
- return
-}
-
-func (refTypes *referencedTypes) AddTypeID(tid TypeId) {
- refTypes.tids = append(refTypes.tids, tid)
-}
-
-func (refTypes *referencedTypes) ReferencedTypeId(index uint64) (TypeId, error) {
- if index >= uint64(len(refTypes.tids)) {
- return 0, verror.New(errInvalidTypeIdIndex, nil)
+func (d *decoder81) top() *decStackEntry {
+ if stackTop := len(d.stack) - 1; stackTop >= 0 {
+ return &d.stack[stackTop]
}
- return refTypes.tids[index], nil
+ return nil
}
-func (refTypes *referencedTypes) Mark() {
- refTypes.marker = len(refTypes.tids)
-}
-
-type referencedAnyLens struct {
- lens []int
- marker int
-}
-
-func (refAnys *referencedAnyLens) Reset() (err error) {
- refAnys.lens = refAnys.lens[:0]
- return
-}
-
-func (refAnys *referencedAnyLens) AddAnyLen(len int) {
- refAnys.lens = append(refAnys.lens, len)
-}
-
-func (refAnys *referencedAnyLens) ReferencedAnyLen(index uint64) (int, error) {
- if index >= uint64(len(refAnys.lens)) {
- return 0, verror.New(errInvalidAnyIndex, nil)
+// dfsNextType determines the type of the next value that we will decode, by
+// walking the static type in DFS order. To bootstrap we retrieve the top-level
+// type from the VOM value message.
+func (d *decoder81) dfsNextType() (*vdl.Type, error) {
+ top := d.top()
+ if top == nil {
+ // Bootstrap: start decoding a new top-level value.
+ if !d.flag.SeparateTypeDec() {
+ if err := d.decodeTypeDefs(); err != nil {
+ return nil, err
+ }
+ }
+ tid, err := d.nextMessage()
+ if err != nil {
+ return nil, err
+ }
+ return d.typeDec.lookupType(tid)
}
- return refAnys.lens[index], nil
+ // Return the next type from our composite types.
+ tt := top.Type
+ switch tt.Kind() {
+ case vdl.Array, vdl.List:
+ return tt.Elem(), nil
+ case vdl.Set:
+ return tt.Key(), nil
+ case vdl.Map:
+ top.Flag = top.Flag.FlipIsMapKey()
+ if top.Flag.IsMapKey() {
+ return tt.Key(), nil
+ } else {
+ return tt.Elem(), nil
+ }
+ case vdl.Union, vdl.Struct:
+ return tt.Field(top.Index).Type, nil
+ }
+ return nil, fmt.Errorf("vom: can't StartValue on %v", tt)
}
-func (refAnys *referencedAnyLens) Mark() {
- refAnys.marker = len(refAnys.lens)
+func (d *decoder81) NextEntry() (bool, error) {
+ // Our strategy is to increment top.Index until it reaches top.LenHint.
+ // Currently the LenHint is always set, so it's stronger than a hint.
+ //
+ // TODO(toddw): Handle sentry-terminated collections without a LenHint.
+ top := d.top()
+ if top == nil {
+ return false, errEmptyDecoderStack
+ }
+ // Increment index and check errors.
+ top.Index++
+ switch top.Type.Kind() {
+ case vdl.Array, vdl.List, vdl.Set, vdl.Map:
+ if top.Index > top.LenHint && top.LenHint >= 0 {
+ return false, fmt.Errorf("vom: NextEntry called after done, stack: %+v", d.stack)
+ }
+ default:
+ return false, fmt.Errorf("vom: NextEntry called on invalid type, stack: %+v", d.stack)
+ }
+ return top.Index == top.LenHint, nil
+}
+
+func (d *decoder81) NextField() (int, error) {
+ top := d.top()
+ if top == nil {
+ return -1, errEmptyDecoderStack
+ }
+ // Increment index and check errors. Note that the actual top.Index is
+ // decoded from the buf data stream; we use top.LenHint to help detect when
+ // the fields are done, and to detect invalid calls after we're done.
+ top.Index++
+ switch top.Type.Kind() {
+ case vdl.Union, vdl.Struct:
+ if top.Index > top.LenHint {
+ return -1, fmt.Errorf("vom: NextField called after done, stack: %+v", d.stack)
+ }
+ default:
+ return -1, fmt.Errorf("vom: NextField called on invalid type, stack: %+v", d.stack)
+ }
+ var field int
+ switch top.Type.Kind() {
+ case vdl.Union:
+ if top.Index == top.LenHint {
+ // We know we're done since we set LenHint=Index+1 the first time around,
+ // and we incremented the index above.
+ return -1, nil
+ }
+ // Decode the union field index.
+ switch index, err := binaryDecodeUint(d.buf); {
+ case err != nil:
+ return -1, err
+ case index >= uint64(top.Type.NumField()):
+ return -1, verror.New(errIndexOutOfRange, nil)
+ default:
+ // Set LenHint=Index+1 so that we'll know we're done next time around.
+ field = int(index)
+ top.Index = field
+ top.LenHint = field + 1
+ }
+ case vdl.Struct:
+ // Handle the end-of-struct sentry.
+ switch ok, err := binaryDecodeControlOnly(d.buf, WireCtrlEnd); {
+ case err != nil:
+ return -1, err
+ case ok:
+ // Set Index=LenHint to ensure repeated calls will fail.
+ top.Index = top.LenHint
+ return -1, nil
+ }
+ // Decode the struct field index.
+ switch index, err := binaryDecodeUint(d.buf); {
+ case err != nil:
+ return -1, err
+ case index >= uint64(top.Type.NumField()):
+ return -1, verror.New(errIndexOutOfRange, nil)
+ default:
+ field = int(index)
+ top.Index = field
+ }
+ }
+ return field, nil
+}
+
+func (d *decoder81) Type() *vdl.Type {
+ if top := d.top(); top != nil {
+ return top.Type
+ }
+ return nil
+}
+
+func (d *decoder81) IsAny() bool {
+ if top := d.top(); top != nil {
+ return top.Flag.IsAny()
+ }
+ return false
+}
+
+func (d *decoder81) IsOptional() bool {
+ if top := d.top(); top != nil {
+ return top.Flag.IsOptional()
+ }
+ return false
+}
+
+func (d *decoder81) IsNil() bool {
+ if top := d.top(); top != nil {
+ // Becuase of the "dereferencing" we do, the only time the type is any or
+ // optional is when it's nil.
+ return top.Type == vdl.AnyType || top.Type.Kind() == vdl.Optional
+ }
+ return false
+}
+
+func (d *decoder81) Index() int {
+ if top := d.top(); top != nil {
+ return top.Index
+ }
+ return -1
+}
+
+func (d *decoder81) LenHint() int {
+ if top := d.top(); top != nil {
+ // Note that union and struct shouldn't have a LenHint, but we abuse it in
+ // NextField as a convenience for detecting when fields are done, so an
+ // "arbitrary" value is returned here. Users shouldn't be looking at it for
+ // union and struct anyways.
+ return top.LenHint
+ }
+ return -1
+}
+
+func (d *decoder81) DecodeBool() (bool, error) {
+ tt := d.Type()
+ if tt == nil {
+ return false, errEmptyDecoderStack
+ }
+ if tt.Kind() == vdl.Bool {
+ return binaryDecodeBool(d.buf)
+ }
+ return false, errIncompatibleDecode(tt, "bool")
+}
+
+func (d *decoder81) DecodeString() (string, error) {
+ tt := d.Type()
+ if tt == nil {
+ return "", errEmptyDecoderStack
+ }
+ switch tt.Kind() {
+ case vdl.String:
+ return binaryDecodeString(d.buf)
+ case vdl.Enum:
+ return d.binaryDecodeEnum(tt)
+ }
+ return "", errIncompatibleDecode(tt, "string")
+}
+
+func (d *decoder81) binaryDecodeEnum(tt *vdl.Type) (string, error) {
+ index, err := binaryDecodeUint(d.buf)
+ switch {
+ case err != nil:
+ return "", err
+ case index >= uint64(tt.NumEnumLabel()):
+ return "", fmt.Errorf("vom: enum index %d out of range, %v", index, tt)
+ }
+ return tt.EnumLabel(int(index)), nil
+}
+
+func (d *decoder81) binaryDecodeByte() (byte, error) {
+ // Handle a special-case where normally single bytes are written out as
+ // variable sized numbers, which use 2 bytes to encode bytes > 127. But each
+ // byte contained in a list or array is written out as one byte. E.g.
+ // byte(0x81) -> 0xFF81 : single byte with variable-size
+ // []byte("\x81\x82") -> 0x028182 : each elem byte encoded as one byte
+ if d.flag.IsParentBytes() {
+ return d.buf.ReadByte()
+ }
+ x, err := binaryDecodeUint(d.buf)
+ return byte(x), err
+}
+
+func (d *decoder81) DecodeUint(bitlen int) (uint64, error) {
+ tt := d.Type()
+ if tt == nil {
+ return 0, errEmptyDecoderStack
+ }
+ return d.decodeUint(tt, uint(bitlen))
+}
+
+func (d *decoder81) decodeUint(tt *vdl.Type, ubitlen uint) (uint64, error) {
+ const errFmt = "vom: conversion from %v into uint%d loses precision: %v"
+ switch tt.Kind() {
+ case vdl.Byte:
+ x, err := d.binaryDecodeByte()
+ if err != nil {
+ return 0, err
+ }
+ return uint64(x), err
+ case vdl.Uint16, vdl.Uint32, vdl.Uint64:
+ x, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ if shift := 64 - ubitlen; x != (x<<shift)>>shift {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return x, nil
+ case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64:
+ x, err := binaryDecodeInt(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ ux := uint64(x)
+ if shift := 64 - ubitlen; x < 0 || ux != (ux<<shift)>>shift {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return ux, nil
+ case vdl.Float32, vdl.Float64:
+ x, err := binaryDecodeFloat(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ ux := uint64(x)
+ if shift := 64 - ubitlen; x != float64(ux) || ux != (ux<<shift)>>shift {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return ux, nil
+ }
+ return 0, errIncompatibleDecode(tt, fmt.Sprintf("uint%d", ubitlen))
+}
+
+func (d *decoder81) DecodeInt(bitlen int) (int64, error) {
+ tt := d.Type()
+ if tt == nil {
+ return 0, errEmptyDecoderStack
+ }
+ return d.decodeInt(tt, uint(bitlen))
+}
+
+func (d *decoder81) decodeInt(tt *vdl.Type, ubitlen uint) (int64, error) {
+ const errFmt = "vom: conversion from %v into int%d loses precision: %v"
+ switch tt.Kind() {
+ case vdl.Byte:
+ x, err := d.binaryDecodeByte()
+ if err != nil {
+ return 0, err
+ }
+ // The only case that fails is if we're converting byte(x) to int8, and x
+ // uses more than 7 bits (i.e. is greater than 127).
+ if ubitlen <= 8 && x > 0x7f {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return int64(x), nil
+ case vdl.Uint16, vdl.Uint32, vdl.Uint64:
+ x, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ ix := int64(x)
+ // The shift uses 65 since the topmost bit is the sign bit. I.e. 32 bit
+ // numbers should be shifted by 33 rather than 32.
+ if shift := 65 - ubitlen; ix < 0 || x != (x<<shift)>>shift {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return ix, nil
+ case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64:
+ x, err := binaryDecodeInt(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ if shift := 64 - ubitlen; x != (x<<shift)>>shift {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return x, nil
+ case vdl.Float32, vdl.Float64:
+ x, err := binaryDecodeFloat(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ ix := int64(x)
+ if shift := 64 - ubitlen; x != float64(ix) || ix != (ix<<shift)>>shift {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return ix, nil
+ }
+ return 0, errIncompatibleDecode(tt, fmt.Sprintf("int%d", ubitlen))
+}
+
+func (d *decoder81) DecodeFloat(bitlen int) (float64, error) {
+ tt := d.Type()
+ if tt == nil {
+ return 0, errEmptyDecoderStack
+ }
+ return d.decodeFloat(tt, uint(bitlen))
+}
+
+func (d *decoder81) decodeFloat(tt *vdl.Type, ubitlen uint) (float64, error) {
+ const errFmt = "vom: conversion from %v into float%d loses precision: %v"
+ switch tt.Kind() {
+ case vdl.Byte:
+ x, err := d.binaryDecodeByte()
+ if err != nil {
+ return 0, err
+ }
+ return float64(x), nil
+ case vdl.Uint16, vdl.Uint32, vdl.Uint64:
+ x, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ var max uint64
+ if ubitlen > 32 {
+ max = float64MaxInt
+ } else {
+ max = float32MaxInt
+ }
+ if x > max {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return float64(x), nil
+ case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64:
+ x, err := binaryDecodeInt(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ var min, max int64
+ if ubitlen > 32 {
+ min, max = float64MinInt, float64MaxInt
+ } else {
+ min, max = float32MinInt, float32MaxInt
+ }
+ if x < min || x > max {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return float64(x), nil
+ case vdl.Float32, vdl.Float64:
+ x, err := binaryDecodeFloat(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ if ubitlen <= 32 && (x < -math.MaxFloat32 || x > math.MaxFloat32) {
+ return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
+ }
+ return x, nil
+ }
+ return 0, errIncompatibleDecode(tt, fmt.Sprintf("float%d", ubitlen))
+}
+
+func (d *decoder81) DecodeBytes(fixedLen int, v *[]byte) error {
+ top := d.top()
+ if top == nil {
+ return errEmptyDecoderStack
+ }
+ tt := top.Type
+ if !tt.IsBytes() {
+ return vdl.DecodeConvertedBytes(d, fixedLen, v)
+ }
+ return d.decodeBytes(tt, top.LenHint, fixedLen, v)
+}
+
+func (d *decoder81) decodeBytes(tt *vdl.Type, lenHint, fixedLen int, v *[]byte) error {
+ switch {
+ case lenHint == -1:
+ return fmt.Errorf("vom: LenHint is currently required, %v", tt)
+ case fixedLen >= 0 && fixedLen != lenHint:
+ return fmt.Errorf("vom: got %d bytes, want fixed len %d, %v", lenHint, fixedLen, tt)
+ case lenHint == 0:
+ *v = nil
+ return nil
+ case fixedLen >= 0:
+ // Only re-use the existing buffer if we're filling in an array. This
+ // sacrifices some performance, but also avoids bugs when repeatedly
+ // decoding into the same value.
+ *v = (*v)[:lenHint]
+ default:
+ *v = make([]byte, lenHint)
+ }
+ return d.buf.ReadIntoBuf(*v)
+}
+
+func (d *decoder81) DecodeTypeObject() (*vdl.Type, error) {
+ tt := d.Type()
+ if tt == nil {
+ return nil, errEmptyDecoderStack
+ }
+ if tt != vdl.TypeObjectType {
+ return nil, errIncompatibleDecode(tt, "typeobject")
+ }
+ return d.binaryDecodeType()
+}
+
+func (d *decoder81) binaryDecodeType() (*vdl.Type, error) {
+ typeIndex, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return nil, err
+ }
+ tid, err := d.refTypes.ReferencedTypeId(typeIndex)
+ if err != nil {
+ return nil, err
+ }
+ return d.typeDec.lookupType(tid)
+}
+
+func (d *decoder81) SkipValue() error {
+ tt, err := d.dfsNextType()
+ if err != nil {
+ return err
+ }
+ if len(d.stack) == 0 {
+ // Handle top-level values. It's easy to determine the byte length of the
+ // value, so we can just skip the bytes.
+ valueLen, err := d.peekValueByteLen(tt)
+ if err != nil {
+ return err
+ }
+ if err := d.buf.Skip(valueLen); err != nil {
+ return err
+ }
+ return d.endMessage()
+ }
+ return d.skipValue(tt)
}
diff --git a/vom/decoder_test.go b/vom/decoder_test.go
index 6d33e57..98317e4 100644
--- a/vom/decoder_test.go
+++ b/vom/decoder_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package vom
+package vom_test
import (
"bytes"
@@ -16,172 +16,122 @@
"testing"
"v.io/v23/vdl"
- "v.io/v23/vom/testdata/data81"
+ "v.io/v23/vom"
"v.io/v23/vom/testdata/types"
+ "v.io/v23/vom/vomtest"
+
+ // Import verror to ensure that interface tests result in *verror.E
+ _ "v.io/v23/verror"
+)
+
+var (
+ rtIface = reflect.TypeOf((*interface{})(nil)).Elem()
+ rtValue = reflect.TypeOf(vdl.Value{})
)
func TestDecoder(t *testing.T) {
- for _, test := range data81.Tests {
- // Decode hex patterns into binary data.
- binversion, err := binFromHexPat(test.HexVersion)
- if err != nil {
- t.Errorf("%s: couldn't convert to binary from hexversion: %q", test.Name, test.HexVersion)
- continue
- }
- bintype, err := binFromHexPat(test.HexType)
- if err != nil {
- t.Errorf("%s: couldn't convert to binary from hextype: %q", test.Name, test.HexType)
- continue
- }
- binvalue, err := binFromHexPat(test.HexValue)
- if err != nil {
- t.Errorf("%s: couldn't convert to binary from hexvalue: %q", test.Name, test.HexValue)
- continue
- }
-
- name := test.Name + " [vdl.Value]"
- testDecodeVDL(t, name, binversion+bintype+binvalue, test.Value)
- name = test.Name + " [vdl.Value] (with TypeDecoder)"
- testDecodeVDLWithTypeDecoder(t, name, binversion, bintype, binvalue, test.Value)
- name = test.Name + " [vdl.Any]"
- testDecodeVDL(t, name, binversion+bintype+binvalue, vdl.AnyValue(test.Value))
- name = test.Name + " [vdl.Any] (with TypeDecoder)"
- testDecodeVDLWithTypeDecoder(t, name, binversion, bintype, binvalue, vdl.AnyValue(test.Value))
-
- // Convert into Go value for the rest of our tests.
- goValue, err := toGoValue(test.Value)
- if err != nil {
- t.Errorf("%s: %v", test.Name, err)
- continue
- }
-
- name = test.Name + " [go value]"
- testDecodeGo(t, name, binversion+bintype+binvalue, reflect.TypeOf(goValue), goValue)
- name = test.Name + " [go value] (with TypeDecoder)"
- testDecodeGoWithTypeDecoder(t, name, binversion, bintype, binvalue, reflect.TypeOf(goValue), goValue)
-
- name = test.Name + " [go interface]"
- testDecodeGo(t, name, binversion+bintype+binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), goValue)
- name = test.Name + " [go interface] (with TypeDecoder)"
- testDecodeGoWithTypeDecoder(t, name, binversion, bintype, binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), goValue)
+ // The decoder tests take a long time, so we run them concurrently.
+ var pending sync.WaitGroup
+ for _, test := range vomtest.AllPass() {
+ pending.Add(1)
+ go func(test vomtest.Entry) {
+ defer pending.Done()
+ testDecoder(t, "[go value]", test, rvPtrValue(test.Value))
+ testDecoder(t, "[go iface]", test, rvPtrIface(test.Value))
+ vv, err := vdl.ValueFromReflect(test.Value)
+ if err != nil {
+ t.Errorf("%s: ValueFromReflect failed: %v", test.Name(), err)
+ return
+ }
+ vvWant := reflect.ValueOf(vv)
+ testDecoder(t, "[new *vdl.Value]", test, vvWant)
+ testDecoderFunc(t, "[zero vdl.Value]", test, vvWant, func() reflect.Value {
+ return reflect.ValueOf(vdl.ZeroValue(vv.Type()))
+ })
+ }(test)
}
+ pending.Wait()
}
-func testDecodeVDL(t *testing.T, name, bin string, value *vdl.Value) {
- for _, mode := range AllReadModes {
- head := fmt.Sprintf("%s (%s)", name, mode)
- decoder := NewDecoder(mode.TestReader(strings.NewReader(bin)))
- if value == nil {
- value = vdl.ZeroValue(vdl.AnyType)
+func rvPtrValue(rv reflect.Value) reflect.Value {
+ result := reflect.New(rv.Type())
+ result.Elem().Set(rv)
+ return result
+}
+
+func rvPtrIface(rv reflect.Value) reflect.Value {
+ result := reflect.New(rtIface)
+ result.Elem().Set(rv)
+ return result
+}
+
+func testDecoder(t *testing.T, pre string, test vomtest.Entry, rvWant reflect.Value) {
+ testDecoderFunc(t, pre, test, rvWant, func() reflect.Value {
+ return reflect.New(rvWant.Type().Elem())
+ })
+ // TODO(toddw): Add tests that start with a randomly-set value.
+}
+
+func testDecoderFunc(t *testing.T, pre string, test vomtest.Entry, rvWant reflect.Value, rvNew func() reflect.Value) {
+ readEOF := make([]byte, 1)
+ for _, mode := range vom.AllReadModes {
+ // Test vom.NewDecoder.
+ {
+ name := fmt.Sprintf("%s (%s) %s", pre, mode, test.Name())
+ rvGot := rvNew()
+ reader := mode.TestReader(bytes.NewReader(test.Bytes()))
+ dec := vom.NewDecoder(reader)
+ if err := dec.Decode(rvGot.Interface()); err != nil {
+ t.Errorf("%s: Decode failed: %v", name, err)
+ return
+ }
+ if !vdl.DeepEqualReflect(rvGot, rvWant) {
+ t.Errorf("%s\nGOT %v\nWANT %v", name, rvGot, rvWant)
+ return
+ }
+ if n, err := reader.Read(readEOF); n != 0 || err != io.EOF {
+ t.Errorf("%s: reader got (%d,%v), want (0,EOF)", name, n, err)
+ }
}
- 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 vom.NewDecoderWithTypeDecoder
+ {
+ name := fmt.Sprintf("%s (%s with TypeDecoder) %s", pre, mode, test.Name())
+ rvGot := rvNew()
+ readerT := mode.TestReader(bytes.NewReader(test.TypeBytes()))
+ decT := vom.NewTypeDecoder(readerT)
+ decT.Start()
+ reader := mode.TestReader(bytes.NewReader(test.ValueBytes()))
+ dec := vom.NewDecoderWithTypeDecoder(reader, decT)
+ err := dec.Decode(rvGot.Interface())
+ decT.Stop()
+ if err != nil {
+ t.Errorf("%s: Decode failed: %v", name, err)
+ return
+ }
+ if !vdl.DeepEqualReflect(rvGot, rvWant) {
+ t.Errorf("%s\nGOT %v\nWANT %v", name, rvGot, rvWant)
+ return
+ }
+ if n, err := reader.Read(readEOF); n != 0 || err != io.EOF {
+ t.Errorf("%s: reader got (%d,%v), want (0,EOF)", name, n, err)
+ }
+ if n, err := readerT.Read(readEOF); n != 0 || err != io.EOF {
+ t.Errorf("%s: readerT got (%d,%v), want (0,EOF)", name, n, err)
+ }
}
}
// Test single-shot vom.Decode twice, to ensure we test the cache hit case.
- testDecodeVDLSingleShot(t, name, bin, value)
- testDecodeVDLSingleShot(t, name, bin, value)
-}
-
-func testDecodeVDLSingleShot(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 := Decode([]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 testDecodeVDLWithTypeDecoder(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 := NewDecoderWithTypeDecoder(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)
+ for i := 0; i < 2; i++ {
+ name := fmt.Sprintf("%s (single-shot %d) %s", pre, i, test.Name())
+ rvGot := rvNew()
+ if err := vom.Decode(test.Bytes(), rvGot.Interface()); err != nil {
+ t.Errorf("%s: Decode failed: %v", name, err)
return
}
- if want := value; !vdl.EqualValue(got, want) {
- t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
+ if !vdl.DeepEqualReflect(rvGot, rvWant) {
+ t.Errorf("%s\nGOT %v\nWANT %v", name, rvGot, rvWant)
return
}
- typedec.Stop()
- }
-}
-
-func testDecodeGo(t *testing.T, name, bin string, rt reflect.Type, want interface{}) {
- for _, mode := range AllReadModes {
- head := fmt.Sprintf("%s (%s)", name, mode)
- decoder := NewDecoder(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.
- testDecodeGoSingleShot(t, name, bin, rt, want)
- testDecodeGoSingleShot(t, name, bin, rt, want)
-}
-
-func testDecodeGoSingleShot(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 := Decode([]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 testDecodeGoWithTypeDecoder(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 := NewDecoderWithTypeDecoder(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()
}
}
@@ -253,13 +203,13 @@
}
var (
- typeenc *TypeEncoder
- typedec *TypeDecoder
+ typeenc *vom.TypeEncoder
+ typedec *vom.TypeDecoder
)
if withTypeEncoderDecoder {
r, w := newPipe()
- typeenc = NewTypeEncoder(w)
- typedec = NewTypeDecoder(r)
+ typeenc = vom.NewTypeEncoder(w)
+ typedec = vom.NewTypeDecoder(r)
typedec.Start()
defer typedec.Stop()
}
@@ -274,16 +224,16 @@
name := fmt.Sprintf("[%d]:%+v,%+v", n, test.In, test.Want)
var (
- encoder *Encoder
- decoder *Decoder
+ encoder *vom.Encoder
+ decoder *vom.Decoder
buf bytes.Buffer
)
if withTypeEncoderDecoder {
- encoder = NewEncoderWithTypeEncoder(&buf, typeenc)
- decoder = NewDecoderWithTypeDecoder(&buf, typedec)
+ encoder = vom.NewEncoderWithTypeEncoder(&buf, typeenc)
+ decoder = vom.NewDecoderWithTypeDecoder(&buf, typedec)
} else {
- encoder = NewEncoder(&buf)
- decoder = NewDecoder(&buf)
+ encoder = vom.NewEncoder(&buf)
+ decoder = vom.NewDecoder(&buf)
}
if err := encoder.Encode(test.In); err != nil {
@@ -374,6 +324,14 @@
return err
}
+func hex2Bin(t *testing.T, hex string) []byte {
+ var bin string
+ if _, err := fmt.Sscanf(hex, "%x", &bin); err != nil {
+ t.Fatalf("error converting %q to binary: %v", hex, err)
+ }
+ return []byte(bin)
+}
+
// Test that no EOF is returned from Decode() if the type stream finished before the value stream.
func TestTypeStreamEndsFirst(t *testing.T) {
hexversion := "81"
@@ -384,10 +342,10 @@
binvalue := string(hex2Bin(t, hexvalue))
// Ensure EOF isn't returned if the type decode stream ends first
tr := newExtractErrReader(strings.NewReader(binversion + bintype))
- typedec := NewTypeDecoder(tr)
+ typedec := vom.NewTypeDecoder(tr)
typedec.Start()
wr := newWaitingReader(strings.NewReader(binversion + binvalue))
- decoder := NewDecoderWithTypeDecoder(wr, typedec)
+ decoder := vom.NewDecoderWithTypeDecoder(wr, typedec)
var v interface{}
go func() {
if tr.WaitForError() == nil {
@@ -415,9 +373,9 @@
binversion := string(hex2Bin(t, hexversion))
binvalue := string(hex2Bin(t, hexvalue))
// Ensure EOF isn't returned if the type decode stream ends first
- typedec := NewTypeDecoder(&errorReader{})
+ typedec := vom.NewTypeDecoder(&errorReader{})
typedec.Start()
- decoder := NewDecoderWithTypeDecoder(strings.NewReader(binversion+binvalue), typedec)
+ decoder := vom.NewDecoderWithTypeDecoder(strings.NewReader(binversion+binvalue), typedec)
var v interface{}
if err := decoder.Decode(&v); err == nil {
t.Errorf("expected error in decode, but got none")
@@ -428,7 +386,7 @@
// in a deadlock.
func TestFuzzTypeDecodeDeadlock(t *testing.T) {
var v interface{}
- d := NewDecoder(strings.NewReader("\x81\x30"))
+ d := vom.NewDecoder(strings.NewReader("\x81\x30"))
// Before the fix, this line caused a deadlock and panic.
d.Decode(&v)
return
@@ -438,7 +396,7 @@
// panic over in package vdl.
func TestFuzzVdlPanic(t *testing.T) {
var v interface{}
- d := NewDecoder(strings.NewReader("\x81S*\x00\x00$000000000000000000000000000000000000\x01*\xe1U(\x05\x00 00000000000000000000000000000000\x01*\x02+\xe1"))
+ d := vom.NewDecoder(strings.NewReader("\x81S*\x00\x00$000000000000000000000000000000000000\x01*\xe1U(\x05\x00 00000000000000000000000000000000\x01*\x02+\xe1"))
// Before this fix this line caused a panic.
d.Decode(&v)
return
@@ -487,7 +445,7 @@
// Test that input found by go-fuzz cannot cause a stack overflow.
func TestFuzzDecodeOverflow(t *testing.T) {
var v interface{}
- d := NewDecoder(strings.NewReader("\x81\x51\x04\x03\x01\x29\xe1"))
+ d := vom.NewDecoder(strings.NewReader("\x81\x51\x04\x03\x01\x29\xe1"))
// Before the fix, this line caused a stack overflow. After the fix, we
// expect an error.
@@ -499,7 +457,7 @@
// Test that input discovered by go-fuzz does not result in a hang anymore.
func TestFuzzTypeDecodeHang(t *testing.T) {
var v interface{}
- d := NewDecoder(strings.NewReader(
+ d := vom.NewDecoder(strings.NewReader(
"\x81W&\x03\x00 v.io/v23/vom/t" +
"estdata/types.Rec4\x01)" +
"\xe1U&\x03\x00 v.io/v23/vom/t" +
@@ -530,14 +488,14 @@
X []byte
B bar
}
- encoded, err := Encode(foo{"hello", 42, 3.14159265359, []byte{1, 2, 3, 4, 5, 6}, bar{[]int64{0}}})
+ encoded, err := vom.Encode(foo{"hello", 42, 3.14159265359, []byte{1, 2, 3, 4, 5, 6}, bar{[]int64{0}}})
if err != nil {
t.Fatalf("Encode failed: %v", err)
}
for x := 0; x < len(encoded)-1; x++ {
var f interface{}
- if err := Decode(encoded[:x], &f); err == nil {
- t.Errorf("Encode did not fail with x=%d", x)
+ if err := vom.Decode(encoded[:x], &f); err == nil {
+ t.Errorf("Decode did not fail with x=%d", x)
}
}
}
diff --git a/vom/decoder_util.go b/vom/decoder_util.go
new file mode 100644
index 0000000..01753a9
--- /dev/null
+++ b/vom/decoder_util.go
@@ -0,0 +1,412 @@
+// 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.
+
+package vom
+
+import (
+ "v.io/v23/vdl"
+ "v.io/v23/verror"
+)
+
+var (
+ errDecodeZeroTypeID = verror.Register(pkgPath+".errDecodeZeroTypeID", verror.NoRetry, "{1:}{2:} vom: zero type id{:_}")
+ errIndexOutOfRange = verror.Register(pkgPath+".errIndexOutOfRange", verror.NoRetry, "{1:}{2:} vom: index out of range{:_}")
+ errLeftOverBytes = verror.Register(pkgPath+".errLeftOverBytes", verror.NoRetry, "{1:}{2:} vom: {3} leftover bytes{:_}")
+ errUnexpectedControlByte = verror.Register(pkgPath+".errUnexpectedControlByte", verror.NoRetry, "{1:}{2:} vom: unexpected control byte {3}{:_}")
+ errDecodeValueUnhandledType = verror.Register(pkgPath+".errDecodeValueUnhandledType", verror.NoRetry, "{1:}{2:} vom: decodeValue unhandled type {3}{:_}")
+ errIgnoreValueUnhandledType = verror.Register(pkgPath+".errIgnoreValueUnhandledType", verror.NoRetry, "{1:}{2:} vom: ignoreValue unhandled type {3}{:_}")
+ errInvalidTypeIdIndex = verror.Register(pkgPath+".errInvalidTypeIdIndex", verror.NoRetry, "{1:}{2:} vom: value referenced invalid index into type id table {:_}")
+ errInvalidAnyIndex = verror.Register(pkgPath+".errInvalidAnyIndex", verror.NoRetry, "{1:}{2:} vom: value referenced invalid index into anyLen table {:_}")
+)
+
+func (d *decoder81) decodeTypeDefs() error {
+ for {
+ typeNext, err := d.typeIsNext()
+ if err != nil {
+ return err
+ }
+ if !typeNext {
+ return nil
+ }
+ if err := d.typeDec.readSingleType(); err != nil {
+ return err
+ }
+ }
+}
+
+// peekValueByteLen returns the byte length of the next value.
+func (d *decoder81) peekValueByteLen(tt *vdl.Type) (int, error) {
+ if hasChunkLen(tt) {
+ // Use the explicit message length.
+ return d.buf.lim, nil
+ }
+ // No explicit message length, but the length can be computed.
+ switch {
+ case tt.Kind() == vdl.Array && tt.IsBytes():
+ // Byte arrays are exactly their length and encoded with 1-byte header.
+ return tt.Len() + 1, nil
+ case tt.Kind() == vdl.String || tt.IsBytes():
+ // Strings and byte lists are encoded with a length header.
+ strlen, bytelen, err := binaryPeekUint(d.buf)
+ switch {
+ case err != nil:
+ return 0, err
+ case strlen > maxBinaryMsgLen:
+ return 0, verror.New(errMsgLen, nil, maxBinaryMsgLen)
+ }
+ return int(strlen) + bytelen, nil
+ default:
+ // Must be a primitive, which is encoded as an underlying uint.
+ return binaryPeekUintByteLen(d.buf)
+ }
+}
+
+func (d *decoder81) decodeRaw(tt *vdl.Type, valLen int, raw *RawBytes) error {
+ raw.Version = d.buf.version
+ raw.Type = tt
+ raw.Data = make([]byte, valLen)
+ if err := d.buf.ReadIntoBuf(raw.Data); err != nil {
+ return err
+ }
+ refTypeLen := len(d.refTypes.tids)
+ if cap(raw.RefTypes) >= refTypeLen {
+ raw.RefTypes = raw.RefTypes[:refTypeLen]
+ } else {
+ raw.RefTypes = make([]*vdl.Type, refTypeLen)
+ }
+ for i, tid := range d.refTypes.tids {
+ var err error
+ if raw.RefTypes[i], err = d.typeDec.lookupType(tid); err != nil {
+ return err
+ }
+ }
+ raw.AnyLengths = d.refAnyLens.lens
+ return nil
+}
+
+func (d *decoder81) readAnyHeader() (*vdl.Type, int, error) {
+ // Handle WireCtrlNil.
+ switch ok, err := binaryDecodeControlOnly(d.buf, WireCtrlNil); {
+ case err != nil:
+ return nil, 0, err
+ case ok:
+ return nil, 0, nil // nil any
+ }
+ // Read the index of the referenced type id.
+ typeIndex, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return nil, 0, err
+ }
+ var tid TypeId
+ if d.buf.version == Version80 {
+ tid = TypeId(typeIndex)
+ } else if tid, err = d.refTypes.ReferencedTypeId(typeIndex); err != nil {
+ return nil, 0, err
+ }
+ // Look up the referenced type id.
+ ttElem, err := d.typeDec.lookupType(tid)
+ if err != nil {
+ return nil, 0, err
+ }
+ var anyLen int
+ if d.buf.version != Version80 {
+ // Read and lookup the index of the any byte length. Reference the any len,
+ // even if it isn't used, to report missing references.
+ lenIndex, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return nil, 0, err
+ }
+ if anyLen, err = d.refAnyLens.ReferencedAnyLen(lenIndex); err != nil {
+ return nil, 0, err
+ }
+ }
+ return ttElem, anyLen, nil
+}
+
+func (d *decoder81) skipValue(tt *vdl.Type) error {
+ if tt.IsBytes() {
+ len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
+ if err != nil {
+ return err
+ }
+ return d.buf.Skip(len)
+ }
+ switch kind := tt.Kind(); kind {
+ case vdl.Bool:
+ return d.buf.Skip(1)
+ case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64, vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64, vdl.Float32, vdl.Float64, vdl.Enum, vdl.TypeObject:
+ // The underlying encoding of all these types is based on uint.
+ return binarySkipUint(d.buf)
+ case vdl.String:
+ return binarySkipString(d.buf)
+ case vdl.Array, vdl.List, vdl.Set, vdl.Map:
+ len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
+ if err != nil {
+ return err
+ }
+ for ix := 0; ix < len; ix++ {
+ if kind == vdl.Set || kind == vdl.Map {
+ if err := d.skipValue(tt.Key()); err != nil {
+ return err
+ }
+ }
+ if kind == vdl.Array || kind == vdl.List || kind == vdl.Map {
+ if err := d.skipValue(tt.Elem()); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ case vdl.Struct:
+ // Loop through decoding the 0-based field index and corresponding field.
+ for {
+ switch ok, err := binaryDecodeControlOnly(d.buf, WireCtrlEnd); {
+ case err != nil:
+ return err
+ case ok:
+ return nil // end of struct
+ }
+ switch index, err := binaryDecodeUint(d.buf); {
+ case err != nil:
+ return err
+ case index >= uint64(tt.NumField()):
+ return verror.New(errIndexOutOfRange, nil)
+ default:
+ ttfield := tt.Field(int(index))
+ if err := d.skipValue(ttfield.Type); err != nil {
+ return err
+ }
+ }
+ }
+ case vdl.Union:
+ switch index, err := binaryDecodeUint(d.buf); {
+ case err != nil:
+ return err
+ case index >= uint64(tt.NumField()):
+ return verror.New(errIndexOutOfRange, nil)
+ default:
+ ttfield := tt.Field(int(index))
+ return d.skipValue(ttfield.Type)
+ }
+ case vdl.Optional:
+ // Read the WireCtrlNil code, but if it's not WireCtrlNil we need to keep
+ // the buffer as-is, since it's the first byte of the value, which may
+ // itself be another control code.
+ switch ctrl, err := binaryPeekControl(d.buf); {
+ case err != nil:
+ return err
+ case ctrl == WireCtrlNil:
+ d.buf.SkipAvailable(1) // nil optional
+ return nil
+ default:
+ return d.skipValue(tt.Elem()) // non-nil optional
+ }
+ case vdl.Any:
+ switch ok, err := binaryDecodeControlOnly(d.buf, WireCtrlNil); {
+ case err != nil:
+ return err
+ case ok:
+ return nil // nil any
+ }
+ switch index, err := binaryDecodeUint(d.buf); {
+ case err != nil:
+ return err
+ default:
+ tid, err := d.refTypes.ReferencedTypeId(index)
+ if err != nil {
+ return err
+ }
+ ttElem, err := d.typeDec.lookupType(tid)
+ if err != nil {
+ return err
+ }
+ return d.skipValue(ttElem)
+ }
+ default:
+ return verror.New(errIgnoreValueUnhandledType, nil, tt)
+ }
+}
+
+func (d *decoder81) nextMessage() (TypeId, error) {
+ if leftover := d.buf.RemoveLimit(); leftover > 0 {
+ return 0, verror.New(errLeftOverBytes, nil, leftover)
+ }
+ // Decode version byte, if not already decoded.
+ if d.buf.version == 0 {
+ version, err := d.buf.ReadByte()
+ if err != nil {
+ return 0, verror.New(errEndedBeforeVersionByte, nil, err)
+ }
+ d.buf.version = Version(version)
+ if !isAllowedVersion(d.buf.version) {
+ return 0, verror.New(errBadVersionByte, nil, d.buf.version)
+ }
+ }
+ // Decode the next message id.
+ incomplete, err := binaryDecodeControlOnly(d.buf, WireCtrlTypeIncomplete)
+ if err != nil {
+ return 0, err
+ }
+ mid, err := binaryDecodeInt(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ if incomplete {
+ if mid >= 0 {
+ // TypeIncomplete must be followed with a type message.
+ return 0, verror.New(errInvalid, nil)
+ }
+ d.flag = d.flag.Set(decFlagTypeIncomplete)
+ } else if mid < 0 {
+ d.flag = d.flag.Clear(decFlagTypeIncomplete)
+ }
+ // TODO(toddw): Clean up the logic below.
+ var tid TypeId
+ var hasAny, hasTypeObject, hasLength bool
+ switch {
+ case mid < 0:
+ tid = TypeId(-mid)
+ hasLength = true
+ hasAny = false
+ hasTypeObject = false
+ case mid > 0:
+ tid = TypeId(mid)
+ t, err := d.typeDec.lookupType(tid)
+ if err != nil {
+ return 0, err
+ }
+ hasLength = hasChunkLen(t)
+ hasAny = containsAny(t)
+ hasTypeObject = containsTypeObject(t)
+ default:
+ return 0, verror.New(errDecodeZeroTypeID, nil)
+ }
+
+ if (hasAny || hasTypeObject) && d.buf.version != Version80 {
+ l, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ for i := 0; i < int(l); i++ {
+ refId, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ d.refTypes.AddTypeID(TypeId(refId))
+ }
+ }
+ if hasAny && d.buf.version != Version80 {
+ l, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ for i := 0; i < int(l); i++ {
+ refAnyLen, err := binaryDecodeLen(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ d.refAnyLens.AddAnyLen(refAnyLen)
+ }
+ }
+
+ if hasLength {
+ chunkLen, err := binaryDecodeUint(d.buf)
+ if err != nil {
+ return 0, err
+ }
+ d.buf.SetLimit(int(chunkLen))
+ }
+
+ return tid, nil
+}
+
+func (d *decoder81) typeIsNext() (bool, error) {
+ if d.buf.version == 0 {
+ version, err := d.buf.ReadByte()
+ if err != nil {
+ return false, verror.New(errEndedBeforeVersionByte, nil, err)
+ }
+ d.buf.version = Version(version)
+ if !isAllowedVersion(d.buf.version) {
+ return false, verror.New(errBadVersionByte, nil, d.buf.version)
+ }
+ }
+ switch ctrl, err := binaryPeekControl(d.buf); {
+ case err != nil:
+ return false, err
+ case ctrl == WireCtrlTypeIncomplete:
+ return true, nil
+ case ctrl != 0:
+ return false, verror.New(errBadControlCode, nil, ctrl)
+ }
+ mid, _, err := binaryPeekInt(d.buf)
+ if err != nil {
+ return false, err
+ }
+ return mid < 0, nil
+}
+
+func (d *decoder81) endMessage() error {
+ if leftover := d.buf.RemoveLimit(); leftover > 0 {
+ return verror.New(errLeftOverBytes, nil, leftover)
+ }
+ if err := d.refTypes.Reset(); err != nil {
+ return err
+ }
+ if err := d.refAnyLens.Reset(); err != nil {
+ return err
+ }
+ return nil
+}
+
+type referencedTypes struct {
+ tids []TypeId
+ marker int
+}
+
+func (refTypes *referencedTypes) Reset() (err error) {
+ refTypes.tids = refTypes.tids[:0]
+ refTypes.marker = 0
+ return
+}
+
+func (refTypes *referencedTypes) AddTypeID(tid TypeId) {
+ refTypes.tids = append(refTypes.tids, tid)
+}
+
+func (refTypes *referencedTypes) ReferencedTypeId(index uint64) (TypeId, error) {
+ if index >= uint64(len(refTypes.tids)) {
+ return 0, verror.New(errInvalidTypeIdIndex, nil)
+ }
+ return refTypes.tids[index], nil
+}
+
+func (refTypes *referencedTypes) Mark() {
+ refTypes.marker = len(refTypes.tids)
+}
+
+type referencedAnyLens struct {
+ lens []int
+ marker int
+}
+
+func (refAnys *referencedAnyLens) Reset() (err error) {
+ refAnys.lens = refAnys.lens[:0]
+ return
+}
+
+func (refAnys *referencedAnyLens) AddAnyLen(len int) {
+ refAnys.lens = append(refAnys.lens, len)
+}
+
+func (refAnys *referencedAnyLens) ReferencedAnyLen(index uint64) (int, error) {
+ if index >= uint64(len(refAnys.lens)) {
+ return 0, verror.New(errInvalidAnyIndex, nil)
+ }
+ return refAnys.lens[index], nil
+}
+
+func (refAnys *referencedAnyLens) Mark() {
+ refAnys.marker = len(refAnys.lens)
+}
diff --git a/vom/encoder.go b/vom/encoder.go
index 6af4f26..d3e3697 100644
--- a/vom/encoder.go
+++ b/vom/encoder.go
@@ -90,7 +90,7 @@
return e
}
-func newXEncoderForRawBytes(w io.Writer) *encoder81 {
+func newEncoderForRawBytes(w io.Writer) *encoder81 {
// RawBytes doesn't need the types to be encoded, since it holds the in-memory
// representation. We still need a type encoder to collect the unique types,
// but we give it a dummy encoder that doesn't have any state set up.
diff --git a/vom/encoder_test.go b/vom/encoder_test.go
index 9baaf9a..ea9e846 100644
--- a/vom/encoder_test.go
+++ b/vom/encoder_test.go
@@ -2,95 +2,75 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package vom
+package vom_test
import (
"bytes"
"fmt"
"testing"
- "v.io/v23/vom/testdata/data81"
- "v.io/v23/vom/testdata/types"
+ "v.io/v23/vdl"
+ "v.io/v23/vom"
+ "v.io/v23/vom/vomtest"
)
-func TestEncoder81(t *testing.T) {
- testEncoder(t, Version81, data81.Tests)
-}
-
-func testEncoder(t *testing.T, version Version, tests []types.TestCase) {
- for _, test := range tests {
- name := test.Name + " [vdl.Value]"
- testEncode(t, version, name, test.Value, test.HexVersion+test.HexType+test.HexValue)
- name = test.Name + " [vdl.Value] (with TypeEncoder)"
- testEncodeWithTypeEncoder(t, version, name, test.Value, test.HexVersion, test.HexType, test.HexValue)
-
- // Convert into Go value for the rest of our tests.
- goValue, err := toGoValue(test.Value)
+func TestEncoder(t *testing.T) {
+ for _, test := range vomtest.AllPass() {
+ testEncoder(t, "[go value]", test, test.Value.Interface())
+ vv, err := vdl.ValueFromReflect(test.Value)
if err != nil {
- t.Errorf("%s: %v", name, err)
+ t.Errorf("%s: ValueFromReflect failed: %v", test.Name(), err)
continue
}
-
- name = test.Name + " [go value]"
- testEncode(t, version, name, goValue, test.HexVersion+test.HexType+test.HexValue)
- name = test.Name + " [go value] (with TypeEncoder)"
- testEncodeWithTypeEncoder(t, version, name, goValue, test.HexVersion, test.HexType, test.HexValue)
+ testEncoder(t, "[vdl.Value]", test, vv)
}
}
-func testEncode(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 := NewVersionedEncoder(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 = VersionedEncode(version, value)
- if err != nil {
- t.Errorf("%s: Encode(%#v) failed: %v", name, value, err)
- return
- }
+func testEncoder(t *testing.T, pre string, test vomtest.Entry, value interface{}) {
+ // Test vom.NewEncoder.
+ {
+ var buf bytes.Buffer
+ name := fmt.Sprintf("%s %s", pre, test.Name())
+ enc := vom.NewVersionedEncoder(test.Version, &buf)
+ if err := enc.Encode(value); err != nil {
+ t.Errorf("%s: Encode failed: %v", name, err)
+ return
}
- got, want := fmt.Sprintf("%x", bin), hex
- match, err := matchHexPat(got, want)
+ if got, want := buf.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s\nGOT %x\nWANT %x", name, got, want)
+ return
+ }
+ }
+ // Test vom.NewEncoderWithTypeEncoder.
+ {
+ var buf, bufT bytes.Buffer
+ name := fmt.Sprintf("%s (with TypeEncoder) %s", pre, test.Name())
+ encT := vom.NewVersionedTypeEncoder(test.Version, &bufT)
+ enc := vom.NewVersionedEncoderWithTypeEncoder(test.Version, &buf, encT)
+ if err := enc.Encode(value); err != nil {
+ t.Errorf("%s: Encode failed: %v", name, err)
+ return
+ }
+ if got, want := bufT.Bytes(), test.TypeBytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s TYPE\nGOT %x\nWANT %x", name, got, want)
+ return
+ }
+ if got, want := buf.Bytes(), test.ValueBytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s VALUE\nGOT %x\nWANT %x", name, got, want)
+ return
+ }
+ }
+ // Test single-shot vom.Encode.
+ {
+ name := fmt.Sprintf("%s (single-shot) %s", pre, test.Name())
+ buf, err := vom.VersionedEncode(test.Version, value)
if err != nil {
- t.Error(err)
+ t.Errorf("%s: Encode failed: %v", name, err)
+ return
}
- if !match {
- t.Errorf("%s: Encode(%#v)\nGOT %s\nWANT %s", name, value, got, want)
+ if got, want := buf, test.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s\nGOT %x\nWANT %x", name, got, want)
+ return
}
}
}
-
-func testEncodeWithTypeEncoder(t *testing.T, version Version, name string, value interface{}, hexversion, hextype, hexvalue string) {
- var buf, typebuf bytes.Buffer
- typeenc := NewVersionedTypeEncoder(version, &typebuf)
- encoder := NewVersionedEncoderWithTypeEncoder(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)
- }
-}
diff --git a/vom/fuzzdump_test.go b/vom/fuzzdump_test.go
index 0cd9399..1b6e908 100644
--- a/vom/fuzzdump_test.go
+++ b/vom/fuzzdump_test.go
@@ -4,7 +4,7 @@
// +build fuzzdump
-package vom
+package vom_test
import (
"crypto/sha1"
@@ -13,8 +13,7 @@
"os"
"testing"
- "v.io/v23/vom/testdata/data80"
- "v.io/v23/vom/testdata/data81"
+ "v.io/v23/vom/vomtest"
)
// Add the vom tests as binary examples for the fuzzer.
@@ -27,34 +26,16 @@
seen := make(map[[sha1.Size]byte]bool)
- tests := append(data80.Tests, data81.Tests...)
-
- for _, test := range tests {
- binversion, err := binFromHexPat(test.HexVersion)
- if err != nil {
- t.Fatal(err)
- }
-
- bintype, err := binFromHexPat(test.HexType)
- if err != nil {
- t.Fatal(err)
- }
-
- binvalue, err := binFromHexPat(test.HexValue)
- if err != nil {
- t.Fatal(err)
- }
-
- data := []byte(binversion + bintype + binvalue)
+ for _, test := range vomtest.AllPass() {
+ data := test.Bytes()
hash := sha1.Sum(data)
-
if seen[hash] {
continue
}
seen[hash] = true
- fn := fmt.Sprintf("fuzz-workdir/corpus/%x", hash)
- if err := ioutil.WriteFile(fn, data, 0644); err != nil {
+ name := fmt.Sprintf("fuzz-workdir/corpus/%x", hash)
+ if err := ioutil.WriteFile(name, data, 0644); err != nil {
t.Fatal(err)
}
}
diff --git a/vom/new_raw_bytes_test.go b/vom/new_raw_bytes_test.go
deleted file mode 100644
index 3631218..0000000
--- a/vom/new_raw_bytes_test.go
+++ /dev/null
@@ -1,138 +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.
-
-package vom_test
-
-import (
- "bytes"
- "io"
- "testing"
-
- "v.io/v23/vdl"
- "v.io/v23/vom"
- "v.io/v23/vom/vomtest"
-)
-
-// TODO(toddw): Clean up these tests.
-
-func TestXRawBytesDecodeEncode(t *testing.T) {
- for _, test := range vomtest.AllPass() {
- // Interleaved
- rb := vom.RawBytes{}
- interleavedReader := bytes.NewReader(test.Bytes())
- if err := vom.NewDecoder(interleavedReader).Decode(&rb); err != nil {
- t.Errorf("%s: decode failed: %v", test.Name(), err)
- continue
- }
- if _, err := interleavedReader.ReadByte(); err != io.EOF {
- t.Errorf("%s: expected EOF, but got %v", test.Name(), err)
- continue
- }
-
- var out bytes.Buffer
- enc := vom.NewVersionedEncoder(test.Version, &out)
- if err := enc.Encode(&rb); err != nil {
- t.Errorf("%s: encode failed: %v\nRawBytes: %v", test.Name(), err, rb)
- continue
- }
- if got, want := out.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("%s\nGOT %x\nWANT %x", test.Name(), got, want)
- }
-
- // Split type and value stream.
- rb = vom.RawBytes{}
- typeReader := bytes.NewReader(test.TypeBytes())
- typeDec := vom.NewTypeDecoder(typeReader)
- typeDec.Start()
- defer typeDec.Stop()
- valueReader := bytes.NewReader(test.ValueBytes())
- if err := vom.NewDecoderWithTypeDecoder(valueReader, typeDec).Decode(&rb); err != nil {
- t.Errorf("%s: decode failed: %v", test.Name(), err)
- continue
- }
- if _, err := typeReader.ReadByte(); err != io.EOF {
- t.Errorf("%s: type reader got %v, want EOF", test.Name(), err)
- continue
- }
- if _, err := valueReader.ReadByte(); err != io.EOF {
- t.Errorf("%s: value reader got %v, want EOF", test.Name(), err)
- continue
- }
-
- out.Reset()
- var typeOut bytes.Buffer
- typeEnc := vom.NewVersionedTypeEncoder(test.Version, &typeOut)
- enc = vom.NewVersionedEncoderWithTypeEncoder(test.Version, &out, typeEnc)
- if err := enc.Encode(&rb); err != nil {
- t.Errorf("%s: encode failed: %v\nRawBytes: %v", test.Name(), err, rb)
- continue
- }
- if got, want := typeOut.Bytes(), test.TypeBytes(); !bytes.Equal(got, want) {
- t.Errorf("%s: type bytes\nGOT %x\nWANT %x", test.Name(), got, want)
- }
- if got, want := out.Bytes(), test.ValueBytes(); !bytes.Equal(got, want) {
- t.Errorf("%s: value bytes\nGOT %x\nWANT %x", test.Name(), got, want)
- }
- }
-}
-
-func TestXRawBytesToFromValue(t *testing.T) {
- for _, test := range vomtest.AllPass() {
- rb, err := vom.XRawBytesFromValue(test.Value.Interface())
- if err != nil {
- t.Errorf("%v %s: XRawBytesFromValue failed: %v", test.Version, test.Name(), err)
- continue
- }
- var vv *vdl.Value
- if err := rb.ToValue(&vv); err != nil {
- t.Errorf("%v %s: rb.ToValue failed: %v", test.Version, test.Name(), err)
- continue
- }
- if got, want := vv, vdl.ValueOf(test.Value.Interface()); !vdl.EqualValue(got, want) {
- t.Errorf("%v %s\nGOT %v\nWANT %v", test.Version, test.Name(), got, want)
- }
- }
-}
-
-func TestXRawBytesDecoder(t *testing.T) {
- for _, test := range vomtest.AllPass() {
- tt, err := vdl.TypeFromReflect(test.Value.Type())
- if err != nil {
- t.Errorf("%s: TypeFromReflect failed: %v", test.Name(), err)
- continue
- }
- in, err := vom.XRawBytesFromValue(test.Value.Interface())
- if err != nil {
- t.Errorf("%s: XRawBytesFromValue failed: %v", test.Name(), err)
- continue
- }
- out := vdl.ZeroValue(tt)
- if err := out.VDLRead(in.Decoder()); err != nil {
- t.Errorf("%s: VDLRead failed: %v", test.Name(), err)
- continue
- }
- if got, want := out, vdl.ValueOf(test.Value.Interface()); !vdl.EqualValue(got, want) {
- t.Errorf("%s\nGOT %v\nWANT %v", test.Name(), got, want)
- }
- }
-}
-
-func TestXRawBytesWriter(t *testing.T) {
- for _, test := range vomtest.AllPass() {
- var buf bytes.Buffer
- enc := vom.NewEncoder(&buf)
- rb, err := vom.XRawBytesFromValue(test.Value.Interface())
- if err != nil {
- t.Errorf("%s: XRawBytesFromValue failed: %v", test.Name(), err)
- continue
- }
- if err := rb.VDLWrite(enc.Encoder()); err != nil {
- t.Errorf("%s: VDLWrite failed: %v", test.Name(), err)
- continue
- }
- if got, want := buf.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("%s\nGOT %x\nWANT %x", test.Name(), got, want)
- }
- }
-}
diff --git a/vom/new_transcode_test.go b/vom/new_transcode_test.go
deleted file mode 100644
index 9c00e7d..0000000
--- a/vom/new_transcode_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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 vom_test
-
-import (
- "bytes"
- "testing"
-
- "v.io/v23/vdl"
- "v.io/v23/vom"
- "v.io/v23/vom/vomtest"
-)
-
-func TestTranscodeXDecoderToXEncoder(t *testing.T) {
- for _, test := range vomtest.AllPass() {
- var buf bytes.Buffer
- enc := vom.NewVersionedEncoder(test.Version, &buf)
- dec := vom.NewDecoder(bytes.NewReader(test.Bytes()))
- if err := vdl.Transcode(enc.Encoder(), dec.Decoder()); err != nil {
- t.Errorf("%s: Transcode failed: %v", test.Name(), err)
- continue
- }
- if got, want := buf.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("%s\nGOT %x\nWANT %x", test.Name(), got, want)
- }
- }
-}
-
-// TODO(bprosnitz) This is probably not the right place for this test.
-func TestTranscodeVDLValueToXEncoder(t *testing.T) {
- for _, test := range vomtest.AllPass() {
- var buf bytes.Buffer
- enc := vom.NewEncoder(&buf)
- if err := vdl.Write(enc.Encoder(), test.Value.Interface()); err != nil {
- t.Errorf("%s: vdl.Write failed: %v", test.Name(), err)
- continue
- }
- if got, want := buf.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("%s\nGOT %x\nWANT %x", test.Name(), got, want)
- }
- }
-}
diff --git a/vom/raw_bytes.go b/vom/raw_bytes.go
index b0794e0..e611a90 100644
--- a/vom/raw_bytes.go
+++ b/vom/raw_bytes.go
@@ -46,23 +46,6 @@
return rb, nil
}
-// XRawBytesFromValue is a mirror of RawBytesFromValue, but uses the new
-// XEncoder and XDecoder. It's only exposed to allow for testing.
-//
-// TODO(toddw): Remove this function after we've switched to XEncoder and
-// XDecoder, and thus it's no longer needed.
-func XRawBytesFromValue(value interface{}) (*RawBytes, error) {
- data, err := VersionedEncode(DefaultVersion, value)
- if err != nil {
- return nil, err
- }
- rb := new(RawBytes)
- if err := Decode(data, rb); err != nil {
- return nil, err
- }
- return rb, nil
-}
-
// String outputs a string representation of RawBytes of the form
// RawBytes{Version81, int8, RefTypes{bool, string}, AnyLengths{4}, fa0e9dcc}
func (rb *RawBytes) String() string {
@@ -164,7 +147,7 @@
}
// Slowpath: the bytes are not available, we must encode new bytes.
var buf bytes.Buffer
- enc := newXEncoderForRawBytes(&buf)
+ enc := newEncoderForRawBytes(&buf)
if err := vdl.Transcode(enc, dec); err != nil {
return err
}
diff --git a/vom/raw_bytes_test.go b/vom/raw_bytes_test.go
index 0bd4016..532a12d 100644
--- a/vom/raw_bytes_test.go
+++ b/vom/raw_bytes_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package vom
+package vom_test
import (
"bytes"
@@ -11,8 +11,8 @@
"testing"
"v.io/v23/vdl"
- "v.io/v23/vom/testdata/data81"
- "v.io/v23/vom/testdata/types"
+ "v.io/v23/vom"
+ "v.io/v23/vom/vomtest"
)
type testUint64 uint64
@@ -22,27 +22,27 @@
}
type structAny struct {
- X *RawBytes
+ X *vom.RawBytes
}
type structAnyAndTypes struct {
A *vdl.Type
- B *RawBytes
+ B *vom.RawBytes
C *vdl.Type
- D *RawBytes
+ D *vom.RawBytes
}
// Test various combinations of having/not having any and type object.
var rawBytesTestCases = []struct {
name string
goValue interface{}
- rawBytes RawBytes
+ rawBytes vom.RawBytes
}{
{
name: "testUint64(99)",
goValue: testUint64(99),
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(testUint64(0)),
Data: []byte{0x63},
},
@@ -50,8 +50,8 @@
{
name: "typeobject(int32)",
goValue: vdl.Int32Type,
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(vdl.Int32Type),
RefTypes: []*vdl.Type{vdl.Int32Type},
Data: []byte{0x00},
@@ -60,31 +60,31 @@
{
name: "structTypeObject{typeobject(int32)}",
goValue: structTypeObject{vdl.Int32Type},
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(structTypeObject{}),
RefTypes: []*vdl.Type{vdl.Int32Type},
- Data: []byte{0x00, 0x00, WireCtrlEnd},
+ Data: []byte{0x00, 0x00, vom.WireCtrlEnd},
},
},
{
name: `structAnyAndTypes{typeobject(int32), true, typeobject(bool), "abc"}`,
goValue: structAnyAndTypes{
vdl.Int32Type,
- &RawBytes{
- Version: DefaultVersion,
+ &vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.BoolType,
Data: []byte{0x01},
},
vdl.BoolType,
- &RawBytes{
- Version: DefaultVersion,
+ &vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(""),
Data: []byte{0x03, 0x61, 0x62, 0x63},
},
},
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(structAnyAndTypes{}),
RefTypes: []*vdl.Type{vdl.Int32Type, vdl.BoolType, vdl.StringType},
AnyLengths: []int{1, 4},
@@ -93,15 +93,15 @@
0x01, 0x01, 0x00, 0x01, // B
0x02, 0x01, // C
0x03, 0x02, 0x01, 0x03, 0x61, 0x62, 0x63, // D
- WireCtrlEnd,
+ vom.WireCtrlEnd,
},
},
},
{
name: "large message", // to test that multibyte length is encoded properly
goValue: makeLargeBytes(1000),
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.ListType(vdl.ByteType),
Data: append([]byte{0xfe, 0x03, 0xe8}, makeLargeBytes(1000)...),
},
@@ -109,8 +109,8 @@
{
name: "*vdl.Value",
goValue: vdl.ValueOf(uint16(5)),
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.Uint16Type,
Data: []byte{0x05},
},
@@ -118,23 +118,23 @@
{
name: "*vdl.Value - top level any",
goValue: vdl.ValueOf([]interface{}{uint16(5)}).Index(0),
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.Uint16Type,
Data: []byte{0x05},
},
},
{
name: "any(nil)",
- goValue: &RawBytes{
- Version: DefaultVersion,
+ goValue: &vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.AnyType,
- Data: []byte{WireCtrlNil},
+ Data: []byte{vom.WireCtrlNil},
},
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.AnyType,
- Data: []byte{WireCtrlNil},
+ Data: []byte{vom.WireCtrlNil},
},
},
}
@@ -149,13 +149,13 @@
func TestDecodeToRawBytes(t *testing.T) {
for _, test := range rawBytesTestCases {
- bytes, err := Encode(test.goValue)
+ bytes, err := vom.Encode(test.goValue)
if err != nil {
t.Errorf("%s: Encode failed: %v", test.name, err)
continue
}
- var rb RawBytes
- if err := Decode(bytes, &rb); err != nil {
+ var rb vom.RawBytes
+ if err := vom.Decode(bytes, &rb); err != nil {
t.Errorf("%s: Decode failed: %v", test.name, err)
continue
}
@@ -167,12 +167,12 @@
func TestEncodeFromRawBytes(t *testing.T) {
for _, test := range rawBytesTestCases {
- fullBytes, err := Encode(test.goValue)
+ fullBytes, err := vom.Encode(test.goValue)
if err != nil {
t.Errorf("%s: Encode goValue failed: %v", test.name, err)
continue
}
- fullBytesFromRaw, err := Encode(&test.rawBytes)
+ fullBytesFromRaw, err := vom.Encode(&test.rawBytes)
if err != nil {
t.Errorf("%s: Encode RawBytes failed: %v", test.name, err)
continue
@@ -187,13 +187,13 @@
var rawBytesWrappedTestCases = []struct {
name string
goValue interface{}
- rawBytes RawBytes
+ rawBytes vom.RawBytes
}{
{
name: "testUint64(99)",
goValue: testUint64(99),
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(testUint64(0)),
RefTypes: []*vdl.Type{vdl.TypeOf(testUint64(0))},
AnyLengths: []int{1},
@@ -203,8 +203,8 @@
{
name: "typeobject(int32)",
goValue: vdl.Int32Type,
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(vdl.Int32Type),
RefTypes: []*vdl.Type{vdl.TypeObjectType, vdl.Int32Type},
AnyLengths: []int{1},
@@ -214,8 +214,8 @@
{
name: "structTypeObject{typeobject(int32)}",
goValue: structTypeObject{vdl.Int32Type},
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(structTypeObject{}),
RefTypes: []*vdl.Type{vdl.TypeOf(structTypeObject{}), vdl.Int32Type},
AnyLengths: []int{3},
@@ -226,20 +226,20 @@
name: `structAnyAndTypes{typeobject(int32), true, typeobject(bool), "abc"}`,
goValue: structAnyAndTypes{
vdl.Int32Type,
- &RawBytes{
- Version: DefaultVersion,
+ &vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.BoolType,
Data: []byte{0x01},
},
vdl.BoolType,
- &RawBytes{
- Version: DefaultVersion,
+ &vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(""),
Data: []byte{0x03, 0x61, 0x62, 0x63},
},
},
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.TypeOf(structAnyAndTypes{}),
RefTypes: []*vdl.Type{vdl.TypeOf(structAnyAndTypes{}), vdl.Int32Type, vdl.BoolType, vdl.StringType},
AnyLengths: []int{16, 1, 4},
@@ -255,8 +255,8 @@
{
name: "large message", // to test that multibyte length is encoded properly
goValue: makeLargeBytes(1000),
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.ListType(vdl.ByteType),
RefTypes: []*vdl.Type{vdl.ListType(vdl.ByteType)},
AnyLengths: []int{0x3eb},
@@ -266,8 +266,8 @@
{
name: "*vdl.Value",
goValue: vdl.ValueOf(uint16(5)),
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.Uint16Type,
RefTypes: []*vdl.Type{vdl.Uint16Type},
AnyLengths: []int{1},
@@ -277,8 +277,8 @@
{
name: "*vdl.Value - top level any",
goValue: vdl.ValueOf([]interface{}{uint16(5)}).Index(0),
- rawBytes: RawBytes{
- Version: DefaultVersion,
+ rawBytes: vom.RawBytes{
+ Version: vom.DefaultVersion,
Type: vdl.Uint16Type,
RefTypes: []*vdl.Type{vdl.Uint16Type},
AnyLengths: []int{1},
@@ -290,13 +290,13 @@
func TestWrappedRawBytes(t *testing.T) {
for i, test := range rawBytesWrappedTestCases {
unwrapped := rawBytesTestCases[i]
- wrappedBytes, err := Encode(structAny{&unwrapped.rawBytes})
+ wrappedBytes, err := vom.Encode(structAny{&unwrapped.rawBytes})
if err != nil {
t.Errorf("%s: Encode failed: %v", test.name, err)
continue
}
var any structAny
- if err := Decode(wrappedBytes, &any); err != nil {
+ if err := vom.Decode(wrappedBytes, &any); err != nil {
t.Errorf("%s: Decode failed: %v", test.name, err)
continue
}
@@ -308,11 +308,11 @@
func TestEncodeNilRawBytes(t *testing.T) {
// Top-level
- want, err := Encode(vdl.ZeroValue(vdl.AnyType))
+ want, err := vom.Encode(vdl.ZeroValue(vdl.AnyType))
if err != nil {
t.Fatal(err)
}
- got, err := Encode((*RawBytes)(nil))
+ got, err := vom.Encode((*vom.RawBytes)(nil))
if err != nil {
t.Fatal(err)
}
@@ -320,11 +320,11 @@
t.Errorf("top-level\nGOT %x\nWANT %x", got, want)
}
// Within an object.
- want, err = Encode([]*vdl.Value{vdl.ZeroValue(vdl.AnyType)})
+ want, err = vom.Encode([]*vdl.Value{vdl.ZeroValue(vdl.AnyType)})
if err != nil {
t.Fatal(err)
}
- got, err = Encode([]*RawBytes{nil})
+ got, err = vom.Encode([]*vom.RawBytes{nil})
if err != nil {
t.Fatal(err)
}
@@ -333,115 +333,8 @@
}
}
-func TestRawBytesDecodeEncode(t *testing.T) {
- versions := []struct {
- Version Version
- Tests []types.TestCase
- }{
- {Version81, data81.Tests},
- }
- for _, testVersion := range versions {
- for _, test := range testVersion.Tests {
- // Interleaved
- rb := RawBytes{}
- interleavedReader := bytes.NewReader(hex2Bin(t, test.Hex))
- if err := NewDecoder(interleavedReader).Decode(&rb); err != nil {
- t.Errorf("unexpected error decoding %s: %v", test.Name, err)
- continue
- }
- if _, err := interleavedReader.ReadByte(); err != io.EOF {
- t.Errorf("expected EOF, but got %v", err)
- continue
- }
-
- var out bytes.Buffer
- enc := NewVersionedEncoder(testVersion.Version, &out)
- if err := enc.Encode(&rb); err != nil {
- t.Errorf("unexpected error encoding raw bytes %v in test %s: %v", rb, test.Name, err)
- continue
- }
- if !bytes.Equal(out.Bytes(), hex2Bin(t, test.Hex)) {
- t.Errorf("got bytes: %x but expected %s", out.Bytes(), test.Hex)
- }
-
- // Split type and value stream.
- rb = RawBytes{}
- typeReader := bytes.NewReader(hex2Bin(t, test.HexVersion+test.HexType))
- typeDec := NewTypeDecoder(typeReader)
- typeDec.Start()
- defer typeDec.Stop()
- valueReader := bytes.NewReader(hex2Bin(t, test.HexVersion+test.HexValue))
- if err := NewDecoderWithTypeDecoder(valueReader, typeDec).Decode(&rb); err != nil {
- t.Errorf("unexpected error decoding %s: %v", test.Name, err)
- continue
- }
- if test.HexType != "" {
- // If HexType is empty, then the type stream will just have the version byte that won't be read, so ignore
- // that case.
- if _, err := typeReader.ReadByte(); err != io.EOF {
- t.Errorf("in type reader expected EOF, but got %v", err)
- continue
- }
- }
- if _, err := valueReader.ReadByte(); err != io.EOF {
- t.Errorf("in value reader expected EOF, but got %v", err)
- continue
- }
-
- out.Reset()
- var typeOut bytes.Buffer
- typeEnc := NewVersionedTypeEncoder(testVersion.Version, &typeOut)
- enc = NewVersionedEncoderWithTypeEncoder(testVersion.Version, &out, typeEnc)
- if err := enc.Encode(&rb); err != nil {
- t.Errorf("unexpected error encoding raw value %v in test %s: %v", rb, test.Name, err)
- continue
- }
- expectedType := test.HexVersion + test.HexType
- if expectedType == "81" || expectedType == "82" {
- expectedType = ""
- }
- if !bytes.Equal(typeOut.Bytes(), hex2Bin(t, expectedType)) {
- t.Errorf("got type bytes: %x but expected %s", typeOut.Bytes(), expectedType)
- }
- if !bytes.Equal(out.Bytes(), hex2Bin(t, test.HexVersion+test.HexValue)) {
- t.Errorf("got value bytes: %x but expected %s", out.Bytes(), test.HexVersion+test.HexValue)
- }
- }
- }
-}
-
-func TestRawBytesToFromValue(t *testing.T) {
- versions := []struct {
- Version Version
- Tests []types.TestCase
- }{
- {Version81, data81.Tests},
- }
- for _, testVersion := range versions {
- for _, test := range testVersion.Tests {
- rb, err := RawBytesFromValue(test.Value)
- if err != nil {
- t.Fatalf("%v %s: error in RawBytesFromValue %v", testVersion.Version, test.Name, err)
- }
- var vv *vdl.Value
- if err := rb.ToValue(&vv); err != nil {
- t.Fatalf("%v %s: error in rb.ToValue %v", testVersion.Version, test.Name, err)
- }
- if test.Name == "any(nil)" {
- // Skip any(nil)
- // TODO(bprosnitz) any(nil) results in two different nil representations. This shouldn't be the case.
- continue
- }
- if got, want := vv, test.Value; !vdl.EqualValue(got, want) {
- t.Errorf("%v %s: error in converting to and from raw value. got %v, but want %v", testVersion.Version,
- test.Name, got, want)
- }
- }
- }
-}
-
func TestVdlTypeOfRawBytes(t *testing.T) {
- if got, want := vdl.TypeOf(&RawBytes{}), vdl.AnyType; got != want {
+ if got, want := vdl.TypeOf(&vom.RawBytes{}), vdl.AnyType; got != want {
t.Errorf("got %v, want %v", got, want)
}
}
@@ -458,7 +351,7 @@
func TestConvertRawBytes(t *testing.T) {
for _, test := range rawBytesTestCases {
- var rb *RawBytes
+ var rb *vom.RawBytes
if err := vdl.Convert(&rb, test.goValue); err != nil {
t.Errorf("%s: Convert failed: %v", test.name, err)
}
@@ -490,7 +383,7 @@
// are more ids in the encoder/decoder.
func TestReusedDecoderEncoderRawBytes(t *testing.T) {
var buf bytes.Buffer
- enc := NewEncoder(&buf)
+ enc := vom.NewEncoder(&buf)
if err := enc.Encode(structAnyInterface{int64(4)}); err != nil {
t.Fatalf("error on encode: %v", err)
}
@@ -498,7 +391,7 @@
t.Fatalf("error on encode: %v", err)
}
- dec := NewDecoder(bytes.NewReader(buf.Bytes()))
+ dec := vom.NewDecoder(bytes.NewReader(buf.Bytes()))
var x structAny
if err := dec.Decode(&x); err != nil {
t.Fatalf("error on decode: %v", err)
@@ -525,12 +418,12 @@
func TestRawBytesString(t *testing.T) {
tests := []struct {
- input *RawBytes
+ input *vom.RawBytes
expected string
}{
{
- input: &RawBytes{
- Version81,
+ input: &vom.RawBytes{
+ vom.Version81,
vdl.Int8Type,
[]*vdl.Type{vdl.BoolType, vdl.StringType},
[]int{4},
@@ -538,8 +431,8 @@
expected: "RawBytes{Version81, int8, RefTypes{bool, string}, AnyLengths{4}, fa0e9dcc}",
},
{
- input: &RawBytes{
- Version81,
+ input: &vom.RawBytes{
+ vom.Version81,
vdl.Int8Type,
[]*vdl.Type{vdl.BoolType},
[]int{},
@@ -547,8 +440,8 @@
expected: "RawBytes{Version81, int8, RefTypes{bool}, fa0e9dcc}",
},
{
- input: &RawBytes{
- Version81,
+ input: &vom.RawBytes{
+ vom.Version81,
vdl.Int8Type,
nil,
nil,
@@ -564,10 +457,10 @@
}
func TestRawBytesVDLType(t *testing.T) {
- if got, want := vdl.TypeOf(RawBytes{}), vdl.AnyType; got != want {
+ if got, want := vdl.TypeOf(vom.RawBytes{}), vdl.AnyType; got != want {
t.Errorf("vom.RawBytes{} got %v, want %v", got, want)
}
- if got, want := vdl.TypeOf((*RawBytes)(nil)), vdl.AnyType; got != want {
+ if got, want := vdl.TypeOf((*vom.RawBytes)(nil)), vdl.AnyType; got != want {
t.Errorf("vom.RawBytes{} got %v, want %v", got, want)
}
}
@@ -586,20 +479,20 @@
// of dealing with the format for describing types with any in the
// value message header.
var typeBuf, buf bytes.Buffer
- typeEnc := NewTypeEncoder(&typeBuf)
- if err := NewEncoderWithTypeEncoder(&buf, typeEnc).Encode(simpleStruct{5}); err != nil {
+ typeEnc := vom.NewTypeEncoder(&typeBuf)
+ if err := vom.NewEncoderWithTypeEncoder(&buf, typeEnc).Encode(simpleStruct{5}); err != nil {
t.Fatalf("failure when preparing type message bytes: %v", t)
}
var inputMessage []byte = typeBuf.Bytes()
- inputMessage = append(inputMessage, byte(WireIdFirstUserType*2)) // New value message tid
- inputMessage = append(inputMessage, 8) // Message length
+ inputMessage = append(inputMessage, byte(vom.WireIdFirstUserType*2)) // New value message tid
+ inputMessage = append(inputMessage, 8) // Message length
// non-vom bytes (invalid because there is no struct field at index 10)
dataPortion := []byte{10, 20, 30, 40, 50, 60, 70, 80}
inputMessage = append(inputMessage, dataPortion...)
// Now ensure decoding into a RawBytes works correctly.
- var rb *RawBytes
- if err := Decode(inputMessage, &rb); err != nil {
+ var rb *vom.RawBytes
+ if err := vom.Decode(inputMessage, &rb); err != nil {
t.Fatalf("error decoding %x into a RawBytes: %v", inputMessage, err)
}
if got, want := rb.Type, vdl.TypeOf(simpleStruct{}); got != want {
@@ -610,7 +503,7 @@
}
// Now re-encode and ensure that we get the original bytes.
- encoded, err := Encode(rb)
+ encoded, err := vom.Encode(rb)
if err != nil {
t.Fatalf("error encoding RawBytes %v: %v", rb, err)
}
@@ -619,40 +512,123 @@
}
}
-func TestRawBytesDecoder(t *testing.T) {
- regex := filterRegex(t)
- for _, test := range data81.Tests {
- if !regex.MatchString(test.Name) {
+func TestRawBytesDecodeEncode(t *testing.T) {
+ for _, test := range vomtest.AllPass() {
+ // Interleaved
+ rb := vom.RawBytes{}
+ interleavedReader := bytes.NewReader(test.Bytes())
+ if err := vom.NewDecoder(interleavedReader).Decode(&rb); err != nil {
+ t.Errorf("%s: decode failed: %v", test.Name(), err)
+ continue
+ }
+ if _, err := interleavedReader.ReadByte(); err != io.EOF {
+ t.Errorf("%s: expected EOF, but got %v", test.Name(), err)
continue
}
- in := RawBytesOf(test.Value)
- out := vdl.ZeroValue(test.Value.Type())
- if err := out.VDLRead(in.Decoder()); err != nil {
- t.Errorf("%s: error in ValueRead: %v", test.Name, err)
+ var out bytes.Buffer
+ enc := vom.NewVersionedEncoder(test.Version, &out)
+ if err := enc.Encode(&rb); err != nil {
+ t.Errorf("%s: encode failed: %v\nRawBytes: %v", test.Name(), err, rb)
continue
}
- if !vdl.EqualValue(test.Value, out) {
- t.Errorf("%s: got %v, want %v", test.Name, out, test.Value)
+ if got, want := out.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s\nGOT %x\nWANT %x", test.Name(), got, want)
+ }
+
+ // Split type and value stream.
+ rb = vom.RawBytes{}
+ typeReader := bytes.NewReader(test.TypeBytes())
+ typeDec := vom.NewTypeDecoder(typeReader)
+ typeDec.Start()
+ defer typeDec.Stop()
+ valueReader := bytes.NewReader(test.ValueBytes())
+ if err := vom.NewDecoderWithTypeDecoder(valueReader, typeDec).Decode(&rb); err != nil {
+ t.Errorf("%s: decode failed: %v", test.Name(), err)
+ continue
+ }
+ if _, err := typeReader.ReadByte(); err != io.EOF {
+ t.Errorf("%s: type reader got %v, want EOF", test.Name(), err)
+ continue
+ }
+ if _, err := valueReader.ReadByte(); err != io.EOF {
+ t.Errorf("%s: value reader got %v, want EOF", test.Name(), err)
+ continue
+ }
+
+ out.Reset()
+ var typeOut bytes.Buffer
+ typeEnc := vom.NewVersionedTypeEncoder(test.Version, &typeOut)
+ enc = vom.NewVersionedEncoderWithTypeEncoder(test.Version, &out, typeEnc)
+ if err := enc.Encode(&rb); err != nil {
+ t.Errorf("%s: encode failed: %v\nRawBytes: %v", test.Name(), err, rb)
+ continue
+ }
+ if got, want := typeOut.Bytes(), test.TypeBytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s: type bytes\nGOT %x\nWANT %x", test.Name(), got, want)
+ }
+ if got, want := out.Bytes(), test.ValueBytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s: value bytes\nGOT %x\nWANT %x", test.Name(), got, want)
+ }
+ }
+}
+
+func TestRawBytesToFromValue(t *testing.T) {
+ for _, test := range vomtest.AllPass() {
+ rb, err := vom.RawBytesFromValue(test.Value.Interface())
+ if err != nil {
+ t.Errorf("%v %s: RawBytesFromValue failed: %v", test.Version, test.Name(), err)
+ continue
+ }
+ var vv *vdl.Value
+ if err := rb.ToValue(&vv); err != nil {
+ t.Errorf("%v %s: rb.ToValue failed: %v", test.Version, test.Name(), err)
+ continue
+ }
+ if got, want := vv, vdl.ValueOf(test.Value.Interface()); !vdl.EqualValue(got, want) {
+ t.Errorf("%v %s\nGOT %v\nWANT %v", test.Version, test.Name(), got, want)
+ }
+ }
+}
+
+func TestRawBytesDecoder(t *testing.T) {
+ for _, test := range vomtest.AllPass() {
+ tt, err := vdl.TypeFromReflect(test.Value.Type())
+ if err != nil {
+ t.Errorf("%s: TypeFromReflect failed: %v", test.Name(), err)
+ continue
+ }
+ in, err := vom.RawBytesFromValue(test.Value.Interface())
+ if err != nil {
+ t.Errorf("%s: RawBytesFromValue failed: %v", test.Name(), err)
+ continue
+ }
+ out := vdl.ZeroValue(tt)
+ if err := out.VDLRead(in.Decoder()); err != nil {
+ t.Errorf("%s: VDLRead failed: %v", test.Name(), err)
+ continue
+ }
+ if got, want := out, vdl.ValueOf(test.Value.Interface()); !vdl.EqualValue(got, want) {
+ t.Errorf("%s\nGOT %v\nWANT %v", test.Name(), got, want)
}
}
}
func TestRawBytesWriter(t *testing.T) {
- regex := filterRegex(t)
- for _, test := range data81.Tests {
- if !regex.MatchString(test.Name) {
- continue
- }
-
+ for _, test := range vomtest.AllPass() {
var buf bytes.Buffer
- enc := NewEncoder(&buf)
- if err := RawBytesOf(test.Value).VDLWrite(enc.Encoder()); err != nil {
- t.Errorf("%s: error in transcode: %v", test.Name, err)
+ enc := vom.NewEncoder(&buf)
+ rb, err := vom.RawBytesFromValue(test.Value.Interface())
+ if err != nil {
+ t.Errorf("%s: RawBytesFromValue failed: %v", test.Name(), err)
continue
}
- if got, want := buf.Bytes(), hex2Bin(t, test.Hex); !bytes.Equal(got, want) {
- t.Errorf("%s: got %x, want %x", test.Name, got, want)
+ if err := rb.VDLWrite(enc.Encoder()); err != nil {
+ t.Errorf("%s: VDLWrite failed: %v", test.Name(), err)
+ continue
+ }
+ if got, want := buf.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s\nGOT %x\nWANT %x", test.Name(), got, want)
}
}
}
diff --git a/vom/transcode_test.go b/vom/transcode_test.go
index a91bafb..aa00779 100644
--- a/vom/transcode_test.go
+++ b/vom/transcode_test.go
@@ -2,70 +2,43 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package vom
+package vom_test
import (
"bytes"
- "flag"
- "regexp"
"testing"
"v.io/v23/vdl"
- "v.io/v23/vom/testdata/data81"
+ "v.io/v23/vom"
+ "v.io/v23/vom/vomtest"
)
-var filter *string = flag.String("testdata-filter", "", "regex for allowed testdata tests")
-
-func init() {
- flag.Parse()
-}
-
-func filterRegex(t *testing.T) *regexp.Regexp {
- pattern := ".*" + *filter + ".*"
- regex, err := regexp.Compile(pattern)
- if err != nil {
- t.Fatalf("invalid regex filter %q", *filter)
- }
- return regex
-}
-
-func TestTranscodeXDecoderToXEncoder(t *testing.T) {
- regex := filterRegex(t)
- for _, test := range data81.Tests {
- if !regex.MatchString(test.Name) {
- continue
- }
-
- inBytes := hex2Bin(t, test.Hex)
+func TestTranscodeDecoderToEncoder(t *testing.T) {
+ for _, test := range vomtest.AllPass() {
var buf bytes.Buffer
- enc := NewEncoder(&buf)
- dec := NewDecoder(bytes.NewReader(inBytes))
+ enc := vom.NewVersionedEncoder(test.Version, &buf)
+ dec := vom.NewDecoder(bytes.NewReader(test.Bytes()))
if err := vdl.Transcode(enc.Encoder(), dec.Decoder()); err != nil {
- t.Errorf("%s: error in transcode: %v", test.Name, err)
+ t.Errorf("%s: Transcode failed: %v", test.Name(), err)
continue
}
- if got, want := buf.Bytes(), inBytes; !bytes.Equal(got, want) {
- t.Errorf("%s: got %x, want %x", test.Name, got, want)
+ if got, want := buf.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s\nGOT %x\nWANT %x", test.Name(), got, want)
}
}
}
// TODO(bprosnitz) This is probably not the right place for this test.
-func TestTranscodeVDLValueToXEncoder(t *testing.T) {
- regex := filterRegex(t)
- for _, test := range data81.Tests {
- if !regex.MatchString(test.Name) {
- continue
- }
-
+func TestTranscodeVDLValueToEncoder(t *testing.T) {
+ for _, test := range vomtest.AllPass() {
var buf bytes.Buffer
- enc := NewEncoder(&buf)
- if err := test.Value.VDLWrite(enc.Encoder()); err != nil {
- t.Errorf("%s: error in transcode: %v", test.Name, err)
+ enc := vom.NewEncoder(&buf)
+ if err := vdl.Write(enc.Encoder(), test.Value.Interface()); err != nil {
+ t.Errorf("%s: vdl.Write failed: %v", test.Name(), err)
continue
}
- if got, want := buf.Bytes(), hex2Bin(t, test.Hex); !bytes.Equal(got, want) {
- t.Errorf("%s: got %x, want %x", test.Name, got, want)
+ if got, want := buf.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("%s\nGOT %x\nWANT %x", test.Name(), got, want)
}
}
}
diff --git a/vom/xdecoder.go b/vom/xdecoder.go
deleted file mode 100644
index bcbb932..0000000
--- a/vom/xdecoder.go
+++ /dev/null
@@ -1,800 +0,0 @@
-// 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 vom
-
-import (
- "errors"
- "fmt"
- "io"
- "math"
- "os"
-
- "v.io/v23/vdl"
- "v.io/v23/verror"
-)
-
-const (
- // IEEE 754 represents float64 using 52 bits to represent the mantissa, with
- // an extra implied leading bit. That gives us 53 bits to store integers
- // without overflow - i.e. [0, (2^53)-1]. And since 2^53 is a small power of
- // two, it can also be stored without loss via mantissa=1 exponent=53. Thus
- // we have our max and min values. Ditto for float32, which uses 23 bits with
- // an extra implied leading bit.
- float64MaxInt = (1 << 53)
- float64MinInt = -(1 << 53)
- float32MaxInt = (1 << 24)
- float32MinInt = -(1 << 24)
-)
-
-var (
- errEmptyDecoderStack = errors.New("vom: empty decoder stack")
- errReadRawBytesAlreadyStarted = errors.New("vom: read into vom.RawBytes after StartValue called")
- errReadRawBytesFromNonAny = errors.New("vom: read into vom.RawBytes only supported on any values")
-)
-
-// This is only used for debugging; add this as the first line of NewDecoder to
-// dump formatted vom bytes to stdout:
-// r = teeDump(r)
-func teeDump(r io.Reader) io.Reader {
- return io.TeeReader(r, NewDumper(NewDumpWriter(os.Stdout)))
-}
-
-// Decoder manages the receipt and unmarshalling of typed values from the other
-// side of a connection.
-type Decoder struct {
- dec decoder81
-}
-
-type decoder81 struct {
- buf *decbuf
- flag decFlag
- stack []decStackEntry
- refTypes referencedTypes
- refAnyLens referencedAnyLens
- typeDec *TypeDecoder
-}
-
-type decStackEntry struct {
- Type *vdl.Type // Type of the value that we're decoding.
- Index int // Index of the key, elem or field.
- LenHint int // Length of the value, or -1 for unknown.
- NextEntryType *vdl.Type // type for NextEntryValue* methods
- Flag decStackFlag // properties of this stack entry
- NextEntryData nextEntryData // properties of the next entry
-}
-
-// decFlag holds properties of the decoder.
-type decFlag uint
-
-const (
- decFlagIgnoreNextStartValue decFlag = 0x1 // ignore the next call to StartValue
- decFlagIsParentBytes decFlag = 0x2 // parent type is []byte or [N]byte
- decFlagTypeIncomplete decFlag = 0x4 // type has dependencies on unsent types
- decFlagSeparateTypeDec decFlag = 0x8 // type decoder is separate
-
- // In FinishValue we need to clear both of these bits.
- decFlagFinishValue decFlag = decFlagIgnoreNextStartValue | decFlagIsParentBytes
-)
-
-func (f decFlag) Set(bits decFlag) decFlag { return f | bits }
-func (f decFlag) Clear(bits decFlag) decFlag { return f &^ bits }
-
-func (f decFlag) IgnoreNextStartValue() bool { return f&decFlagIgnoreNextStartValue != 0 }
-func (f decFlag) IsParentBytes() bool { return f&decFlagIsParentBytes != 0 }
-func (f decFlag) TypeIncomplete() bool { return f&decFlagTypeIncomplete != 0 }
-func (f decFlag) SeparateTypeDec() bool { return f&decFlagSeparateTypeDec != 0 }
-
-// decStackFlag holds type or value properties of the stack entry.
-type decStackFlag uint
-
-const (
- decStackFlagIsMapKey decStackFlag = 0x1 // key or elem for dfsNextType
- decStackFlagIsAny decStackFlag = 0x2 // the static type is Any
- decStackFlagIsOptional decStackFlag = 0x4 // the static type is Optional
- decStackFlagFastRead decStackFlag = 0x8 // subtypes use ReadValue fastpath
-)
-
-func (f decStackFlag) FlipIsMapKey() decStackFlag { return f ^ decStackFlagIsMapKey }
-func (f decStackFlag) IsMapKey() bool { return f&decStackFlagIsMapKey != 0 }
-func (f decStackFlag) IsAny() bool { return f&decStackFlagIsAny != 0 }
-func (f decStackFlag) IsOptional() bool { return f&decStackFlagIsOptional != 0 }
-func (f decStackFlag) FastRead() bool { return f&decStackFlagFastRead != 0 }
-
-// NewDecoder returns a new Decoder that reads from the given reader. The
-// Decoder understands all formats generated by the Encoder.
-func NewDecoder(r io.Reader) *Decoder {
- buf := newDecbuf(r)
- typeDec := newTypeDecoderInternal(buf)
- return &Decoder{decoder81{
- buf: buf,
- typeDec: typeDec,
- }}
-}
-
-// NewDecoderWithTypeDecoder returns a new Decoder that reads from the given
-// reader. Types are decoded separately through the typeDec.
-func NewDecoderWithTypeDecoder(r io.Reader, typeDec *TypeDecoder) *Decoder {
- return &Decoder{decoder81{
- buf: newDecbuf(r),
- typeDec: typeDec,
- flag: decFlagSeparateTypeDec,
- }}
-}
-
-// Decoder returns d as a vdl.Decoder.
-func (d *Decoder) Decoder() vdl.Decoder {
- return &d.dec
-}
-
-// Decode reads the next value and stores it in value v. The type of v need not
-// exactly match the type of the originally encoded value; decoding succeeds as
-// long as the values are convertible.
-func (d *Decoder) Decode(v interface{}) error {
- return vdl.Read(&d.dec, v)
-}
-
-func (d *decoder81) IgnoreNextStartValue() {
- d.flag = d.flag.Set(decFlagIgnoreNextStartValue)
-}
-
-func (d *decoder81) decodeWireType(wt *wireType) (TypeId, error) {
- // 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 __VDLRead_wireType method.
- tid, err := d.nextMessage()
- if err != nil {
- return 0, err
- }
- d.stack = append(d.stack, decStackEntry{
- Type: wireTypeType,
- Index: -1,
- LenHint: 1, // wireType is a union
- })
- d.flag = d.flag.Set(decFlagIgnoreNextStartValue)
- if err := __VDLRead_wireType(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 *decoder81) readRawBytes(raw *RawBytes) error {
- if d.flag.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
- // already been decoded. So we simply disallow this from happening.
- return errReadRawBytesAlreadyStarted
- }
- tt, err := d.dfsNextType()
- if err != nil {
- return err
- }
- // Handle top-level values. All types of values are supported, since we can
- // simply copy the message bytes.
- if len(d.stack) == 0 {
- anyLen, err := d.peekValueByteLen(tt)
- if err != nil {
- return err
- }
- if err := d.decodeRaw(tt, anyLen, raw); err != nil {
- return err
- }
- return d.endMessage()
- }
- // Handle internal values. Only any values are supported at the moment, since
- // they come with a header that tells us the exact length to read.
- //
- // TODO(toddw): Handle other types, either by reading and skipping bytes based
- // on the type, or by falling back to a decode / re-encode slowpath.
- if tt.Kind() != vdl.Any {
- return errReadRawBytesFromNonAny
- }
- ttElem, anyLen, err := d.readAnyHeader()
- if err != nil {
- return err
- }
- 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.
- raw.Version = d.buf.version
- raw.Type = vdl.AnyType
- raw.RefTypes = nil
- raw.AnyLengths = nil
- raw.Data = []byte{WireCtrlNil}
- return nil
- }
- return d.decodeRaw(ttElem, anyLen, raw)
-}
-
-func (d *decoder81) StartValue(want *vdl.Type) error {
- if d.flag.IgnoreNextStartValue() {
- d.flag = d.flag.Clear(decFlagIgnoreNextStartValue)
- return nil
- }
- tt, err := d.dfsNextType()
- if err != nil {
- return err
- }
- tt, lenHint, flag, err := d.setupType(tt, want)
- if err != nil {
- return err
- }
- d.stack = append(d.stack, decStackEntry{
- Type: tt,
- Index: -1,
- LenHint: lenHint,
- Flag: flag,
- })
- return nil
-}
-
-func (d *decoder81) setupType(tt, want *vdl.Type) (_ *vdl.Type, lenHint int, flag decStackFlag, _ error) {
- // Handle any, which may be nil. We "dereference" non-nil any to the inner
- // type. If that happens to be an optional, it's handled below.
- if tt.Kind() == vdl.Any {
- flag |= decStackFlagIsAny
- var err error
- switch tt, _, err = d.readAnyHeader(); {
- case err != nil:
- return nil, 0, 0, err
- case tt == nil:
- tt = vdl.AnyType // nil any
- }
- }
- // Handle optional, which may be nil. Similar to any, we "dereference"
- // non-nil optional to the inner type, which is never allowed to be another
- // optional or any type.
- if tt.Kind() == vdl.Optional {
- flag |= decStackFlagIsOptional
- // Read the WireCtrlNil code, but if it's not WireCtrlNil we need to keep
- // the buffer as-is, since it's the first byte of the value, which may
- // itself be another control code.
- switch ctrl, err := binaryPeekControl(d.buf); {
- case err != nil:
- return nil, 0, 0, err
- case ctrl == WireCtrlNil:
- d.buf.SkipAvailable(1) // nil optional
- default:
- tt = tt.Elem() // non-nil optional
- }
- }
- // Check compatibility between the actual type and the want type. Since
- // compatibility applies to the entire static type, we only need to perform
- // this check for top-level decoded values, and subsequently for decoded any
- // values. We skip checking non-composite want types, since those will be
- // naturally caught by the Decode* calls anyways.
- if want != nil && (len(d.stack) == 0 || flag.IsAny()) {
- switch want.Kind() {
- case vdl.Optional, vdl.Array, vdl.List, vdl.Set, vdl.Map, vdl.Struct, vdl.Union:
- if tt == want {
- // Set FastRead flag, which will let us use the fastpath for ReadValue*
- // in common cases. We can only use this fastpath if tt and want are
- // identical, which ensures we don't need to perform any conversions.
- if !flag.IsAny() && isFastReadParent(tt) {
- flag |= decStackFlagFastRead
- }
- // Regardless of whether we can use the fastpath, there's no need to
- // check compatibility if tt and want are identical.
- } else {
- if !vdl.Compatible(tt, want) {
- return nil, 0, 0, errIncompatibleDecode(tt, want)
- }
- }
- }
- }
- // Initialize LenHint for composite types.
- switch tt.Kind() {
- case vdl.Array, vdl.List, vdl.Set, vdl.Map:
- // TODO(toddw): Handle sentry-terminated collections without a length hint.
- len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
- if err != nil {
- return nil, 0, 0, err
- }
- lenHint = len
- case vdl.Union:
- // Union shouldn't have a LenHint, but we abuse it in NextField as a
- // convenience for detecting when fields are done, so we initialize it here.
- // It has to be at least 1, since 0 will cause NextField to think that the
- // union field has already been decoded.
- lenHint = 1
- case vdl.Struct:
- // Struct shouldn't have a LenHint, but we abuse it in NextField as a
- // convenience for detecting when fields are done, so we initialize it here.
- lenHint = tt.NumField()
- default:
- lenHint = -1
- }
- if top := d.top(); top != nil && top.Type.IsBytes() {
- d.flag = d.flag.Set(decFlagIsParentBytes)
- } else {
- d.flag = d.flag.Clear(decFlagIsParentBytes)
- }
- return tt, lenHint, flag, nil
-}
-
-func errIncompatibleDecode(tt *vdl.Type, want interface{}) error {
- return fmt.Errorf("vom: incompatible decode from %v into %v", tt, want)
-}
-
-func (d *decoder81) FinishValue() error {
- d.flag = d.flag.Clear(decFlagFinishValue)
- stackTop := len(d.stack) - 1
- if stackTop == -1 {
- return errEmptyDecoderStack
- }
- d.stack = d.stack[:stackTop]
- if stackTop == 0 {
- return d.endMessage()
- }
- return nil
-}
-
-func (d *decoder81) top() *decStackEntry {
- if stackTop := len(d.stack) - 1; stackTop >= 0 {
- return &d.stack[stackTop]
- }
- return nil
-}
-
-// dfsNextType determines the type of the next value that we will decode, by
-// walking the static type in DFS order. To bootstrap we retrieve the top-level
-// type from the VOM value message.
-func (d *decoder81) dfsNextType() (*vdl.Type, error) {
- top := d.top()
- if top == nil {
- // Bootstrap: start decoding a new top-level value.
- if !d.flag.SeparateTypeDec() {
- if err := d.decodeTypeDefs(); err != nil {
- return nil, err
- }
- }
- tid, err := d.nextMessage()
- if err != nil {
- return nil, err
- }
- return d.typeDec.lookupType(tid)
- }
- // Return the next type from our composite types.
- tt := top.Type
- switch tt.Kind() {
- case vdl.Array, vdl.List:
- return tt.Elem(), nil
- case vdl.Set:
- return tt.Key(), nil
- case vdl.Map:
- top.Flag = top.Flag.FlipIsMapKey()
- if top.Flag.IsMapKey() {
- return tt.Key(), nil
- } else {
- return tt.Elem(), nil
- }
- case vdl.Union, vdl.Struct:
- return tt.Field(top.Index).Type, nil
- }
- return nil, fmt.Errorf("vom: can't StartValue on %v", tt)
-}
-
-func (d *decoder81) NextEntry() (bool, error) {
- // Our strategy is to increment top.Index until it reaches top.LenHint.
- // Currently the LenHint is always set, so it's stronger than a hint.
- //
- // TODO(toddw): Handle sentry-terminated collections without a LenHint.
- top := d.top()
- if top == nil {
- return false, errEmptyDecoderStack
- }
- // Increment index and check errors.
- top.Index++
- switch top.Type.Kind() {
- case vdl.Array, vdl.List, vdl.Set, vdl.Map:
- if top.Index > top.LenHint && top.LenHint >= 0 {
- return false, fmt.Errorf("vom: NextEntry called after done, stack: %+v", d.stack)
- }
- default:
- return false, fmt.Errorf("vom: NextEntry called on invalid type, stack: %+v", d.stack)
- }
- return top.Index == top.LenHint, nil
-}
-
-func (d *decoder81) NextField() (int, error) {
- top := d.top()
- if top == nil {
- return -1, errEmptyDecoderStack
- }
- // Increment index and check errors. Note that the actual top.Index is
- // decoded from the buf data stream; we use top.LenHint to help detect when
- // the fields are done, and to detect invalid calls after we're done.
- top.Index++
- switch top.Type.Kind() {
- case vdl.Union, vdl.Struct:
- if top.Index > top.LenHint {
- return -1, fmt.Errorf("vom: NextField called after done, stack: %+v", d.stack)
- }
- default:
- return -1, fmt.Errorf("vom: NextField called on invalid type, stack: %+v", d.stack)
- }
- var field int
- switch top.Type.Kind() {
- case vdl.Union:
- if top.Index == top.LenHint {
- // We know we're done since we set LenHint=Index+1 the first time around,
- // and we incremented the index above.
- return -1, nil
- }
- // Decode the union field index.
- switch index, err := binaryDecodeUint(d.buf); {
- case err != nil:
- return -1, err
- case index >= uint64(top.Type.NumField()):
- return -1, verror.New(errIndexOutOfRange, nil)
- default:
- // Set LenHint=Index+1 so that we'll know we're done next time around.
- field = int(index)
- top.Index = field
- top.LenHint = field + 1
- }
- case vdl.Struct:
- // Handle the end-of-struct sentry.
- switch ok, err := binaryDecodeControlOnly(d.buf, WireCtrlEnd); {
- case err != nil:
- return -1, err
- case ok:
- // Set Index=LenHint to ensure repeated calls will fail.
- top.Index = top.LenHint
- return -1, nil
- }
- // Decode the struct field index.
- switch index, err := binaryDecodeUint(d.buf); {
- case err != nil:
- return -1, err
- case index >= uint64(top.Type.NumField()):
- return -1, verror.New(errIndexOutOfRange, nil)
- default:
- field = int(index)
- top.Index = field
- }
- }
- return field, nil
-}
-
-func (d *decoder81) Type() *vdl.Type {
- if top := d.top(); top != nil {
- return top.Type
- }
- return nil
-}
-
-func (d *decoder81) IsAny() bool {
- if top := d.top(); top != nil {
- return top.Flag.IsAny()
- }
- return false
-}
-
-func (d *decoder81) IsOptional() bool {
- if top := d.top(); top != nil {
- return top.Flag.IsOptional()
- }
- return false
-}
-
-func (d *decoder81) IsNil() bool {
- if top := d.top(); top != nil {
- // Becuase of the "dereferencing" we do, the only time the type is any or
- // optional is when it's nil.
- return top.Type == vdl.AnyType || top.Type.Kind() == vdl.Optional
- }
- return false
-}
-
-func (d *decoder81) Index() int {
- if top := d.top(); top != nil {
- return top.Index
- }
- return -1
-}
-
-func (d *decoder81) LenHint() int {
- if top := d.top(); top != nil {
- // Note that union and struct shouldn't have a LenHint, but we abuse it in
- // NextField as a convenience for detecting when fields are done, so an
- // "arbitrary" value is returned here. Users shouldn't be looking at it for
- // union and struct anyways.
- return top.LenHint
- }
- return -1
-}
-
-func (d *decoder81) DecodeBool() (bool, error) {
- tt := d.Type()
- if tt == nil {
- return false, errEmptyDecoderStack
- }
- if tt.Kind() == vdl.Bool {
- return binaryDecodeBool(d.buf)
- }
- return false, errIncompatibleDecode(tt, "bool")
-}
-
-func (d *decoder81) DecodeString() (string, error) {
- tt := d.Type()
- if tt == nil {
- return "", errEmptyDecoderStack
- }
- switch tt.Kind() {
- case vdl.String:
- return binaryDecodeString(d.buf)
- case vdl.Enum:
- return d.binaryDecodeEnum(tt)
- }
- return "", errIncompatibleDecode(tt, "string")
-}
-
-func (d *decoder81) binaryDecodeEnum(tt *vdl.Type) (string, error) {
- index, err := binaryDecodeUint(d.buf)
- switch {
- case err != nil:
- return "", err
- case index >= uint64(tt.NumEnumLabel()):
- return "", fmt.Errorf("vom: enum index %d out of range, %v", index, tt)
- }
- return tt.EnumLabel(int(index)), nil
-}
-
-func (d *decoder81) binaryDecodeByte() (byte, error) {
- // Handle a special-case where normally single bytes are written out as
- // variable sized numbers, which use 2 bytes to encode bytes > 127. But each
- // byte contained in a list or array is written out as one byte. E.g.
- // byte(0x81) -> 0xFF81 : single byte with variable-size
- // []byte("\x81\x82") -> 0x028182 : each elem byte encoded as one byte
- if d.flag.IsParentBytes() {
- return d.buf.ReadByte()
- }
- x, err := binaryDecodeUint(d.buf)
- return byte(x), err
-}
-
-func (d *decoder81) DecodeUint(bitlen int) (uint64, error) {
- tt := d.Type()
- if tt == nil {
- return 0, errEmptyDecoderStack
- }
- return d.decodeUint(tt, uint(bitlen))
-}
-
-func (d *decoder81) decodeUint(tt *vdl.Type, ubitlen uint) (uint64, error) {
- const errFmt = "vom: conversion from %v into uint%d loses precision: %v"
- switch tt.Kind() {
- case vdl.Byte:
- x, err := d.binaryDecodeByte()
- if err != nil {
- return 0, err
- }
- return uint64(x), err
- case vdl.Uint16, vdl.Uint32, vdl.Uint64:
- x, err := binaryDecodeUint(d.buf)
- if err != nil {
- return 0, err
- }
- if shift := 64 - ubitlen; x != (x<<shift)>>shift {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return x, nil
- case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64:
- x, err := binaryDecodeInt(d.buf)
- if err != nil {
- return 0, err
- }
- ux := uint64(x)
- if shift := 64 - ubitlen; x < 0 || ux != (ux<<shift)>>shift {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return ux, nil
- case vdl.Float32, vdl.Float64:
- x, err := binaryDecodeFloat(d.buf)
- if err != nil {
- return 0, err
- }
- ux := uint64(x)
- if shift := 64 - ubitlen; x != float64(ux) || ux != (ux<<shift)>>shift {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return ux, nil
- }
- return 0, errIncompatibleDecode(tt, fmt.Sprintf("uint%d", ubitlen))
-}
-
-func (d *decoder81) DecodeInt(bitlen int) (int64, error) {
- tt := d.Type()
- if tt == nil {
- return 0, errEmptyDecoderStack
- }
- return d.decodeInt(tt, uint(bitlen))
-}
-
-func (d *decoder81) decodeInt(tt *vdl.Type, ubitlen uint) (int64, error) {
- const errFmt = "vom: conversion from %v into int%d loses precision: %v"
- switch tt.Kind() {
- case vdl.Byte:
- x, err := d.binaryDecodeByte()
- if err != nil {
- return 0, err
- }
- // The only case that fails is if we're converting byte(x) to int8, and x
- // uses more than 7 bits (i.e. is greater than 127).
- if ubitlen <= 8 && x > 0x7f {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return int64(x), nil
- case vdl.Uint16, vdl.Uint32, vdl.Uint64:
- x, err := binaryDecodeUint(d.buf)
- if err != nil {
- return 0, err
- }
- ix := int64(x)
- // The shift uses 65 since the topmost bit is the sign bit. I.e. 32 bit
- // numbers should be shifted by 33 rather than 32.
- if shift := 65 - ubitlen; ix < 0 || x != (x<<shift)>>shift {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return ix, nil
- case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64:
- x, err := binaryDecodeInt(d.buf)
- if err != nil {
- return 0, err
- }
- if shift := 64 - ubitlen; x != (x<<shift)>>shift {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return x, nil
- case vdl.Float32, vdl.Float64:
- x, err := binaryDecodeFloat(d.buf)
- if err != nil {
- return 0, err
- }
- ix := int64(x)
- if shift := 64 - ubitlen; x != float64(ix) || ix != (ix<<shift)>>shift {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return ix, nil
- }
- return 0, errIncompatibleDecode(tt, fmt.Sprintf("int%d", ubitlen))
-}
-
-func (d *decoder81) DecodeFloat(bitlen int) (float64, error) {
- tt := d.Type()
- if tt == nil {
- return 0, errEmptyDecoderStack
- }
- return d.decodeFloat(tt, uint(bitlen))
-}
-
-func (d *decoder81) decodeFloat(tt *vdl.Type, ubitlen uint) (float64, error) {
- const errFmt = "vom: conversion from %v into float%d loses precision: %v"
- switch tt.Kind() {
- case vdl.Byte:
- x, err := d.binaryDecodeByte()
- if err != nil {
- return 0, err
- }
- return float64(x), nil
- case vdl.Uint16, vdl.Uint32, vdl.Uint64:
- x, err := binaryDecodeUint(d.buf)
- if err != nil {
- return 0, err
- }
- var max uint64
- if ubitlen > 32 {
- max = float64MaxInt
- } else {
- max = float32MaxInt
- }
- if x > max {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return float64(x), nil
- case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64:
- x, err := binaryDecodeInt(d.buf)
- if err != nil {
- return 0, err
- }
- var min, max int64
- if ubitlen > 32 {
- min, max = float64MinInt, float64MaxInt
- } else {
- min, max = float32MinInt, float32MaxInt
- }
- if x < min || x > max {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return float64(x), nil
- case vdl.Float32, vdl.Float64:
- x, err := binaryDecodeFloat(d.buf)
- if err != nil {
- return 0, err
- }
- if ubitlen <= 32 && (x < -math.MaxFloat32 || x > math.MaxFloat32) {
- return 0, fmt.Errorf(errFmt, tt, ubitlen, x)
- }
- return x, nil
- }
- return 0, errIncompatibleDecode(tt, fmt.Sprintf("float%d", ubitlen))
-}
-
-func (d *decoder81) DecodeBytes(fixedLen int, v *[]byte) error {
- top := d.top()
- if top == nil {
- return errEmptyDecoderStack
- }
- tt := top.Type
- if !tt.IsBytes() {
- return vdl.DecodeConvertedBytes(d, fixedLen, v)
- }
- return d.decodeBytes(tt, top.LenHint, fixedLen, v)
-}
-
-func (d *decoder81) decodeBytes(tt *vdl.Type, lenHint, fixedLen int, v *[]byte) error {
- switch {
- case lenHint == -1:
- return fmt.Errorf("vom: LenHint is currently required, %v", tt)
- case fixedLen >= 0 && fixedLen != lenHint:
- return fmt.Errorf("vom: got %d bytes, want fixed len %d, %v", lenHint, fixedLen, tt)
- case lenHint == 0:
- *v = nil
- return nil
- case fixedLen >= 0:
- // Only re-use the existing buffer if we're filling in an array. This
- // sacrifices some performance, but also avoids bugs when repeatedly
- // decoding into the same value.
- *v = (*v)[:lenHint]
- default:
- *v = make([]byte, lenHint)
- }
- return d.buf.ReadIntoBuf(*v)
-}
-
-func (d *decoder81) DecodeTypeObject() (*vdl.Type, error) {
- tt := d.Type()
- if tt == nil {
- return nil, errEmptyDecoderStack
- }
- if tt != vdl.TypeObjectType {
- return nil, errIncompatibleDecode(tt, "typeobject")
- }
- return d.binaryDecodeType()
-}
-
-func (d *decoder81) binaryDecodeType() (*vdl.Type, error) {
- typeIndex, err := binaryDecodeUint(d.buf)
- if err != nil {
- return nil, err
- }
- tid, err := d.refTypes.ReferencedTypeId(typeIndex)
- if err != nil {
- return nil, err
- }
- return d.typeDec.lookupType(tid)
-}
-
-func (d *decoder81) SkipValue() error {
- tt, err := d.dfsNextType()
- if err != nil {
- return err
- }
- if len(d.stack) == 0 {
- // Handle top-level values. It's easy to determine the byte length of the
- // value, so we can just skip the bytes.
- valueLen, err := d.peekValueByteLen(tt)
- if err != nil {
- return err
- }
- if err := d.buf.Skip(valueLen); err != nil {
- return err
- }
- return d.endMessage()
- }
- return d.skipValue(tt)
-}
diff --git a/vom/xdecoder_test.go b/vom/xdecoder_test.go
deleted file mode 100644
index 90fd789..0000000
--- a/vom/xdecoder_test.go
+++ /dev/null
@@ -1,134 +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.
-
-package vom_test
-
-import (
- "bytes"
- "fmt"
- "io"
- "reflect"
- "sync"
- "testing"
-
- "v.io/v23/vdl"
- "v.io/v23/vom"
- "v.io/v23/vom/vomtest"
-
- // Import verror to ensure that interface tests result in *verror.E
- _ "v.io/v23/verror"
-)
-
-// TODO(toddw): Add all useful tests from decoder_test.go
-
-var (
- rtIface = reflect.TypeOf((*interface{})(nil)).Elem()
- rtValue = reflect.TypeOf(vdl.Value{})
-)
-
-func TestXDecoder(t *testing.T) {
- // The decoder tests take a long time, so we run them concurrently.
- var pending sync.WaitGroup
- for _, test := range vomtest.AllPass() {
- pending.Add(1)
- go func(test vomtest.Entry) {
- defer pending.Done()
- testXDecoder(t, "[go value]", test, rvPtrValue(test.Value))
- testXDecoder(t, "[go iface]", test, rvPtrIface(test.Value))
- vv, err := vdl.ValueFromReflect(test.Value)
- if err != nil {
- t.Errorf("%s: ValueFromReflect failed: %v", test.Name(), err)
- return
- }
- vvWant := reflect.ValueOf(vv)
- testXDecoder(t, "[new *vdl.Value]", test, vvWant)
- testXDecoderFunc(t, "[zero vdl.Value]", test, vvWant, func() reflect.Value {
- return reflect.ValueOf(vdl.ZeroValue(vv.Type()))
- })
- }(test)
- }
- pending.Wait()
-}
-
-func rvPtrValue(rv reflect.Value) reflect.Value {
- result := reflect.New(rv.Type())
- result.Elem().Set(rv)
- return result
-}
-
-func rvPtrIface(rv reflect.Value) reflect.Value {
- result := reflect.New(rtIface)
- result.Elem().Set(rv)
- return result
-}
-
-func testXDecoder(t *testing.T, pre string, test vomtest.Entry, rvWant reflect.Value) {
- testXDecoderFunc(t, pre, test, rvWant, func() reflect.Value {
- return reflect.New(rvWant.Type().Elem())
- })
- // TODO(toddw): Add tests that start with a randomly-set value.
-}
-
-func testXDecoderFunc(t *testing.T, pre string, test vomtest.Entry, rvWant reflect.Value, rvNew func() reflect.Value) {
- readEOF := make([]byte, 1)
- for _, mode := range vom.AllReadModes {
- // Test vom.NewXDecoder.
- {
- name := fmt.Sprintf("%s (%s) %s", pre, mode, test.Name())
- rvGot := rvNew()
- reader := mode.TestReader(bytes.NewReader(test.Bytes()))
- dec := vom.NewDecoder(reader)
- if err := dec.Decode(rvGot.Interface()); err != nil {
- t.Errorf("%s: Decode failed: %v", name, err)
- return
- }
- if !vdl.DeepEqualReflect(rvGot, rvWant) {
- t.Errorf("%s\nGOT %v\nWANT %v", name, rvGot, rvWant)
- return
- }
- if n, err := reader.Read(readEOF); n != 0 || err != io.EOF {
- t.Errorf("%s: reader got (%d,%v), want (0,EOF)", name, n, err)
- }
- }
- // Test vom.NewXDecoderWithTypeDecoder
- {
- name := fmt.Sprintf("%s (%s with TypeDecoder) %s", pre, mode, test.Name())
- rvGot := rvNew()
- readerT := mode.TestReader(bytes.NewReader(test.TypeBytes()))
- decT := vom.NewTypeDecoder(readerT)
- decT.Start()
- reader := mode.TestReader(bytes.NewReader(test.ValueBytes()))
- dec := vom.NewDecoderWithTypeDecoder(reader, decT)
- err := dec.Decode(rvGot.Interface())
- decT.Stop()
- if err != nil {
- t.Errorf("%s: Decode failed: %v", name, err)
- return
- }
- if !vdl.DeepEqualReflect(rvGot, rvWant) {
- t.Errorf("%s\nGOT %v\nWANT %v", name, rvGot, rvWant)
- return
- }
- if n, err := reader.Read(readEOF); n != 0 || err != io.EOF {
- t.Errorf("%s: reader got (%d,%v), want (0,EOF)", name, n, err)
- }
- if n, err := readerT.Read(readEOF); n != 0 || err != io.EOF {
- t.Errorf("%s: readerT got (%d,%v), want (0,EOF)", name, n, err)
- }
- }
- }
- // Test single-shot vom.Decode twice, to ensure we test the cache hit case.
- for i := 0; i < 2; i++ {
- name := fmt.Sprintf("%s (single-shot %d) %s", pre, i, test.Name())
- rvGot := rvNew()
- if err := vom.Decode(test.Bytes(), rvGot.Interface()); err != nil {
- t.Errorf("%s: Decode failed: %v", name, err)
- return
- }
- if !vdl.DeepEqualReflect(rvGot, rvWant) {
- t.Errorf("%s\nGOT %v\nWANT %v", name, rvGot, rvWant)
- return
- }
- }
-}
diff --git a/vom/xencoder_test.go b/vom/xencoder_test.go
deleted file mode 100644
index 2b9d013..0000000
--- a/vom/xencoder_test.go
+++ /dev/null
@@ -1,76 +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.
-
-package vom_test
-
-import (
- "bytes"
- "fmt"
- "testing"
-
- "v.io/v23/vdl"
- "v.io/v23/vom"
- "v.io/v23/vom/vomtest"
-)
-
-func TestXEncoder(t *testing.T) {
- for _, test := range vomtest.AllPass() {
- testXEncoder(t, "[go value]", test, test.Value.Interface())
- vv, err := vdl.ValueFromReflect(test.Value)
- if err != nil {
- t.Errorf("%s: ValueFromReflect failed: %v", test.Name(), err)
- continue
- }
- testXEncoder(t, "[vdl.Value]", test, vv)
- }
-}
-
-func testXEncoder(t *testing.T, pre string, test vomtest.Entry, value interface{}) {
- // Test vom.NewXEncoder.
- {
- var buf bytes.Buffer
- name := fmt.Sprintf("%s %s", pre, test.Name())
- enc := vom.NewVersionedEncoder(test.Version, &buf)
- if err := enc.Encode(value); err != nil {
- t.Errorf("%s: Encode failed: %v", name, err)
- return
- }
- if got, want := buf.Bytes(), test.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("%s\nGOT %x\nWANT %x", name, got, want)
- return
- }
- }
- // Test vom.NewXEncoderWithTypeEncoder.
- {
- var buf, bufT bytes.Buffer
- name := fmt.Sprintf("%s (with TypeEncoder) %s", pre, test.Name())
- encT := vom.NewVersionedTypeEncoder(test.Version, &bufT)
- enc := vom.NewVersionedEncoderWithTypeEncoder(test.Version, &buf, encT)
- if err := enc.Encode(value); err != nil {
- t.Errorf("%s: Encode failed: %v", name, err)
- return
- }
- if got, want := bufT.Bytes(), test.TypeBytes(); !bytes.Equal(got, want) {
- t.Errorf("%s TYPE\nGOT %x\nWANT %x", name, got, want)
- return
- }
- if got, want := buf.Bytes(), test.ValueBytes(); !bytes.Equal(got, want) {
- t.Errorf("%s VALUE\nGOT %x\nWANT %x", name, got, want)
- return
- }
- }
- // Test single-shot vom.XEncode.
- {
- name := fmt.Sprintf("%s (single-shot) %s", pre, test.Name())
- buf, err := vom.VersionedEncode(test.Version, value)
- if err != nil {
- t.Errorf("%s: Encode failed: %v", name, err)
- return
- }
- if got, want := buf, test.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("%s\nGOT %x\nWANT %x", name, got, want)
- return
- }
- }
-}