Add new vom conversion tests.

While the regular vom tests run against the golden bytes, the
conversion tests use vdltest and perform conversions.  This
exposed a few unimplemented features and bugs; most notably, the
bytes special-case wasn't implemented in xDecoder.

Also updated comments for the new coders.

Change-Id: I2a6326ce8e41425bca2c6c1c44ccc6f801269e58
diff --git a/vdl/.api b/vdl/.api
index 56704dd..9ddce84 100644
--- a/vdl/.api
+++ b/vdl/.api
@@ -34,6 +34,7 @@
 pkg vdl, func Compatible2(*Type, *Type) bool
 pkg vdl, func Convert(interface{}, interface{}) error
 pkg vdl, func CopyValue(*Value) *Value
+pkg vdl, func DecodeConvertedBytes(Decoder, int, *[]byte) error
 pkg vdl, func DeepEqual(interface{}, interface{}) bool
 pkg vdl, func DeepEqualReflect(reflect.Value, reflect.Value) bool
 pkg vdl, func EnumType(...string) *Type
diff --git a/vdl/coder.go b/vdl/coder.go
index dbe7364..d150844 100644
--- a/vdl/coder.go
+++ b/vdl/coder.go
@@ -32,74 +32,156 @@
 	Writer
 }
 
-// Decoder defines the interface for a decoder of vdl values.
+// Decoder defines the interface for a decoder of vdl values.  The Decoder is
+// passed as the argument to VDLRead.  An example of an implementation of this
+// interface is vom.Decoder.
 //
-// TODO(toddw): This is a work in progress.  Update the comments.
+// The Decoder provides an API to read vdl values of all types in depth-first
+// order.  The ordering is based on the type of the value being read.
+// E.g. given the following value:
+//    type MyStruct struct {
+//      A []string
+//      B map[int64]bool
+//      C any
+//    }
+//    value := MyStruct{
+//      A: {"abc", "def"},
+//      B: {123: true, 456: false},
+//      C: float32(1.5),
+//    }
+// The values will be read in the following order:
+//    "abc"
+//    "def"
+//    (123, true)
+//    (456, false)
+//    1.5
 type Decoder interface {
+	// StartValue must be called before decoding each value, for both scalar and
+	// composite values.  Each call pushes the type of the next value on to the
+	// stack.
 	StartValue() error
+	// FinishValue must be called after decoding each value, for both scalar and
+	// composite values.  Each call pops the type of the top value off of the
+	// stack.
 	FinishValue() error
-	StackDepth() int
+	// SkipValue skips the next value; logically it behaves as if a full sequence
+	// of StartValue / ...Decode*... / FinishValue were called.  It enables
+	// optimizations when the caller doesn't care about the next value.
 	SkipValue() error
+	// IgnoreNextStartValue instructs the Decoder to ignore the next call to
+	// StartValue.  It is used to simplify implementations of VDLRead; e.g. a
+	// caller might call StartValue to check for nil values, and subsequently call
+	// StartValue again to read non-nil values.  IgnoreNextStartValue is used to
+	// ignore the second StartValue call.
 	IgnoreNextStartValue()
 
-	NextEntry() (bool, error)
-	NextField() (string, error)
+	// StackDepth returns the stack depth of the current value.  Returns 0 if
+	// StartValue hasn't been called for the next top-level value.
+	StackDepth() int
 
+	// NextEntry instructs the Decoder to move to the next element of an Array or
+	// List, the next key of a Set, or the next (key,elem) pair of a Map.  Returns
+	// done=true when there are no remaining entries.
+	NextEntry() (done bool, _ error)
+	// NextField instructs the Decoder to move to the next field of a Struct or
+	// Union.  Returns the name of the next field, or the empty string when there
+	// are no remaining fields.
+	NextField() (name string, _ error)
+
+	// Type returns the type of the top value on the stack, corresponding to the
+	// previous call of StartValue.  Returns nil when the stack depth is 0.  The
+	// returned type is only Any or Optional iff the value is nil; non-nil values
+	// are "auto-dereferenced" to their underlying element value.
 	Type() *Type
+	// IsAny returns true iff the type of the top value on the stack was Any,
+	// despite the "auto-dereference" behavior of non-nil values.
 	IsAny() bool
+	// IsOptional returns true iff the type of the top value on the stack was
+	// Optional, despite the "auto-dereference" behavior of non-nil values.
 	IsOptional() bool
+	// IsNil returns true iff the top value on the stack is nil.  It is equivalent
+	// to Type() == AnyType || Type().Kind() == Optional.
 	IsNil() bool
+	// Index returns the index of the current entry or field of the top value on
+	// the stack.  Returns -1 if the top value is a scalar, or if NextEntry /
+	// NextField has not been called.
 	Index() int
+	// LenHint returns the length of the top value on the stack, if it is
+	// available.  Returns -1 if the top value is a scalar, or if the length is
+	// not available.
 	LenHint() int
 
-	// DecodeBool decodes and returns a bool.
+	// DecodeBool returns the top value on the stack as a bool.
 	DecodeBool() (bool, error)
-	// DecodeString decodes and returns a string.
+	// DecodeString returns the top value on the stack as a string.
 	DecodeString() (string, error)
-	// DecodeTypeObject decodes and returns a type.
-	DecodeTypeObject() (*Type, error)
-	// DecodeUint decodes and returns a uint, where the result has bitlen bits.
-	// Errors are returned on loss of precision.
+	// DecodeUint returns the top value on the stack as a uint, where the result
+	// has bitlen bits.  Errors are returned on loss of precision.
 	DecodeUint(bitlen int) (uint64, error)
-	// DecodeInt decodes and returns an int, where the result has bitlen bits.
-	// Errors are returned on loss of precision.
+	// DecodeInt returns the top value on the stack as an int, where the result
+	// has bitlen bits.  Errors are returned on loss of precision.
 	DecodeInt(bitlen int) (int64, error)
-	// DecodeFloat decodes and returns a float, where the result has bitlen bits.
-	// Errors are returned on loss of precision.
+	// DecodeFloat returns the top value on the stack as a float, where the result
+	// has bitlen bits.  Errors are returned on loss of precision.
 	DecodeFloat(bitlen int) (float64, error)
-	// DecodeBytes decodes bytes into x.  If fixedlen >= 0 the decoded bytes must
-	// be exactly that length, otherwise there is no restriction on the number of
-	// decoded bytes.  If cap(*x) is not large enough to fit the decoded bytes, a
-	// new byte slice is assigned to *x.
+	// DecodeBytes decodes the top value on the stack as bytes, into x.  If
+	// fixedlen >= 0 the decoded bytes must be exactly that length, otherwise
+	// there is no restriction on the number of decoded bytes.  If cap(*x) is not
+	// large enough to fit the decoded bytes, a new byte slice is assigned to *x.
 	DecodeBytes(fixedlen int, x *[]byte) error
+	// DecodeTypeObject returns the top value on the stack as a type.
+	DecodeTypeObject() (*Type, error)
 }
 
-// Encoder defines the interface for an encoder of vdl values.
+// Encoder defines the interface for an encoder of vdl values.  The Encoder is
+// passed as the argument to VDLWrite.  An example of an implementation of this
+// interface is vom.Encoder.
 //
-// TODO(toddw): This is a work in progress.  Update the comments.
+// The Encoder provides an API to write vdl values of all types in depth-first
+// order.  The ordering is based on the type of the value being written; see
+// Decoder for examples.
 type Encoder interface {
-	SetNextStartValueIsOptional()
-	// {Start,Finish}Value must be called before / after every concrete value
-	// tt must be non-any and non-optional
+	// StartValue must be called before encoding each non-nil value, for both
+	// scalar and composite values.  The tt type cannot be Any or Optional; use
+	// NilValue to encode nil values.
 	StartValue(tt *Type) error
+	// FinishValue must be called after encoding each non-nil value, for both
+	// scalar and composite values.
 	FinishValue() error
-	// NilValue takes the place of StartValue and FinishValue for nil values.
+	// NilValue encodes a nil value.  The tt type must be Any or Optional.
 	NilValue(tt *Type) error
+	// SetNextStartValueIsOptional instructs the encoder that the next call to
+	// StartValue represents a value with an Optional type.
+	SetNextStartValueIsOptional()
 
-	// NextEntry must be called for every entry.
+	// NextEntry instructs the Encoder to move to the next element of an Array or
+	// List, the next key of a Set, or the next (key,elem) pair of a Map.  Set
+	// done=true when there are no remaining entries.
 	NextEntry(done bool) error
-	// NextField must be called for every field.
+	// NextField instructs the Encoder to move to the next field of a Struct or
+	// Union.  Set name to the name of the next field, or set name="" when there
+	// are no remaining fields.
 	NextField(name string) error
 
+	// SetLenHint sets the length of the List, Set or Map value.  It may only be
+	// called immediately after StartValue, before NextEntry has been called.  Do
+	// not call this method if the length is not known.
 	SetLenHint(lenHint int) error
 
-	EncodeBool(v bool) error        // bool
-	EncodeUint(v uint64) error      // byte, uint16, uint32, uint64
-	EncodeInt(v int64) error        // int8, int16, int32, int64
-	EncodeFloat(v float64) error    // float32, float64
-	EncodeBytes(v []byte) error     // []byte
-	EncodeString(v string) error    // string, enum
-	EncodeTypeObject(v *Type) error // *Type
+	// EncodeBool encodes a bool value.
+	EncodeBool(v bool) error
+	// EncodeString encodes a string value.
+	EncodeString(v string) error
+	// EncodeUint encodes a uint value.
+	EncodeUint(v uint64) error
+	// EncodeInt encodes an int value.
+	EncodeInt(v int64) error
+	// EncodeFloat encodes a float value.
+	EncodeFloat(v float64) error
+	// EncodeBytes encodes a bytes value; either an array or list of bytes.
+	EncodeBytes(v []byte) error
+	// EncodeTypeObject encodes a type.
+	EncodeTypeObject(v *Type) error
 }
 
 func decoderCompatible(dec Decoder, tt *Type) error {
@@ -108,3 +190,46 @@
 	}
 	return nil
 }
+
+var ttByteList = ListType(ByteType)
+
+// DecodeConvertedBytes is a helper function for implementations of
+// Decoder.DecodeBytes, to deal with cases where the decoder value is
+// convertible to []byte.  E.g. if the decoder value is []float64, we need to
+// decode each element as a uint8, performing conversion checks.
+//
+// Since this is meant to be used in the implementation of DecodeBytes, there is
+// no outer call to StartValue/FinishValue.
+func DecodeConvertedBytes(dec Decoder, fixedlen int, buf *[]byte) error {
+	if err := decoderCompatible(dec, ttByteList); err != nil {
+		return err
+	}
+	if len := dec.LenHint(); len >= 0 && cap(*buf) < len {
+		*buf = make([]byte, 0, len)
+	} else {
+		*buf = (*buf)[:0]
+	}
+	index := 0
+	for {
+		switch done, err := dec.NextEntry(); {
+		case err != nil:
+			return err
+		case fixedlen >= 0 && done != (index >= fixedlen):
+			return fmt.Errorf("array len mismatch, done:%v index:%d len:%d", done, index, fixedlen)
+		case done:
+			return nil
+		}
+		if err := dec.StartValue(); err != nil {
+			return err
+		}
+		elem, err := dec.DecodeUint(8)
+		if err != nil {
+			return err
+		}
+		if err := dec.FinishValue(); err != nil {
+			return err
+		}
+		*buf = append(*buf, byte(elem))
+		index++
+	}
+}
diff --git a/vdl/pipe.go b/vdl/pipe.go
index 0daea79..b7207b0 100644
--- a/vdl/pipe.go
+++ b/vdl/pipe.go
@@ -681,7 +681,7 @@
 
 func (d *pipeDecoder) DecodeBytes(fixedLen int, b *[]byte) error {
 	if !d.Enc.IsBytes {
-		if err := bytesVDLRead(fixedLen, b, d); err != nil {
+		if err := DecodeConvertedBytes(d, fixedLen, b); err != nil {
 			return d.Enc.closeLocked(err)
 		}
 		return nil
@@ -697,40 +697,3 @@
 	copy(*b, d.Enc.ArgBytes)
 	return d.Enc.Err
 }
-
-func bytesVDLRead(fixedlen int, b *[]byte, d Decoder) error {
-	var err error
-	switch len := d.LenHint(); {
-	case fixedlen >= 0:
-		*b = (*b)[:0]
-	case len > 0:
-		*b = make([]byte, 0, len)
-	default:
-		*b = (*b)[:0]
-	}
-	index := 0
-	for {
-		switch done, err := d.NextEntry(); {
-		case err != nil:
-			return err
-		case fixedlen >= 0 && done != (index >= fixedlen):
-			return fmt.Errorf("array len mismatch, done:%v index:%d len:%d %T", done, index, len(*b), *b)
-		case done:
-			return nil
-		}
-		var elem byte
-		if err = d.StartValue(); err != nil {
-			return err
-		}
-		tmp, err := d.DecodeUint(8)
-		if err != nil {
-			return err
-		}
-		elem = byte(tmp)
-		if err = d.FinishValue(); err != nil {
-			return err
-		}
-		*b = append(*b, elem)
-		index++
-	}
-}
diff --git a/vdl/reflect_decoder.go b/vdl/reflect_decoder.go
index d49bcaa..285b3a9 100644
--- a/vdl/reflect_decoder.go
+++ b/vdl/reflect_decoder.go
@@ -478,7 +478,7 @@
 		return fmt.Errorf("vdl: %v got %v bytes, want fixed len %v", top.Type, top.ReflectValue.Len(), fixedlen)
 	}
 	if !top.Type.IsBytes() {
-		return bytesVDLRead(fixedlen, v, d)
+		return DecodeConvertedBytes(d, fixedlen, v)
 	}
 	if cap(*v) < top.ReflectValue.Len() {
 		*v = make([]byte, top.ReflectValue.Len())
diff --git a/vdl/value_decoder.go b/vdl/value_decoder.go
index 1dd4a1f..be1bd30 100644
--- a/vdl/value_decoder.go
+++ b/vdl/value_decoder.go
@@ -320,7 +320,7 @@
 		return errEmptyDecoderStack
 	}
 	if !top.Value.Type().IsBytes() {
-		return bytesVDLRead(fixedlen, v, d)
+		return DecodeConvertedBytes(d, fixedlen, v)
 	}
 	if fixedlen >= 0 && top.Value.Len() != fixedlen {
 		return fmt.Errorf("vdl: %v got %v bytes, want fixed len %v", top.Value.Type(), top.Value.Len(), fixedlen)
diff --git a/vom/convert_test.go b/vom/convert_test.go
new file mode 100644
index 0000000..961901b
--- /dev/null
+++ b/vom/convert_test.go
@@ -0,0 +1,183 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build newvdltests
+
+package vom_test
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"reflect"
+	"strings"
+	"sync"
+	"testing"
+
+	"v.io/v23/vdl"
+	"v.io/v23/vdl/vdltest"
+	"v.io/v23/vom"
+)
+
+func TestConvert(t *testing.T) {
+	// We run the tests concurrently, sharing a single Type{En,De}coder pair.
+	// This tests concurrent usage of the Type{En,De}coder, while still ensuring
+	// that the {En,De}coders are used sequentially.
+	rType, wType := newPipe()
+	encT := vom.NewTypeEncoder(wType)
+	decT := vom.NewTypeDecoder(rType)
+	decT.Start()
+	defer decT.Stop()
+	var pending sync.WaitGroup
+	// Go race has a limit of 8192 goroutines, so instead of running each test in
+	// its own goroutine, we batch up multiple tests into the same goroutine.
+	const numGoroutines = 50
+	all := vdltest.AllPass()
+	numPerGoroutine := len(all) / numGoroutines
+	for len(all) > 0 {
+		pending.Add(1)
+		num := numPerGoroutine
+		if len(all) < num {
+			num = len(all)
+		}
+		batch := all[:num]
+		all = all[num:]
+		go func(batch []vdltest.Entry) {
+			defer pending.Done()
+			for _, test := range batch {
+				// Perform conversion tests with go values.
+				name := "[go value] " + test.Name()
+				want := rvPtrValue(test.Target).Interface()
+				target := reflect.New(test.Target.Type()).Interface()
+				source := test.Source.Interface()
+				if err := testConvert(target, source, encT, decT); err != nil {
+					t.Errorf("%s: %v", name, err)
+					continue
+				}
+				if got, want := target, want; !vdl.DeepEqual(got, want) {
+					t.Errorf("%s\nGOT  %#v\nWANT %#v", name, got, want)
+					continue
+				}
+				// Skip conversions from VNamedError into vdl.Value, because verror.E has
+				// a weird property that it sets Msg="v.io/v23/verror.Unknown" on its own,
+				// which isn't captured in the vdl.Value.
+				//
+				// TODO(toddw): Fix this weirdness in verror.
+				if strings.Contains(test.Name(), "VNamedError") {
+					continue
+				}
+				// Perform conversion tests with vdl.Value.
+				name = "[vdl.Value] " + test.Name()
+				vvWant, err := vdl.ValueFromReflect(test.Target)
+				if err != nil {
+					t.Errorf("%s: ValueFromReflect(Target) failed: %v", name, err)
+					continue
+				}
+				vvTarget := vdl.ZeroValue(vvWant.Type())
+				vvSource, err := vdl.ValueFromReflect(test.Source)
+				if err != nil {
+					t.Errorf("%s: ValueFromReflect(Source) failed: %v", name, err)
+					continue
+				}
+				if err := testConvert(vvTarget, vvSource, encT, decT); err != nil {
+					t.Errorf("%s: %v", name, err)
+					continue
+				}
+				if got, want := vvTarget, vvWant; !vdl.DeepEqual(got, want) {
+					t.Errorf("%s\nGOT  %#v\nWANT %#v", name, got, want)
+				}
+			}
+		}(batch)
+	}
+	pending.Wait()
+}
+
+func testConvert(target, source interface{}, encT *vom.TypeEncoder, decT *vom.TypeDecoder) error {
+	if err := testConvertCoder(target, source); err != nil {
+		return err
+	}
+	if err := testConvertSingleShot(target, source); err != nil {
+		return err
+	}
+	return testConvertWithTypeCoder(target, source, encT, decT)
+}
+
+func testConvertCoder(target, source interface{}) error {
+	var buf bytes.Buffer
+	enc := vom.NewXEncoder(&buf)
+	if err := enc.Encode(source); err != nil {
+		return fmt.Errorf("Encode failed: %v", err)
+	}
+	data := buf.Bytes()
+	dec := vom.NewXDecoder(&buf)
+	if err := dec.Decode(target); err != nil {
+		return fmt.Errorf("Decode failed: %v\nDATA %x", err, data)
+	}
+	return nil
+}
+
+func testConvertSingleShot(target, source interface{}) error {
+	data, err := vom.XEncode(source)
+	if err != nil {
+		return fmt.Errorf("(single-shot) Encode failed: %v", err)
+	}
+	if err := vom.XDecode(data, target); err != nil {
+		return fmt.Errorf("(single-shot) Decode failed: %v\nDATA %x", err, data)
+	}
+	return nil
+}
+
+func testConvertWithTypeCoder(target, source interface{}, encT *vom.TypeEncoder, decT *vom.TypeDecoder) error {
+	var buf bytes.Buffer
+	enc := vom.NewXEncoderWithTypeEncoder(&buf, encT)
+	if err := enc.Encode(source); err != nil {
+		return fmt.Errorf("(with TypeEncoder) Encode failed: %v", err)
+	}
+	data := buf.Bytes()
+	dec := vom.NewXDecoderWithTypeDecoder(&buf, decT)
+	if err := dec.Decode(target); err != nil {
+		return fmt.Errorf("(with TypeDecoder) Decode failed: %v\nDATA %x", err, data)
+	}
+	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/xdecoder.go b/vom/xdecoder.go
index cabb3e0..e44851e 100644
--- a/vom/xdecoder.go
+++ b/vom/xdecoder.go
@@ -33,6 +33,8 @@
 	errReadRawBytesFromNonAny     = errors.New("vom: read into vom.RawBytes only supported on any values")
 )
 
+// Decoder manages the receipt and unmarshalling of typed values from the other
+// side of a connection.
 type XDecoder struct {
 	dec xDecoder
 }
@@ -52,26 +54,37 @@
 	IsOptional bool
 }
 
+// NewDecoder returns a new Decoder that reads from the given reader.  The
+// Decoder understands all formats generated by the Encoder.
 func NewXDecoder(r io.Reader) *XDecoder {
 	return &XDecoder{xDecoder{
 		old: NewDecoder(r),
 	}}
 }
 
+// NewDecoderWithTypeDecoder returns a new Decoder that reads from the given
+// reader.  Types are decoded separately through the typeDec.
 func NewXDecoderWithTypeDecoder(r io.Reader, typeDec *TypeDecoder) *XDecoder {
 	return &XDecoder{xDecoder{
 		old: NewDecoderWithTypeDecoder(r, typeDec),
 	}}
 }
 
+// Decoder returns d as a vdl.Decoder.
 func (d *XDecoder) 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 *XDecoder) Decode(v interface{}) error {
 	return vdl.Read(&d.dec, v)
 }
 
+// Ignore ignores the next value from d.
+//
+// TODO(toddw): Rename to SkipValue.
 func (d *XDecoder) Ignore() error {
 	return d.dec.old.Ignore()
 }
@@ -470,6 +483,21 @@
 	return false, fmt.Errorf("vom: type mismatch, got %v, want bool", tt)
 }
 
+func (d *xDecoder) 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 stackTop2 := len(d.stack) - 2; stackTop2 >= 0 {
+		if top2 := d.stack[stackTop2]; top2.Type.IsBytes() {
+			return d.old.buf.ReadByte()
+		}
+	}
+	x, err := binaryDecodeUint(d.old.buf)
+	return byte(x), err
+}
+
 func (d *xDecoder) DecodeUint(bitlen int) (uint64, error) {
 	const errFmt = "vom: %v conversion to uint%d loses precision: %v"
 	tt, ubitlen := d.Type(), uint(bitlen)
@@ -477,7 +505,13 @@
 		return 0, errEmptyDecoderStack
 	}
 	switch tt.Kind() {
-	case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64:
+	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.old.buf)
 		if err != nil {
 			return 0, err
@@ -517,7 +551,18 @@
 		return 0, errEmptyDecoderStack
 	}
 	switch tt.Kind() {
-	case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64:
+	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 bitlen <= 8 && x > 0x7f {
+			return 0, fmt.Errorf(errFmt, tt, bitlen, x)
+		}
+		return int64(x), nil
+	case vdl.Uint16, vdl.Uint32, vdl.Uint64:
 		x, err := binaryDecodeUint(d.old.buf)
 		if err != nil {
 			return 0, err
@@ -559,7 +604,13 @@
 		return 0, errEmptyDecoderStack
 	}
 	switch tt.Kind() {
-	case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64:
+	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.old.buf)
 		if err != nil {
 			return 0, err
@@ -607,26 +658,23 @@
 	if top == nil {
 		return errEmptyDecoderStack
 	}
-	tt, len := top.Type, top.LenHint
-	if tt.IsBytes() {
-		switch {
-		case len == -1:
-			return fmt.Errorf("vom: %v LenHint is currently required", tt)
-		case fixedlen >= 0 && fixedlen != len:
-			return fmt.Errorf("vom: %v got %v bytes, want fixed len %v", tt, len, fixedlen)
-		case len == 0:
-			*v = nil
-			return nil
-		}
-		if cap(*v) >= len {
-			*v = (*v)[:len]
-		} else {
-			*v = make([]byte, len)
-		}
-		return d.old.buf.ReadIntoBuf(*v)
+	tt := top.Type
+	if !tt.IsBytes() {
+		return vdl.DecodeConvertedBytes(d, fixedlen, v)
 	}
-	// TODO(toddw): Deal with conversions from []number.
-	return fmt.Errorf("vom: type mismatch, got %v, want bytes", tt)
+	len := top.LenHint
+	switch {
+	case len == -1:
+		return fmt.Errorf("vom: %v LenHint is currently required", tt)
+	case fixedlen >= 0 && fixedlen != len:
+		return fmt.Errorf("vom: %v got %d bytes, want fixed len %d", tt, len, fixedlen)
+	}
+	if cap(*v) >= len {
+		*v = (*v)[:len]
+	} else {
+		*v = make([]byte, len)
+	}
+	return d.old.buf.ReadIntoBuf(*v)
 }
 
 func (d *xDecoder) DecodeString() (string, error) {
diff --git a/vom/xencoder.go b/vom/xencoder.go
index 093a3a3..1817e93 100644
--- a/vom/xencoder.go
+++ b/vom/xencoder.go
@@ -17,23 +17,34 @@
 	errEmptyEncoderStack = errors.New("vom: empty encoder stack")
 )
 
+// Encoder manages the transmission and marshaling of typed values to the other
+// side of a connection.
 type XEncoder struct {
 	enc xEncoder
 }
 
+// NewEncoder returns a new Encoder that writes to the given writer in the VOM
+// binary format.  The binary format is compact and fast.
 func NewXEncoder(w io.Writer) *XEncoder {
 	return NewVersionedXEncoder(DefaultVersion, w)
 }
 
-func NewXEncoderWithTypeEncoder(w io.Writer, typeEnc *TypeEncoder) *XEncoder {
-	return NewVersionedXEncoderWithTypeEncoder(DefaultVersion, w, typeEnc)
-}
-
+// NewVersionedEncoder returns a new Encoder that writes to the given writer with
+// the specified version.
 func NewVersionedXEncoder(version Version, w io.Writer) *XEncoder {
 	typeEnc := newTypeEncoderInternal(version, newXEncoderForTypes(version, w))
 	return NewVersionedXEncoderWithTypeEncoder(version, w, typeEnc)
 }
 
+// NewEncoderWithTypeEncoder returns a new Encoder that writes to the given
+// writer, where types are encoded separately through the typeEnc.
+func NewXEncoderWithTypeEncoder(w io.Writer, typeEnc *TypeEncoder) *XEncoder {
+	return NewVersionedXEncoderWithTypeEncoder(DefaultVersion, w, typeEnc)
+}
+
+// NewVersionedEncoderWithTypeEncoder returns a new Encoder that writes to the
+// given writer with the specified version, where types are encoded separately
+// through the typeEnc.
 func NewVersionedXEncoderWithTypeEncoder(version Version, w io.Writer, typeEnc *TypeEncoder) *XEncoder {
 	if !isAllowedVersion(version) {
 		panic(fmt.Sprintf("unsupported VOM version: %x", version))
@@ -94,10 +105,13 @@
 	}
 }
 
+// Encoder returns e as a vdl.Encoder.
 func (e *XEncoder) Encoder() vdl.Encoder {
 	return &e.enc
 }
 
+// Encode transmits the value v.  Values of type T are encodable as long as the
+// T is a valid vdl type.
 func (e *XEncoder) Encode(v interface{}) error {
 	return vdl.Write(&e.enc, v)
 }
@@ -462,18 +476,33 @@
 	binaryEncodeBool(e.buf, v)
 	return nil
 }
+
 func (e *xEncoder) EncodeUint(v uint64) 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 stackTop2 := len(e.stack) - 2; stackTop2 >= 0 {
+		if top2 := e.stack[stackTop2]; top2.Type.IsBytes() {
+			e.buf.WriteOneByte(byte(v))
+			return nil
+		}
+	}
 	binaryEncodeUint(e.buf, v)
 	return nil
 }
+
 func (e *xEncoder) EncodeInt(v int64) error {
 	binaryEncodeInt(e.buf, v)
 	return nil
 }
+
 func (e *xEncoder) EncodeFloat(v float64) error {
 	binaryEncodeFloat(e.buf, v)
 	return nil
 }
+
 func (e *xEncoder) EncodeBytes(v []byte) error {
 	top := e.top()
 	if top == nil {
@@ -490,6 +519,7 @@
 	e.buf.Write(v)
 	return nil
 }
+
 func (e *xEncoder) EncodeString(v string) error {
 	top := e.top()
 	if top == nil {
@@ -509,6 +539,7 @@
 	}
 	return nil
 }
+
 func (e *xEncoder) EncodeTypeObject(v *vdl.Type) error {
 	tid, err := e.typeEnc.encode(v)
 	if err != nil {