blob: dba0616c329202fdf41a9d6a4fc141b171114093 [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"
"io"
"sync"
"v.io/v23/verror"
)
// Encode writes the value v and returns the encoded bytes. The semantics of
// value encoding are described by Encoder.Encode.
//
// This is a "single-shot" encoding; full type information is always included in
// the returned encoding, as if a new encoder were used for each call.
func Encode(v interface{}) ([]byte, error) {
return VersionedEncode(DefaultVersion, v)
}
// VersionedEncode performs single-shot encoding to a specific version of VOM
func VersionedEncode(version Version, v interface{}) ([]byte, error) {
var buf bytes.Buffer
if err := NewVersionedEncoder(version, &buf).Encode(v); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Decode reads the value from the given data, and stores it in value v. The
// semantics of value decoding are described by Decoder.Decode.
//
// This is a "single-shot" decoding; the data must have been encoded by a call
// to vom.Encode.
func Decode(data []byte, v interface{}) error {
// The implementation below corresponds (logically) to the following:
// return NewDecoder(bytes.NewReader(data)).Decode(valptr)
//
// However decoding type messages is expensive, so we cache typeDecoders to
// skip the decoding in the common case.
buf := newDecbufFromBytes(data)
key, err := computeTypeDecoderCacheKey(buf)
if err != nil {
return err
}
typeDec := singleShotTypeDecoderCache.lookup(key)
cacheMiss := false
if typeDec == nil {
// Cache miss; start decoding at the beginning of all type messages with a
// new TypeDecoder.
cacheMiss = true
buf.beg = 0
typeDec = newTypeDecoderInternal(buf)
} else {
// Cache hit; the buf is already positioned on the message id of the value,
// so we can just continue decoding from there.
buf.version = Version(data[0])
typeDec = newDerivedTypeDecoderInternal(buf, typeDec)
}
// Decode the value message.
dec := &Decoder{decoder81{
buf: buf,
typeDec: typeDec,
}}
if err := dec.Decode(v); err != nil {
return err
}
// Populate the typeDecoder cache for future re-use.
if cacheMiss {
singleShotTypeDecoderCache.insert(key, typeDec)
}
return nil
}
// singleShotTypeDecoderCache is a global cache of TypeDecoders keyed by the
// bytes of the sequence of type messages before the value message. A sequence
// of type messages that is byte-equal doesn't guarantee the same types in the
// general case, since type ids are scoped to a single Encoder/Decoder stream;
// different streams may represent different types using the same type ids.
// However the single-shot vom.Encode is guaranteed to generate the same bytes
// for a given vom version.
//
// TODO(toddw): This cache grows without bounds, use a fixed-size cache instead.
var singleShotTypeDecoderCache = typeDecoderCache{
decoders: make(map[string]*TypeDecoder),
}
type typeDecoderCache struct {
sync.RWMutex
decoders map[string]*TypeDecoder
}
func (x *typeDecoderCache) insert(key string, dec *TypeDecoder) {
x.Lock()
defer x.Unlock()
if x.decoders[key] != nil {
// There was a race between concurrent calls to vom.Decode, and another
// goroutine already populated the cache. It doesn't matter which one we
// use, so there's nothing more to do.
return
}
x.decoders[key] = dec
}
func (x *typeDecoderCache) lookup(key string) *TypeDecoder {
x.RLock()
dec := x.decoders[key]
x.RUnlock()
return dec
}
// computeTypeDecoderCacheKey computes the cache key for the typeDecoderCache,
// by returning the bytes of all type messages. Upon return, the read position
// of b is guaranteed to be on the first byte of the value message.
func computeTypeDecoderCacheKey(b *decbuf) (string, error) {
if b.end == 0 {
return "", io.EOF
}
if version := Version(b.buf[0]); !isAllowedVersion(version) {
return "", verror.New(errBadVersionByte, nil, version)
}
b.beg++
// Walk through bytes until we get to a value message.
for {
if b.end < b.beg {
return "", verror.New(errIndexOutOfRange, nil)
}
// Handle incomplete types.
switch ok, err := binaryDecodeControlOnly(b, WireCtrlTypeIncomplete); {
case err != nil:
return "", err
case ok:
continue
}
// Handle the next message id.
switch id, byteLen, err := binaryPeekInt(b); {
case err != nil:
return "", err
case id > 0:
// This is a value message. The bytes read so far include the version
// byte and all type messages; use all of these bytes as the cache key.
//
// TODO(toddw): Take a fingerprint of these bytes to reduce memory usage.
return string(b.buf[:b.beg]), nil
case id < 0:
// This is a type message. Skip the bytes for the id or control code, and
// decode the message length (which always exists for wireType), and skip
// those bytes too to move to the next message.
b.beg += byteLen
msgLen, err := binaryDecodeLen(b)
if err != nil {
return "", err
}
b.beg += msgLen
default:
return "", verror.New(errDecodeZeroTypeID, nil)
}
}
}