Merge "vom: add String() method to RawBytes"
diff --git a/vom/.api b/vom/.api
index 88b5105..ec5531e 100644
--- a/vom/.api
+++ b/vom/.api
@@ -70,6 +70,7 @@
 pkg vom, method (*Dumper) Status()
 pkg vom, method (*Dumper) Write([]byte) (int, error)
 pkg vom, method (*Encoder) Encode(interface{}) error
+pkg vom, method (*RawBytes) String() string
 pkg vom, method (*RawBytes) ToTarget(vdl.Target) error
 pkg vom, method (*RawBytes) ToValue(interface{}) error
 pkg vom, method (*TypeDecoder) Start()
diff --git a/vom/raw_bytes.go b/vom/raw_bytes.go
index 2946daf..768a6ad 100644
--- a/vom/raw_bytes.go
+++ b/vom/raw_bytes.go
@@ -6,7 +6,9 @@
 
 import (
 	"bytes"
+	"fmt"
 	"reflect"
+	"strconv"
 
 	"v.io/v23/vdl"
 )
@@ -38,6 +40,39 @@
 	return &rb, err
 }
 
+// String outputs a string representation of RawBytes of the form
+// RawBytes{Version81, int8, RefTypes{bool, string}, AnyLengths{4}, fa0e9dcc}
+func (rb *RawBytes) String() string {
+	var buf bytes.Buffer
+	buf.WriteString("RawBytes{")
+	buf.WriteString(rb.Version.String())
+	buf.WriteString(", ")
+	buf.WriteString(rb.Type.String())
+	buf.WriteString(", ")
+	if len(rb.RefTypes) > 0 {
+		buf.WriteString("RefTypes{")
+		for i, t := range rb.RefTypes {
+			if i > 0 {
+				buf.WriteString(", ")
+			}
+			buf.WriteString(t.String())
+		}
+		buf.WriteString("}, ")
+	}
+	if len(rb.AnyLengths) > 0 {
+		buf.WriteString("AnyLengths{")
+		for i, l := range rb.AnyLengths {
+			if i > 0 {
+				buf.WriteString(", ")
+			}
+			buf.WriteString(strconv.FormatUint(l, 10))
+		}
+		buf.WriteString("}, ")
+	}
+	buf.WriteString(fmt.Sprintf("%x}", rb.Data))
+	return buf.String()
+}
+
 func (rb *RawBytes) ToValue(value interface{}) error {
 	// TODO(bprosnitz) This implementation is temporary - we should make it faster
 	dat, err := Encode(rb)
diff --git a/vom/raw_bytes_test.go b/vom/raw_bytes_test.go
index 92916e9..0f94aaf 100644
--- a/vom/raw_bytes_test.go
+++ b/vom/raw_bytes_test.go
@@ -476,3 +476,43 @@
 		}
 	}
 }
+
+func TestRawBytesString(t *testing.T) {
+	tests := []struct {
+		input    *RawBytes
+		expected string
+	}{
+		{
+			input: &RawBytes{
+				Version81,
+				vdl.Int8Type,
+				[]*vdl.Type{vdl.BoolType, vdl.StringType},
+				[]uint64{4},
+				[]byte{0xfa, 0x0e, 0x9d, 0xcc}},
+			expected: "RawBytes{Version81, int8, RefTypes{bool, string}, AnyLengths{4}, fa0e9dcc}",
+		},
+		{
+			input: &RawBytes{
+				Version81,
+				vdl.Int8Type,
+				[]*vdl.Type{vdl.BoolType},
+				[]uint64{},
+				[]byte{0xfa, 0x0e, 0x9d, 0xcc}},
+			expected: "RawBytes{Version81, int8, RefTypes{bool}, fa0e9dcc}",
+		},
+		{
+			input: &RawBytes{
+				Version81,
+				vdl.Int8Type,
+				nil,
+				nil,
+				[]byte{0xfa, 0x0e, 0x9d, 0xcc}},
+			expected: "RawBytes{Version81, int8, fa0e9dcc}",
+		},
+	}
+	for _, test := range tests {
+		if got, want := test.input.String(), test.expected; got != want {
+			t.Errorf("got %v, want %v", got, want)
+		}
+	}
+}