blob: 161860e9399d3001cf62ccf230b84c666924b4cd [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 (
"io"
"os"
"reflect"
"v.io/v23/vdl"
"v.io/v23/verror"
)
var (
errDecodeNil = verror.Register(pkgPath+".errDecodeNil", verror.NoRetry, "{1:}{2:} vom: invalid decode into nil interface{}{:_}")
errDecodeZeroTypeID = verror.Register(pkgPath+".errDecodeZeroTypeID", verror.NoRetry, "{1:}{2:} vom: zero type id{:_}")
errIndexOutOfRange = verror.Register(pkgPath+".errIndexOutOfRange", verror.NoRetry, "{1:}{2:} vom: index out of range{:_}")
errLeftOverBytes = verror.Register(pkgPath+".errLeftOverBytes", verror.NoRetry, "{1:}{2:} vom: {3} leftover bytes{:_}")
errUnexpectedControlByte = verror.Register(pkgPath+".errUnexpectedControlByte", verror.NoRetry, "{1:}{2:} vom: unexpected control byte {3}{:_}")
errDecodeValueUnhandledType = verror.Register(pkgPath+".errDecodeValueUnhandledType", verror.NoRetry, "{1:}{2:} vom: decodeValue unhandled type {3}{:_}")
errIgnoreValueUnhandledType = verror.Register(pkgPath+".errIgnoreValueUnhandledType", verror.NoRetry, "{1:}{2:} vom: ignoreValue unhandled type {3}{:_}")
errInvalidTypeIdIndex = verror.Register(pkgPath+".errInvalidTypeIdIndex", verror.NoRetry, "{1:}{2:} vom: value referenced invalid index into type id table {:_}")
errInvalidAnyIndex = verror.Register(pkgPath+".errInvalidAnyIndex", verror.NoRetry, "{1:}{2:} vom: value referenced invalid index into anyLen table {:_}")
)
// Decoder manages the receipt and unmarshalling of typed values from the other
// side of a connection.
type Decoder struct {
typeDec *TypeDecoder
hasSeparateTypeDec bool // TODO(bprosnitz) This should probably be removed.
// Stream data:
buf *decbuf
// Message data:
typeIncomplete bool // Type message was flagged as having dependencies on unsent types.
refTypes referencedTypes
refAnyLens referencedAnyLens
}
// This is only used for debugging; add this as the first line of NewDecoder to
// dump formatted vom bytes to stdout:
// r = teeDump(r)
func teeDump(r io.Reader) io.Reader {
return io.TeeReader(r, NewDumper(NewDumpWriter(os.Stdout)))
}
// NewDecoder returns a new Decoder that reads from the given reader. The
// Decoder understands all formats generated by the Encoder.
func NewDecoder(r io.Reader) *Decoder {
// When the TypeDecoder isn't shared, we always decode type messages in
// Decoder.decodeValueType() and feed them to the TypeDecoder. That is,
// the TypeDecoder will never read messages from the buffer. So we pass
// a nil buffer to newTypeDecoder.
buf := newDecbuf(r)
typeDec := newTypeDecoderInternal(buf)
return &Decoder{
buf: buf,
typeDec: typeDec,
hasSeparateTypeDec: false,
}
}
// NewDecoderWithTypeDecoder returns a new Decoder that reads from the given
// reader. Types will be decoded separately through the given typeDec.
func NewDecoderWithTypeDecoder(r io.Reader, typeDec *TypeDecoder) *Decoder {
return &Decoder{
buf: newDecbuf(r),
typeDec: typeDec,
hasSeparateTypeDec: true,
}
}
// Decode reads the next value from the reader(s) 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 compatible.
//
// Types that are special-cased, only for v:
// *RawBytes - Store raw (uninterpreted) bytes in v.
//
// Types that are special-cased, recursively throughout v:
// *vdl.Value - Decode into v.
// reflect.Value - Decode into v, which must be settable.
//
// Decoding into a RawBytes captures the value in a raw form, which may be
// subsequently passed to an Encoder for transcoding.
//
// Decode(nil) always returns an error. Use Ignore() to ignore the next value.
func (d *Decoder) Decode(v interface{}) error {
if v == nil {
return verror.New(errDecodeNil, nil)
}
target, err := vdl.ReflectTarget(reflect.ValueOf(v))
if err != nil {
return err
}
return d.decodeToTarget(target)
}
func (d *Decoder) decodeTypeDefs() error {
if !d.hasSeparateTypeDec {
for {
typeNext, err := d.typeIsNext()
if err != nil {
return err
}
if !typeNext {
break
}
if err := d.typeDec.readSingleType(); err != nil {
return err
}
}
}
return nil
}
func (d *Decoder) decodeToTarget(target vdl.Target) error {
if err := d.decodeTypeDefs(); err != nil {
return err
}
tid, err := d.nextMessage()
if err != nil {
return err
}
valType, err := d.typeDec.lookupType(tid)
if err != nil {
return err
}
if hax, ok := target.(hasRvHack); ok {
rv := hax.HackGetRv()
valLen, err := d.peekValueByteLen(valType)
if err != nil {
return err
}
if isRawBytes, err := d.tryDecodeRaw(valType, valLen, rv); err != nil {
return err
} else if isRawBytes {
return d.endMessage()
}
}
if err := d.decodeValue(valType, target); err != nil {
return err
}
return d.endMessage()
}
// Ignore ignores the next value from the reader.
func (d *Decoder) Ignore() error {
if err := d.decodeTypeDefs(); err != nil {
return err
}
tid, err := d.nextMessage()
if err != nil {
return err
}
valType, err := d.typeDec.lookupType(tid)
if err != nil {
return err
}
valLen, err := d.peekValueByteLen(valType)
if err != nil {
return err
}
if err := d.buf.Skip(valLen); err != nil {
return err
}
return d.endMessage()
}
// decodeWireType decodes the next type definition message and returns its
// type id.
func (d *Decoder) decodeWireType(wt *wireType) (TypeId, error) {
tid, err := d.nextMessage()
if err != nil {
return 0, err
}
// Decode the wire type like a regular value.
target, err := vdl.ReflectTarget(reflect.ValueOf(wt))
if err != nil {
return 0, err
}
if err := d.decodeValue(wireTypeType, target); err != nil {
return 0, err
}
return tid, d.endMessage()
}
// peekValueByteLen returns the byte length of the next value.
func (d *Decoder) peekValueByteLen(tt *vdl.Type) (int, error) {
if hasChunkLen(tt) {
// Use the explicit message length.
return d.buf.lim, nil
}
// No explicit message length, but the length can be computed.
switch {
case tt.Kind() == vdl.Byte:
if d.buf.version == Version80 {
return 1, nil
} else {
return binaryPeekUintByteLen(d.buf)
}
case tt.Kind() == vdl.Array && tt.IsBytes():
// Byte arrays are exactly their length and encoded with 1-byte header.
return tt.Len() + 1, nil
case tt.Kind() == vdl.String || tt.IsBytes():
// Strings and byte lists are encoded with a length header.
strlen, bytelen, err := binaryPeekUint(d.buf)
switch {
case err != nil:
return 0, err
case strlen > maxBinaryMsgLen:
return 0, verror.New(errMsgLen, nil)
}
return int(strlen) + bytelen, nil
default:
// Must be a primitive, which is encoded as an underlying uint.
return binaryPeekUintByteLen(d.buf)
}
}
func (d *Decoder) decodeRaw(tt *vdl.Type, valLen int, raw *RawBytes) error {
raw.Version = d.buf.version
raw.Type = tt
raw.Data = make([]byte, valLen)
if err := d.buf.ReadIntoBuf(raw.Data); err != nil {
return err
}
refTypeLen := len(d.refTypes.tids)
if cap(raw.RefTypes) >= refTypeLen {
raw.RefTypes = raw.RefTypes[:refTypeLen]
} else {
raw.RefTypes = make([]*vdl.Type, refTypeLen)
}
for i, tid := range d.refTypes.tids {
var err error
if raw.RefTypes[i], err = d.typeDec.lookupType(tid); err != nil {
return err
}
}
raw.AnyLengths = d.refAnyLens.lens
return nil
}
type hasRvHack interface {
HackGetRv() reflect.Value
}
func (d *Decoder) tryDecodeRaw(tt *vdl.Type, valLen int, rv reflect.Value) (isRawBytes bool, _ error) {
// Dereference pointers down to at most one remaining *.
for rv.IsValid() && rv.Kind() == reflect.Ptr && rv.Elem().IsValid() && rv.Elem().Kind() == reflect.Ptr {
rv = rv.Elem()
}
if rv.IsValid() && rv.Type() == rtPtrToRawBytes {
rb := rv.Interface().(*RawBytes)
if rb == nil {
rb = new(RawBytes)
rv.Set(reflect.ValueOf(rb))
}
return true, d.decodeRaw(tt, valLen, rb)
}
if rv.IsValid() && rv.Type() == rtRawBytes {
rb := rv.Addr().Interface().(*RawBytes)
if err := d.decodeRaw(tt, valLen, rb); err != nil {
return true, err
}
return true, nil
}
return false, nil
}
// decodeValue decodes the rest of the message assuming type tt.
func (d *Decoder) decodeValue(tt *vdl.Type, target vdl.Target) error {
ttFrom := tt
if tt.Kind() == vdl.Optional {
// If the type is optional, we expect to see either WireCtrlNil or the actual
// value, but not both. And thus, we can just peek for the WireCtrlNil here.
switch ctrl, err := binaryPeekControl(d.buf); {
case err != nil:
return err
case ctrl == WireCtrlNil:
d.buf.Skip(1)
return target.FromNil(ttFrom)
}
tt = tt.Elem()
}
if tt.IsBytes() {
len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
if err != nil {
return err
}
// TODO(toddw): remove allocation
buf := make([]byte, len)
if err := d.buf.ReadIntoBuf(buf); err != nil {
return err
}
return target.FromBytes(buf, ttFrom)
}
switch kind := tt.Kind(); kind {
case vdl.Bool:
v, err := binaryDecodeBool(d.buf)
if err != nil {
return err
}
return target.FromBool(v, ttFrom)
case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64:
var v uint64
if tt.Kind() == vdl.Byte && d.buf.version == Version80 {
b, err := d.buf.ReadByte()
if err != nil {
return err
}
v = uint64(b)
} else {
var err error
v, err = binaryDecodeUint(d.buf)
if err != nil {
return err
}
}
return target.FromUint(v, ttFrom)
case vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64:
if d.buf.version == Version80 && tt.Kind() == vdl.Int8 {
return verror.New(errUnsupportedInVOMVersion, nil, "int8", d.buf.version)
}
v, err := binaryDecodeInt(d.buf)
if err != nil {
return err
}
return target.FromInt(v, ttFrom)
case vdl.Float32, vdl.Float64:
v, err := binaryDecodeFloat(d.buf)
if err != nil {
return err
}
return target.FromFloat(v, ttFrom)
case vdl.String:
v, err := binaryDecodeString(d.buf)
if err != nil {
return err
}
return target.FromString(v, ttFrom)
case vdl.Enum:
index, err := binaryDecodeUint(d.buf)
switch {
case err != nil:
return err
case index >= uint64(tt.NumEnumLabel()):
return verror.New(errIndexOutOfRange, nil)
}
return target.FromEnumLabel(tt.EnumLabel(int(index)), ttFrom)
case vdl.TypeObject:
x, err := binaryDecodeUint(d.buf)
if err != nil {
return err
}
var tid TypeId
if d.buf.version == Version80 {
tid = TypeId(x)
} else {
tid, err = d.refTypes.ReferencedTypeId(x)
if err != nil {
return err
}
}
typeobject, err := d.typeDec.lookupType(tid)
if err != nil {
return err
}
return target.FromTypeObject(typeobject)
case vdl.Array, vdl.List:
len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
if err != nil {
return err
}
listTarget, err := target.StartList(ttFrom, len)
if err != nil {
return err
}
for ix := 0; ix < len; ix++ {
elem, err := listTarget.StartElem(ix)
if err != nil {
return err
}
if err := d.decodeValue(tt.Elem(), elem); err != nil {
return err
}
if err := listTarget.FinishElem(elem); err != nil {
return err
}
}
return target.FinishList(listTarget)
case vdl.Set:
len, err := binaryDecodeLen(d.buf)
if err != nil {
return err
}
setTarget, err := target.StartSet(ttFrom, len)
if err != nil {
return err
}
for ix := 0; ix < len; ix++ {
key, err := setTarget.StartKey()
if err != nil {
return err
}
if err := d.decodeValue(tt.Key(), key); err != nil {
return err
}
switch err := setTarget.FinishKey(key); {
case err == vdl.ErrFieldNoExist:
continue
case err != nil:
return err
}
}
return target.FinishSet(setTarget)
case vdl.Map:
len, err := binaryDecodeLen(d.buf)
if err != nil {
return err
}
mapTarget, err := target.StartMap(ttFrom, len)
if err != nil {
return err
}
for ix := 0; ix < len; ix++ {
key, err := mapTarget.StartKey()
if err != nil {
return err
}
if err := d.decodeValue(tt.Key(), key); err != nil {
return err
}
switch field, err := mapTarget.FinishKeyStartField(key); {
case err == vdl.ErrFieldNoExist:
if err := d.ignoreValue(tt.Elem()); err != nil {
return err
}
case err != nil:
return err
default:
if err := d.decodeValue(tt.Elem(), field); err != nil {
return err
}
if err := mapTarget.FinishField(key, field); err != nil {
return err
}
}
}
return target.FinishMap(mapTarget)
case vdl.Struct:
fieldsTarget, err := target.StartFields(ttFrom)
if err != nil {
return err
}
// Loop through decoding the 0-based field index and corresponding field.
decodedFields := make([]bool, tt.NumField())
for {
index, ctrl, err := binaryDecodeUintWithControl(d.buf)
switch {
case err != nil:
return err
case ctrl == WireCtrlEnd:
// Fill not-yet-decoded fields with their zero values.
for index, decoded := range decodedFields {
if decoded {
continue
}
ttfield := tt.Field(index)
switch err := fieldsTarget.ZeroField(ttfield.Name); {
case err == vdl.ErrFieldNoExist:
// Ignore it.
case err != nil:
return err
}
}
return target.FinishFields(fieldsTarget)
case ctrl != 0:
return verror.New(errUnexpectedControlByte, nil, ctrl)
case index >= uint64(tt.NumField()):
return verror.New(errIndexOutOfRange, nil)
}
ttfield := tt.Field(int(index))
switch key, field, err := fieldsTarget.StartField(ttfield.Name); {
case err == vdl.ErrFieldNoExist:
if err := d.ignoreValue(ttfield.Type); err != nil {
return err
}
case err != nil:
return err
default:
if err := d.decodeValue(ttfield.Type, field); err != nil {
return err
}
if err := fieldsTarget.FinishField(key, field); err != nil {
return err
}
}
decodedFields[index] = true
}
case vdl.Union:
fieldsTarget, err := target.StartFields(ttFrom)
if err != nil {
return err
}
index, err := binaryDecodeUint(d.buf)
switch {
case err != nil:
return err
case index >= uint64(tt.NumField()):
return verror.New(errIndexOutOfRange, nil)
}
ttfield := tt.Field(int(index))
key, field, err := fieldsTarget.StartField(ttfield.Name)
if err != nil {
return err
}
if err := d.decodeValue(ttfield.Type, field); err != nil {
return err
}
if err := fieldsTarget.FinishField(key, field); err != nil {
return err
}
return target.FinishFields(fieldsTarget)
case vdl.Any:
elemType, valLen, err := d.readAnyHeader()
if err != nil {
return err
}
if elemType == nil {
return target.FromNil(tt)
}
if hax, ok := target.(hasRvHack); ok {
rv := hax.HackGetRv()
if isRawBytes, err := d.tryDecodeRaw(elemType, valLen, rv); isRawBytes {
return err
}
}
return d.decodeValue(elemType, target)
default:
panic(verror.New(errDecodeValueUnhandledType, nil, tt))
}
}
func (d *Decoder) readAnyHeader() (*vdl.Type, int, error) {
// Read either WireCtrlNil or the index of the referenced type id.
typeIndex, ctrl, err := binaryDecodeUintWithControl(d.buf)
switch {
case err != nil:
return nil, 0, err
case ctrl == WireCtrlNil:
return nil, 0, nil // nil any
case ctrl != 0:
return nil, 0, verror.New(errUnexpectedControlByte, nil, ctrl)
}
var tid TypeId
if d.buf.version == Version80 {
tid = TypeId(typeIndex)
} else if tid, err = d.refTypes.ReferencedTypeId(typeIndex); err != nil {
return nil, 0, err
}
// Look up the referenced type id.
ttElem, err := d.typeDec.lookupType(tid)
if err != nil {
return nil, 0, err
}
var anyLen int
if d.buf.version != Version80 {
// Read and lookup the index of the any byte length. Reference the any len,
// even if it isn't used, to report missing references.
lenIndex, err := binaryDecodeUint(d.buf)
if err != nil {
return nil, 0, err
}
if anyLen, err = d.refAnyLens.ReferencedAnyLen(lenIndex); err != nil {
return nil, 0, err
}
}
return ttElem, anyLen, nil
}
// ignoreValue ignores the rest of the value of type t. This is used to ignore
// unknown struct fields.
func (d *Decoder) ignoreValue(tt *vdl.Type) error {
// TODO(toddw): How about ignoring optional values?
if tt.IsBytes() {
len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
if err != nil {
return err
}
return d.buf.Skip(len)
}
switch kind := tt.Kind(); kind {
case vdl.Bool:
return d.buf.Skip(1)
case vdl.Byte:
if d.buf.version == Version80 {
return d.buf.Skip(1)
} else {
return binaryIgnoreUint(d.buf)
}
case vdl.Uint16, vdl.Uint32, vdl.Uint64, vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64, vdl.Float32, vdl.Float64, vdl.Enum, vdl.TypeObject:
if d.buf.version == Version80 && tt.Kind() == vdl.Int8 {
return verror.New(errUnsupportedInVOMVersion, nil, "int8", d.buf.version)
}
// The underlying encoding of all these types is based on uint.
return binaryIgnoreUint(d.buf)
case vdl.String:
return binaryIgnoreString(d.buf)
case vdl.Array, vdl.List, vdl.Set, vdl.Map:
len, err := binaryDecodeLenOrArrayLen(d.buf, tt)
if err != nil {
return err
}
for ix := 0; ix < len; ix++ {
if kind == vdl.Set || kind == vdl.Map {
if err := d.ignoreValue(tt.Key()); err != nil {
return err
}
}
if kind == vdl.Array || kind == vdl.List || kind == vdl.Map {
if err := d.ignoreValue(tt.Elem()); err != nil {
return err
}
}
}
return nil
case vdl.Struct:
// Loop through decoding the 0-based field index and corresponding field.
for {
switch index, ctrl, err := binaryDecodeUintWithControl(d.buf); {
case err != nil:
return err
case ctrl == WireCtrlEnd:
return nil
case ctrl != 0:
return verror.New(errUnexpectedControlByte, nil, ctrl)
case index >= uint64(tt.NumField()):
return verror.New(errIndexOutOfRange, nil)
default:
ttfield := tt.Field(int(index))
if err := d.ignoreValue(ttfield.Type); err != nil {
return err
}
}
}
case vdl.Union:
switch index, err := binaryDecodeUint(d.buf); {
case err != nil:
return err
case index >= uint64(tt.NumField()):
return verror.New(errIndexOutOfRange, nil)
default:
ttfield := tt.Field(int(index))
return d.ignoreValue(ttfield.Type)
}
case vdl.Any:
var elemTid TypeId
switch x, ctrl, err := binaryDecodeUintWithControl(d.buf); {
case err != nil:
return err
case ctrl == WireCtrlNil:
return nil
case ctrl != 0:
return verror.New(errUnexpectedControlByte, nil, ctrl)
case d.buf.version == Version80:
elemTid = TypeId(x)
default:
if elemTid, err = d.refTypes.ReferencedTypeId(x); err != nil {
return err
}
}
elemType, err := d.typeDec.lookupType(elemTid)
if err != nil {
return err
}
return d.ignoreValue(elemType)
default:
panic(verror.New(errIgnoreValueUnhandledType, nil, tt))
}
}
func (d *Decoder) nextMessage() (TypeId, error) {
if leftover := d.buf.RemoveLimit(); leftover > 0 {
return 0, verror.New(errLeftOverBytes, nil, leftover)
}
if d.buf.version == 0 {
version, err := d.buf.ReadByte()
if err != nil {
return 0, verror.New(errEndedBeforeVersionByte, nil, err)
}
d.buf.version = Version(version)
if !isAllowedVersion(d.buf.version) {
return 0, verror.New(errBadVersionByte, nil, d.buf.version)
}
}
mid, cr, err := binaryDecodeIntWithControl(d.buf)
if err != nil {
return 0, err
}
if cr == WireCtrlTypeIncomplete {
mid, cr, err = binaryDecodeIntWithControl(d.buf)
if err != nil {
return 0, err
}
if cr != 0 || mid >= 0 {
// only can have incomplete types on new type messages
return 0, verror.New(errInvalid, nil)
}
d.typeIncomplete = true
} else if mid < 0 {
d.typeIncomplete = false
}
if cr != 0 {
return 0, verror.New(errBadControlCode, nil)
}
var tid TypeId
var hasAny, hasTypeObject bool
var hasLength bool
switch {
case mid < 0:
tid = TypeId(-mid)
hasLength = true
hasAny = false
hasTypeObject = false
case mid > 0:
tid = TypeId(mid)
t, err := d.typeDec.lookupType(tid)
if err != nil {
return 0, err
}
hasLength = hasChunkLen(t)
hasAny = containsAny(t)
hasTypeObject = containsTypeObject(t)
default:
return 0, verror.New(errDecodeZeroTypeID, nil)
}
if (hasAny || hasTypeObject) && d.buf.version != Version80 {
l, err := binaryDecodeUint(d.buf)
if err != nil {
return 0, err
}
for i := 0; i < int(l); i++ {
refId, err := binaryDecodeUint(d.buf)
if err != nil {
return 0, err
}
d.refTypes.AddTypeID(TypeId(refId))
}
}
if hasAny && d.buf.version != Version80 {
l, err := binaryDecodeUint(d.buf)
if err != nil {
return 0, err
}
for i := 0; i < int(l); i++ {
refAnyLen, err := binaryDecodeLen(d.buf)
if err != nil {
return 0, err
}
d.refAnyLens.AddAnyLen(refAnyLen)
}
}
if hasLength {
chunkLen, err := binaryDecodeUint(d.buf)
if err != nil {
return 0, err
}
d.buf.SetLimit(int(chunkLen))
}
return tid, nil
}
func (d *Decoder) typeIsNext() (bool, error) {
if d.buf.version == 0 {
version, err := d.buf.ReadByte()
if err != nil {
return false, verror.New(errEndedBeforeVersionByte, nil, err)
}
d.buf.version = Version(version)
if !isAllowedVersion(d.buf.version) {
return false, verror.New(errBadVersionByte, nil, d.buf.version)
}
}
mid, cr, _, err := binaryPeekIntWithControl(d.buf)
if err != nil {
return false, err
}
return mid < 0 || cr == WireCtrlTypeIncomplete, nil
}
func (d *Decoder) endMessage() error {
if leftover := d.buf.RemoveLimit(); leftover > 0 {
return verror.New(errLeftOverBytes, nil, leftover)
}
if err := d.refTypes.Reset(); err != nil {
return err
}
if err := d.refAnyLens.Reset(); err != nil {
return err
}
return nil
}
func (d *Decoder) reset() error {
return nil
}
type referencedTypes struct {
tids []TypeId
marker int
}
func (refTypes *referencedTypes) Reset() (err error) {
refTypes.tids = refTypes.tids[:0]
refTypes.marker = 0
return
}
func (refTypes *referencedTypes) AddTypeID(tid TypeId) {
refTypes.tids = append(refTypes.tids, tid)
}
func (refTypes *referencedTypes) ReferencedTypeId(index uint64) (TypeId, error) {
if index >= uint64(len(refTypes.tids)) {
return 0, verror.New(errInvalidTypeIdIndex, nil)
}
return refTypes.tids[index], nil
}
func (refTypes *referencedTypes) Mark() {
refTypes.marker = len(refTypes.tids)
}
type referencedAnyLens struct {
lens []int
marker int
}
func (refAnys *referencedAnyLens) Reset() (err error) {
refAnys.lens = refAnys.lens[:0]
return
}
func (refAnys *referencedAnyLens) AddAnyLen(len int) {
refAnys.lens = append(refAnys.lens, len)
}
func (refAnys *referencedAnyLens) ReferencedAnyLen(index uint64) (int, error) {
if index >= uint64(len(refAnys.lens)) {
return 0, verror.New(errInvalidAnyIndex, nil)
}
return refAnys.lens[index], nil
}
func (refAnys *referencedAnyLens) Mark() {
refAnys.marker = len(refAnys.lens)
}