blob: d4c81f2e8579a571862d890cd69b83f28cf4a7b6 [file] [log] [blame]
// 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 (
"bytes"
"fmt"
"strconv"
"v.io/v23/vdl"
)
type RawBytes struct {
Version Version
Type *vdl.Type
RefTypes []*vdl.Type
AnyLengths []int
Data []byte
}
func (RawBytes) __VDLReflect(struct {
Type interface{} // ensure vdl.TypeOf(RawBytes{}) returns vdl.AnyType
}) {
}
func RawBytesOf(value interface{}) *RawBytes {
rb, err := RawBytesFromValue(value)
if err != nil {
panic(err)
}
return rb
}
func RawBytesFromValue(value interface{}) (*RawBytes, error) {
// TODO(bprosnitz) This implementation is temporary - we should make it faster
data, err := Encode(value)
if err != nil {
return nil, err
}
rb := new(RawBytes)
if err := Decode(data, rb); err != nil {
return nil, err
}
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 {
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.Itoa(l))
}
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)
if err != nil {
return err
}
return Decode(dat, value)
}
func (rb *RawBytes) IsNil() bool {
if rb == nil {
return true
}
if rb.Type != vdl.AnyType && rb.Type.Kind() != vdl.Optional {
return false
}
return len(rb.Data) == 1 && rb.Data[0] == WireCtrlNil
}
func (rb *RawBytes) Decoder() vdl.Decoder {
dec := NewZDecoder(bytes.NewReader(rb.Data))
dec.buf.version = rb.Version
dec.refTypes.tids = make([]TypeId, len(rb.RefTypes))
for i, refType := range rb.RefTypes {
tid := TypeId(i) + WireIdFirstUserType
dec.typeDec.idToType[tid] = refType
dec.refTypes.tids[i] = tid
}
dec.refAnyLens.lens = make([]int, len(rb.AnyLengths))
for i, anyLen := range rb.AnyLengths {
dec.refAnyLens.lens[i] = anyLen
}
xd := &xDecoder{old: dec}
tt, lenHint, flag, err := xd.setupType(rb.Type, nil)
if err != nil {
panic(err) // TODO(toddw): Change this to not panic.
}
xd.stack = append(xd.stack, decoderStackEntry{
Type: tt,
Index: -1,
LenHint: lenHint,
Flag: flag,
})
xd.ignoreNextStartValue = true
return xd
}
func (rb *RawBytes) VDLIsZero() bool {
return rb == nil || (rb.Type == vdl.AnyType && rb.IsNil())
}
// TODO(toddw) This is slow - we should fix this.
func (rb *RawBytes) VDLEqual(x interface{}) bool {
orb, ok := x.(*RawBytes)
if !ok {
return false
}
return vdl.EqualValue(vdl.ValueOf(rb), vdl.ValueOf(orb))
}
func (rb *RawBytes) VDLRead(dec vdl.Decoder) error {
// Fastpath: the bytes are already available in the xDecoder. Note that this
// also handles the case where dec is RawBytes.Decoder().
if d, ok := dec.(*xDecoder); ok {
return d.readRawBytes(rb)
}
// Slowpath: the bytes are not available, we must encode new bytes.
var buf bytes.Buffer
enc := newXEncoderForRawBytes(&buf)
if err := vdl.Transcode(enc, dec); err != nil {
return err
}
// Fill in rb with the results of the transcoding, captured in enc.
rb.Version = enc.version
rb.Type = enc.msgType
if enc.tids == nil || len(enc.tids.tids) == 0 {
rb.RefTypes = nil
} else {
tids, idToType := enc.tids.tids, enc.typeEnc.makeIdToTypeUnlocked()
rb.RefTypes = make([]*vdl.Type, len(tids))
for i, tid := range tids {
tt := bootstrapIdToType[tid]
if tt == nil {
if tt = idToType[tid]; tt == nil {
return fmt.Errorf("vom: internal error, type id %d in %v doesn't exist in %v", i, tids, idToType)
}
}
rb.RefTypes[i] = tt
}
}
if enc.anyLens == nil || len(enc.anyLens.lens) == 0 {
rb.AnyLengths = nil
} else {
rb.AnyLengths = enc.anyLens.lens
}
rb.Data = buf.Bytes()
return nil
}
func (rb *RawBytes) VDLWrite(enc vdl.Encoder) error {
// Fastpath: we're trying to encode into an xEncoder. We can only write
// directly if the versions are the same and if the encoder hasn't written any
// other values yet, since otherwise rb.Data needs to be re-written to account
// for the differences.
//
// TODO(toddw): Code a variant that performs the re-writing.
if e, ok := enc.(*xEncoder); ok && e.version == rb.Version && len(e.stack) == 0 {
return e.writeRawBytes(rb)
}
// Slowpath: decodes bytes from rb and fill in enc.
return vdl.Transcode(enc, rb.Decoder())
}