blob: ce170935af9ad2d54351f411050f40bd69e5dfee [file] [log] [blame]
// 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 (
"strconv"
"v.io/v23/vdl"
)
// This file contains the ReadValue* methods. The semantics of these methods is
// the same as if StartValue, Decode*, FinishValue were called in sequence. The
// implementation is faster than actually calling that sequence, because we can
// avoid pushing and popping the decoder stack and also avoid unnecessary
// compatibility checks. We also get a minor improvement by avoiding extra
// method calls indirected through the Decoder interface.
//
// Each method has the same pattern:
//
// StartValue:
// If IgnoreNextStarValue is set, the type is already on the stack. Otherwise
// setup the type to process Any and Optional headers. We pass nil to
// d.setupType to avoid the compatibility check, since the decode step will
// naturally let us perform that check.
// Decode:
// We implement common-case fastpaths; e.g. avoiding unnecessary conversions.
// FinishValue:
// Mirrors StartValue, only pop the stack if necessary.
func (d *decoder81) ReadValueBool() (value bool, err error) {
// StartValue
isOnStack := d.flag.IgnoreNextStartValue()
var tt *vdl.Type
if isOnStack {
tt = d.stack[len(d.stack)-1].Type
} else {
if tt, err = d.dfsNextType(); err != nil {
return false, err
}
if tt, _, _, err = d.setupType(tt, nil); err != nil {
return false, err
}
}
// Decode
switch tt.Kind() {
case vdl.Bool:
value, err = binaryDecodeBool(d.buf)
default:
return false, errIncompatibleDecode(tt, "bool")
}
// FinishValue
if isOnStack {
if err := d.FinishValue(); err != nil {
return false, err
}
} else {
d.flag = d.flag.Clear(decFlagFinishValue)
if len(d.stack) == 0 {
if err := d.endMessage(); err != nil {
return false, err
}
}
}
return value, err
}
func (d *decoder81) ReadValueString() (value string, err error) {
// StartValue
isOnStack := d.flag.IgnoreNextStartValue()
var tt *vdl.Type
if isOnStack {
tt = d.stack[len(d.stack)-1].Type
} else {
if tt, err = d.dfsNextType(); err != nil {
return "", err
}
if tt, _, _, err = d.setupType(tt, nil); err != nil {
return "", err
}
}
// Decode
switch tt.Kind() {
case vdl.String:
value, err = binaryDecodeString(d.buf)
case vdl.Enum:
value, err = d.binaryDecodeEnum(tt)
default:
return "", errIncompatibleDecode(tt, "string")
}
// FinishValue
if isOnStack {
if err := d.FinishValue(); err != nil {
return "", err
}
} else {
d.flag = d.flag.Clear(decFlagFinishValue)
if len(d.stack) == 0 {
if err := d.endMessage(); err != nil {
return "", err
}
}
}
return value, err
}
func (d *decoder81) ReadValueUint(bitlen int) (value uint64, err error) {
// StartValue
isOnStack := d.flag.IgnoreNextStartValue()
var tt *vdl.Type
if isOnStack {
tt = d.stack[len(d.stack)-1].Type
} else {
if tt, err = d.dfsNextType(); err != nil {
return 0, err
}
if tt, _, _, err = d.setupType(tt, nil); err != nil {
return 0, err
}
}
// Decode, avoiding unnecessary number conversions.
switch kind := tt.Kind(); kind {
case vdl.Uint16, vdl.Uint32, vdl.Uint64:
if kind.BitLen() <= bitlen {
value, err = binaryDecodeUint(d.buf)
} else {
value, err = d.decodeUint(tt, uint(bitlen))
}
case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64, vdl.Float32, vdl.Float64:
value, err = d.decodeUint(tt, uint(bitlen))
case vdl.Byte:
var b byte
b, err = d.binaryDecodeByte()
value = uint64(b)
default:
return 0, errIncompatibleDecode(tt, "uint"+strconv.Itoa(bitlen))
}
// FinishValue
if isOnStack {
if err := d.FinishValue(); err != nil {
return 0, err
}
} else {
d.flag = d.flag.Clear(decFlagFinishValue)
if len(d.stack) == 0 {
if err := d.endMessage(); err != nil {
return 0, err
}
}
}
return value, err
}
func (d *decoder81) ReadValueInt(bitlen int) (value int64, err error) {
// StartValue
isOnStack := d.flag.IgnoreNextStartValue()
var tt *vdl.Type
if isOnStack {
tt = d.stack[len(d.stack)-1].Type
} else {
if tt, err = d.dfsNextType(); err != nil {
return 0, err
}
if tt, _, _, err = d.setupType(tt, nil); err != nil {
return 0, err
}
}
// Decode, avoiding unnecessary number conversions.
switch kind := tt.Kind(); kind {
case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64:
if kind.BitLen() <= bitlen {
value, err = binaryDecodeInt(d.buf)
} else {
value, err = d.decodeInt(tt, uint(bitlen))
}
case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64, vdl.Float32, vdl.Float64:
value, err = d.decodeInt(tt, uint(bitlen))
default:
return 0, errIncompatibleDecode(tt, "int"+strconv.Itoa(bitlen))
}
// FinishValue
if isOnStack {
if err := d.FinishValue(); err != nil {
return 0, err
}
} else {
d.flag = d.flag.Clear(decFlagFinishValue)
if len(d.stack) == 0 {
if err := d.endMessage(); err != nil {
return 0, err
}
}
}
return value, err
}
func (d *decoder81) ReadValueFloat(bitlen int) (value float64, err error) {
// StartValue
isOnStack := d.flag.IgnoreNextStartValue()
var tt *vdl.Type
if isOnStack {
tt = d.stack[len(d.stack)-1].Type
} else {
if tt, err = d.dfsNextType(); err != nil {
return 0, err
}
if tt, _, _, err = d.setupType(tt, nil); err != nil {
return 0, err
}
}
// Decode, avoiding unnecessary number conversions.
switch kind := tt.Kind(); kind {
case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64, vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64, vdl.Float32, vdl.Float64:
value, err = d.decodeFloat(tt, uint(bitlen))
default:
return 0, errIncompatibleDecode(tt, "float"+strconv.Itoa(bitlen))
}
// FinishValue
if isOnStack {
if err := d.FinishValue(); err != nil {
return 0, err
}
} else {
d.flag = d.flag.Clear(decFlagFinishValue)
if len(d.stack) == 0 {
if err := d.endMessage(); err != nil {
return 0, err
}
}
}
return value, err
}
func (d *decoder81) ReadValueTypeObject() (value *vdl.Type, err error) {
// StartValue
isOnStack := d.flag.IgnoreNextStartValue()
var tt *vdl.Type
if isOnStack {
tt = d.stack[len(d.stack)-1].Type
} else {
if tt, err = d.dfsNextType(); err != nil {
return nil, err
}
if tt, _, _, err = d.setupType(tt, nil); err != nil {
return nil, err
}
}
// Decode
switch tt.Kind() {
case vdl.TypeObject:
value, err = d.binaryDecodeType()
default:
return nil, errIncompatibleDecode(tt, "typeobject")
}
// FinishValue
if isOnStack {
if err := d.FinishValue(); err != nil {
return nil, err
}
} else {
d.flag = d.flag.Clear(decFlagFinishValue)
if len(d.stack) == 0 {
if err := d.endMessage(); err != nil {
return nil, err
}
}
}
return value, err
}
// ReadValueBytes is more complicated than the other ReadValue* methods, since
// []byte lists and [n]byte arrays aren't scalar, and may need more complicated
// conversions
func (d *decoder81) ReadValueBytes(fixedLen int, x *[]byte) (err error) {
// StartValue. Initialize tt and lenHint, and track whether the []byte type
// is already on the stack via isOnStack.
isOnStack := d.flag.IgnoreNextStartValue()
d.flag = d.flag.Clear(decFlagIgnoreNextStartValue)
var tt *vdl.Type
var lenHint int
if isOnStack {
top := d.top()
tt, lenHint = top.Type, top.LenHint
} else {
if tt, err = d.dfsNextType(); err != nil {
return err
}
var flag decStackFlag
if tt, lenHint, flag, err = d.setupType(tt, nil); err != nil {
return err
}
// If tt isn't []byte or [n]byte (or a named variant of these), we need to
// perform conversion byte-by-byte. This is complicated, and can't be
// really fast, so we just push an entry onto the stack and handle this via
// DecodeConvertedBytes below.
//
// We also need to perform the compatibility check, to make sure tt is
// compatible with []byte. The check is fairly expensive, so skipping it
// when tt is actually a bytes type makes the the common case faster.
if !tt.IsBytes() {
if !vdl.Compatible(tt, ttByteList) {
return errIncompatibleDecode(tt, "bytes")
}
d.stack = append(d.stack, decStackEntry{
Type: tt,
Index: -1,
LenHint: lenHint,
Flag: flag,
})
isOnStack = true
}
}
// Decode. The common-case fastpath reads directly from the buffer.
if tt.IsBytes() {
if err := d.decodeBytes(tt, lenHint, fixedLen, x); err != nil {
return err
}
} else {
if err := vdl.DecodeConvertedBytes(d, fixedLen, x); err != nil {
return err
}
}
// FinishValue
if isOnStack {
if err := d.FinishValue(); err != nil {
return err
}
} else {
d.flag = d.flag.Clear(decFlagIsParentBytes)
if len(d.stack) == 0 {
if err := d.endMessage(); err != nil {
return err
}
}
}
return nil
}
var ttByteList = vdl.ListType(vdl.ByteType)