internal/encoding/decode.go (953 lines of code) (raw):
// Copyright (C) 2017 Kale Blankenship
// Portions Copyright (c) Microsoft Corporation
package encoding
import (
"encoding/binary"
"errors"
"fmt"
"math"
"reflect"
"time"
"github.com/Azure/go-amqp/internal/buffer"
)
// unmarshaler is fulfilled by types that can unmarshal
// themselves from AMQP data.
type unmarshaler interface {
Unmarshal(r *buffer.Buffer) error
}
// unmarshal decodes AMQP encoded data into i.
//
// The decoding method is based on the type of i.
//
// If i implements unmarshaler, i.Unmarshal() will be called.
//
// Pointers to primitive types will be decoded via the appropriate read[Type] function.
//
// If i is a pointer to a pointer (**Type), it will be dereferenced and a new instance
// of (*Type) is allocated via reflection.
//
// Common map types (map[string]string, map[Symbol]any, and
// map[any]any), will be decoded via conversion to the mapStringAny,
// mapSymbolAny, and mapAnyAny types.
func Unmarshal(r *buffer.Buffer, i any) error {
if tryReadNull(r) {
return nil
}
switch t := i.(type) {
case *int:
val, err := readInt(r)
if err != nil {
return err
}
*t = val
case *int8:
val, err := readSbyte(r)
if err != nil {
return err
}
*t = val
case *int16:
val, err := readShort(r)
if err != nil {
return err
}
*t = val
case *int32:
val, err := readInt32(r)
if err != nil {
return err
}
*t = val
case *int64:
val, err := readLong(r)
if err != nil {
return err
}
*t = val
case *uint64:
val, err := readUlong(r)
if err != nil {
return err
}
*t = val
case *uint32:
val, err := readUint32(r)
if err != nil {
return err
}
*t = val
case **uint32: // fastpath for uint32 pointer fields
val, err := readUint32(r)
if err != nil {
return err
}
*t = &val
case *uint16:
val, err := readUshort(r)
if err != nil {
return err
}
*t = val
case *uint8:
val, err := ReadUbyte(r)
if err != nil {
return err
}
*t = val
case *float32:
val, err := readFloat(r)
if err != nil {
return err
}
*t = val
case *float64:
val, err := readDouble(r)
if err != nil {
return err
}
*t = val
case *string:
val, err := ReadString(r)
if err != nil {
return err
}
*t = val
case *Symbol:
s, err := ReadString(r)
if err != nil {
return err
}
*t = Symbol(s)
case *[]byte:
val, err := readBinary(r)
if err != nil {
return err
}
*t = val
case *bool:
b, err := readBool(r)
if err != nil {
return err
}
*t = b
case *time.Time:
ts, err := readTimestamp(r)
if err != nil {
return err
}
*t = ts
case *[]int8:
return (*arrayInt8)(t).Unmarshal(r)
case *[]uint16:
return (*arrayUint16)(t).Unmarshal(r)
case *[]int16:
return (*arrayInt16)(t).Unmarshal(r)
case *[]uint32:
return (*arrayUint32)(t).Unmarshal(r)
case *[]int32:
return (*arrayInt32)(t).Unmarshal(r)
case *[]uint64:
return (*arrayUint64)(t).Unmarshal(r)
case *[]int64:
return (*arrayInt64)(t).Unmarshal(r)
case *[]float32:
return (*arrayFloat)(t).Unmarshal(r)
case *[]float64:
return (*arrayDouble)(t).Unmarshal(r)
case *[]bool:
return (*arrayBool)(t).Unmarshal(r)
case *[]string:
return (*arrayString)(t).Unmarshal(r)
case *[]Symbol:
return (*arraySymbol)(t).Unmarshal(r)
case *[][]byte:
return (*arrayBinary)(t).Unmarshal(r)
case *[]time.Time:
return (*arrayTimestamp)(t).Unmarshal(r)
case *[]UUID:
return (*arrayUUID)(t).Unmarshal(r)
case *[]any:
return (*list)(t).Unmarshal(r)
case *map[any]any:
return (*mapAnyAny)(t).Unmarshal(r)
case *map[string]any:
return (*mapStringAny)(t).Unmarshal(r)
case *map[Symbol]any:
return (*mapSymbolAny)(t).Unmarshal(r)
case *DeliveryState:
type_, _, err := PeekMessageType(r.Bytes())
if err != nil {
return err
}
switch AMQPType(type_) {
case TypeCodeStateAccepted:
*t = new(StateAccepted)
case TypeCodeStateModified:
*t = new(StateModified)
case TypeCodeStateReceived:
*t = new(StateReceived)
case TypeCodeStateRejected:
*t = new(StateRejected)
case TypeCodeStateReleased:
*t = new(StateReleased)
default:
return fmt.Errorf("unexpected type %d for deliveryState", type_)
}
return Unmarshal(r, *t)
case *any:
v, err := ReadAny(r)
if err != nil {
return err
}
*t = v
case unmarshaler:
return t.Unmarshal(r)
default:
// handle **T
v := reflect.Indirect(reflect.ValueOf(i))
// can't unmarshal into a non-pointer
if v.Kind() != reflect.Ptr {
return fmt.Errorf("unable to unmarshal %T", i)
}
// if nil pointer, allocate a new value to
// unmarshal into
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
return Unmarshal(r, v.Interface())
}
return nil
}
// unmarshalComposite is a helper for use in a composite's unmarshal() function.
//
// The composite from r will be unmarshaled into zero or more fields. An error
// will be returned if typ does not match the decoded type.
func UnmarshalComposite(r *buffer.Buffer, type_ AMQPType, fields ...UnmarshalField) error {
cType, numFields, err := readCompositeHeader(r)
if err != nil {
return err
}
// check type matches expectation
if cType != type_ {
return fmt.Errorf("invalid header %#0x for %#0x", cType, type_)
}
// Validate the field count is less than or equal to the number of fields
// provided. Fields may be omitted by the sender if they are not set.
if numFields > int64(len(fields)) {
return fmt.Errorf("invalid field count %d for %#0x", numFields, type_)
}
for i, field := range fields[:numFields] {
// If the field is null and handleNull is set, call it.
if tryReadNull(r) {
if field.HandleNull != nil {
err = field.HandleNull()
if err != nil {
return err
}
}
continue
}
// Unmarshal each of the received fields.
err = Unmarshal(r, field.Field)
if err != nil {
return fmt.Errorf("unmarshaling field %d: %v", i, err)
}
}
// check and call handleNull for the remaining fields
for _, field := range fields[numFields:] {
if field.HandleNull != nil {
err = field.HandleNull()
if err != nil {
return err
}
}
}
return nil
}
// unmarshalField is a struct that contains a field to be unmarshaled into.
//
// An optional nullHandler can be set. If the composite field being unmarshaled
// is null and handleNull is not nil, nullHandler will be called.
type UnmarshalField struct {
Field any
HandleNull NullHandler
}
// nullHandler is a function to be called when a composite's field
// is null.
type NullHandler func() error
func readType(r *buffer.Buffer) (AMQPType, error) {
n, err := r.ReadByte()
return AMQPType(n), err
}
func peekType(r *buffer.Buffer) (AMQPType, error) {
n, err := r.PeekByte()
return AMQPType(n), err
}
// readCompositeHeader reads and consumes the composite header from r.
func readCompositeHeader(r *buffer.Buffer) (_ AMQPType, fields int64, _ error) {
type_, err := readType(r)
if err != nil {
return 0, 0, err
}
// compsites always start with 0x0
if type_ != 0 {
return 0, 0, fmt.Errorf("invalid composite header %#02x", type_)
}
// next, the composite type is encoded as an AMQP uint8
v, err := readUlong(r)
if err != nil {
return 0, 0, err
}
// fields are represented as a list
fields, err = readListHeader(r)
return AMQPType(v), fields, err
}
func readListHeader(r *buffer.Buffer) (length int64, _ error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
listLength := r.Len()
switch type_ {
case TypeCodeList0:
return 0, nil
case TypeCodeList8:
buf, ok := r.Next(2)
if !ok {
return 0, errors.New("invalid length")
}
_ = buf[1]
size := int(buf[0])
if size > listLength-1 {
return 0, errors.New("invalid length")
}
length = int64(buf[1])
case TypeCodeList32:
buf, ok := r.Next(8)
if !ok {
return 0, errors.New("invalid length")
}
_ = buf[7]
size := int(binary.BigEndian.Uint32(buf[:4]))
if size > listLength-4 {
return 0, errors.New("invalid length")
}
length = int64(binary.BigEndian.Uint32(buf[4:8]))
default:
return 0, fmt.Errorf("type code %#02x is not a recognized list type", type_)
}
return length, nil
}
func readArrayHeader(r *buffer.Buffer) (length int64, _ error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
arrayLength := r.Len()
switch type_ {
case TypeCodeArray8:
buf, ok := r.Next(2)
if !ok {
return 0, errors.New("invalid length")
}
_ = buf[1]
size := int(buf[0])
if size > arrayLength-1 {
return 0, errors.New("invalid length")
}
length = int64(buf[1])
case TypeCodeArray32:
buf, ok := r.Next(8)
if !ok {
return 0, errors.New("invalid length")
}
_ = buf[7]
size := binary.BigEndian.Uint32(buf[:4])
if int(size) > arrayLength-4 {
return 0, fmt.Errorf("invalid length for type %02x", type_)
}
length = int64(binary.BigEndian.Uint32(buf[4:8]))
default:
return 0, fmt.Errorf("type code %#02x is not a recognized array type", type_)
}
return length, nil
}
func ReadString(r *buffer.Buffer) (string, error) {
type_, err := readType(r)
if err != nil {
return "", err
}
var length int64
switch type_ {
case TypeCodeStr8, TypeCodeSym8:
n, err := r.ReadByte()
if err != nil {
return "", err
}
length = int64(n)
case TypeCodeStr32, TypeCodeSym32:
buf, ok := r.Next(4)
if !ok {
return "", fmt.Errorf("invalid length for type %#02x", type_)
}
length = int64(binary.BigEndian.Uint32(buf))
default:
return "", fmt.Errorf("type code %#02x is not a recognized string type", type_)
}
buf, ok := r.Next(length)
if !ok {
return "", errors.New("invalid length")
}
return string(buf), nil
}
func readBinary(r *buffer.Buffer) ([]byte, error) {
type_, err := readType(r)
if err != nil {
return nil, err
}
var length int64
switch type_ {
case TypeCodeVbin8:
n, err := r.ReadByte()
if err != nil {
return nil, err
}
length = int64(n)
case TypeCodeVbin32:
buf, ok := r.Next(4)
if !ok {
return nil, fmt.Errorf("invalid length for type %#02x", type_)
}
length = int64(binary.BigEndian.Uint32(buf))
default:
return nil, fmt.Errorf("type code %#02x is not a recognized binary type", type_)
}
if length == 0 {
// An empty value and a nil value are distinct,
// ensure that the returned value is not nil in this case.
return make([]byte, 0), nil
}
buf, ok := r.Next(length)
if !ok {
return nil, errors.New("invalid length")
}
return append([]byte(nil), buf...), nil
}
func ReadAny(r *buffer.Buffer) (any, error) {
if tryReadNull(r) {
return nil, nil
}
type_, err := peekType(r)
if err != nil {
return nil, errors.New("invalid length")
}
switch type_ {
// composite
case 0x0:
return readComposite(r)
// bool
case TypeCodeBool, TypeCodeBoolTrue, TypeCodeBoolFalse:
return readBool(r)
// uint
case TypeCodeUbyte:
return ReadUbyte(r)
case TypeCodeUshort:
return readUshort(r)
case TypeCodeUint,
TypeCodeSmallUint,
TypeCodeUint0:
return readUint32(r)
case TypeCodeUlong,
TypeCodeSmallUlong,
TypeCodeUlong0:
return readUlong(r)
// int
case TypeCodeByte:
return readSbyte(r)
case TypeCodeShort:
return readShort(r)
case TypeCodeInt,
TypeCodeSmallint:
return readInt32(r)
case TypeCodeLong,
TypeCodeSmalllong:
return readLong(r)
// floating point
case TypeCodeFloat:
return readFloat(r)
case TypeCodeDouble:
return readDouble(r)
// binary
case TypeCodeVbin8, TypeCodeVbin32:
return readBinary(r)
// strings
case TypeCodeStr8, TypeCodeStr32:
return ReadString(r)
case TypeCodeSym8, TypeCodeSym32:
// symbols currently decoded as string to avoid
// exposing symbol type in message, this may need
// to change if users need to distinguish strings
// from symbols
return ReadString(r)
// timestamp
case TypeCodeTimestamp:
return readTimestamp(r)
// UUID
case TypeCodeUUID:
return readUUID(r)
// arrays
case TypeCodeArray8, TypeCodeArray32:
return readAnyArray(r)
// lists
case TypeCodeList0, TypeCodeList8, TypeCodeList32:
return readAnyList(r)
// maps
case TypeCodeMap8:
return readAnyMap(r)
case TypeCodeMap32:
return readAnyMap(r)
// TODO: implement
case TypeCodeDecimal32:
return nil, errors.New("decimal32 not implemented")
case TypeCodeDecimal64:
return nil, errors.New("decimal64 not implemented")
case TypeCodeDecimal128:
return nil, errors.New("decimal128 not implemented")
case TypeCodeChar:
return nil, errors.New("char not implemented")
default:
return nil, fmt.Errorf("unknown type %#02x", type_)
}
}
func readAnyMap(r *buffer.Buffer) (any, error) {
var m map[any]any
err := (*mapAnyAny)(&m).Unmarshal(r)
if err != nil {
return nil, err
}
if len(m) == 0 {
return m, nil
}
stringKeys := true
Loop:
for key := range m {
switch key.(type) {
case string:
case Symbol:
default:
stringKeys = false
break Loop
}
}
if stringKeys {
mm := make(map[string]any, len(m))
for key, value := range m {
switch key := key.(type) {
case string:
mm[key] = value
case Symbol:
mm[string(key)] = value
}
}
return mm, nil
}
return m, nil
}
func readAnyList(r *buffer.Buffer) (any, error) {
var a []any
err := (*list)(&a).Unmarshal(r)
return a, err
}
func readAnyArray(r *buffer.Buffer) (any, error) {
// get the array type
buf := r.Bytes()
if len(buf) < 1 {
return nil, errors.New("invalid length")
}
var typeIdx int
switch AMQPType(buf[0]) {
case TypeCodeArray8:
typeIdx = 3
case TypeCodeArray32:
typeIdx = 9
default:
return nil, fmt.Errorf("invalid array type %02x", buf[0])
}
if len(buf) < typeIdx+1 {
return nil, errors.New("invalid length")
}
switch AMQPType(buf[typeIdx]) {
case TypeCodeByte:
var a []int8
err := (*arrayInt8)(&a).Unmarshal(r)
return a, err
case TypeCodeUbyte:
var a ArrayUByte
err := a.Unmarshal(r)
return a, err
case TypeCodeUshort:
var a []uint16
err := (*arrayUint16)(&a).Unmarshal(r)
return a, err
case TypeCodeShort:
var a []int16
err := (*arrayInt16)(&a).Unmarshal(r)
return a, err
case TypeCodeUint0, TypeCodeSmallUint, TypeCodeUint:
var a []uint32
err := (*arrayUint32)(&a).Unmarshal(r)
return a, err
case TypeCodeSmallint, TypeCodeInt:
var a []int32
err := (*arrayInt32)(&a).Unmarshal(r)
return a, err
case TypeCodeUlong0, TypeCodeSmallUlong, TypeCodeUlong:
var a []uint64
err := (*arrayUint64)(&a).Unmarshal(r)
return a, err
case TypeCodeSmalllong, TypeCodeLong:
var a []int64
err := (*arrayInt64)(&a).Unmarshal(r)
return a, err
case TypeCodeFloat:
var a []float32
err := (*arrayFloat)(&a).Unmarshal(r)
return a, err
case TypeCodeDouble:
var a []float64
err := (*arrayDouble)(&a).Unmarshal(r)
return a, err
case TypeCodeBool, TypeCodeBoolTrue, TypeCodeBoolFalse:
var a []bool
err := (*arrayBool)(&a).Unmarshal(r)
return a, err
case TypeCodeStr8, TypeCodeStr32:
var a []string
err := (*arrayString)(&a).Unmarshal(r)
return a, err
case TypeCodeSym8, TypeCodeSym32:
var a []Symbol
err := (*arraySymbol)(&a).Unmarshal(r)
return a, err
case TypeCodeVbin8, TypeCodeVbin32:
var a [][]byte
err := (*arrayBinary)(&a).Unmarshal(r)
return a, err
case TypeCodeTimestamp:
var a []time.Time
err := (*arrayTimestamp)(&a).Unmarshal(r)
return a, err
case TypeCodeUUID:
var a []UUID
err := (*arrayUUID)(&a).Unmarshal(r)
return a, err
default:
return nil, fmt.Errorf("array decoding not implemented for %#02x", buf[typeIdx])
}
}
func readComposite(r *buffer.Buffer) (any, error) {
buf := r.Bytes()
if len(buf) < 2 {
return nil, errors.New("invalid length for composite")
}
// compsites start with 0x0
if AMQPType(buf[0]) != 0x0 {
return nil, fmt.Errorf("invalid composite header %#02x", buf[0])
}
var compositeType uint64
switch AMQPType(buf[1]) {
case TypeCodeSmallUlong:
if len(buf) < 3 {
return nil, errors.New("invalid length for smallulong")
}
compositeType = uint64(buf[2])
case TypeCodeUlong:
if len(buf) < 10 {
return nil, errors.New("invalid length for ulong")
}
compositeType = binary.BigEndian.Uint64(buf[2:])
}
if compositeType > math.MaxUint8 {
// try as described type
var dt DescribedType
err := dt.Unmarshal(r)
return dt, err
}
switch AMQPType(compositeType) {
// Error
case TypeCodeError:
t := new(Error)
err := t.Unmarshal(r)
return t, err
// Lifetime Policies
case TypeCodeDeleteOnClose:
t := DeleteOnClose
err := t.Unmarshal(r)
return t, err
case TypeCodeDeleteOnNoMessages:
t := DeleteOnNoMessages
err := t.Unmarshal(r)
return t, err
case TypeCodeDeleteOnNoLinks:
t := DeleteOnNoLinks
err := t.Unmarshal(r)
return t, err
case TypeCodeDeleteOnNoLinksOrMessages:
t := DeleteOnNoLinksOrMessages
err := t.Unmarshal(r)
return t, err
// Delivery States
case TypeCodeStateAccepted:
t := new(StateAccepted)
err := t.Unmarshal(r)
return t, err
case TypeCodeStateModified:
t := new(StateModified)
err := t.Unmarshal(r)
return t, err
case TypeCodeStateReceived:
t := new(StateReceived)
err := t.Unmarshal(r)
return t, err
case TypeCodeStateRejected:
t := new(StateRejected)
err := t.Unmarshal(r)
return t, err
case TypeCodeStateReleased:
t := new(StateReleased)
err := t.Unmarshal(r)
return t, err
case TypeCodeOpen,
TypeCodeBegin,
TypeCodeAttach,
TypeCodeFlow,
TypeCodeTransfer,
TypeCodeDisposition,
TypeCodeDetach,
TypeCodeEnd,
TypeCodeClose,
TypeCodeSource,
TypeCodeTarget,
TypeCodeMessageHeader,
TypeCodeDeliveryAnnotations,
TypeCodeMessageAnnotations,
TypeCodeMessageProperties,
TypeCodeApplicationProperties,
TypeCodeApplicationData,
TypeCodeAMQPSequence,
TypeCodeAMQPValue,
TypeCodeFooter,
TypeCodeSASLMechanism,
TypeCodeSASLInit,
TypeCodeSASLChallenge,
TypeCodeSASLResponse,
TypeCodeSASLOutcome:
return nil, fmt.Errorf("readComposite unmarshal not implemented for %#02x", compositeType)
default:
// try as described type
var dt DescribedType
err := dt.Unmarshal(r)
return dt, err
}
}
func readTimestamp(r *buffer.Buffer) (time.Time, error) {
type_, err := readType(r)
if err != nil {
return time.Time{}, err
}
if type_ != TypeCodeTimestamp {
return time.Time{}, fmt.Errorf("invalid type for timestamp %02x", type_)
}
n, err := r.ReadUint64()
ms := int64(n)
return time.UnixMilli(ms), err
}
func readInt(r *buffer.Buffer) (int, error) {
type_, err := peekType(r)
if err != nil {
return 0, err
}
switch type_ {
// Unsigned
case TypeCodeUbyte:
n, err := ReadUbyte(r)
return int(n), err
case TypeCodeUshort:
n, err := readUshort(r)
return int(n), err
case TypeCodeUint0, TypeCodeSmallUint, TypeCodeUint:
n, err := readUint32(r)
return int(n), err
case TypeCodeUlong0, TypeCodeSmallUlong, TypeCodeUlong:
n, err := readUlong(r)
return int(n), err
// Signed
case TypeCodeByte:
n, err := readSbyte(r)
return int(n), err
case TypeCodeShort:
n, err := readShort(r)
return int(n), err
case TypeCodeSmallint, TypeCodeInt:
n, err := readInt32(r)
return int(n), err
case TypeCodeSmalllong, TypeCodeLong:
n, err := readLong(r)
return int(n), err
default:
return 0, fmt.Errorf("type code %#02x is not a recognized number type", type_)
}
}
func readLong(r *buffer.Buffer) (int64, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
switch type_ {
case TypeCodeSmalllong:
n, err := r.ReadByte()
return int64(int8(n)), err
case TypeCodeLong:
n, err := r.ReadUint64()
return int64(n), err
default:
return 0, fmt.Errorf("invalid type for uint32 %02x", type_)
}
}
func readInt32(r *buffer.Buffer) (int32, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
switch type_ {
case TypeCodeSmallint:
n, err := r.ReadByte()
return int32(int8(n)), err
case TypeCodeInt:
n, err := r.ReadUint32()
return int32(n), err
default:
return 0, fmt.Errorf("invalid type for int32 %02x", type_)
}
}
func readShort(r *buffer.Buffer) (int16, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
if type_ != TypeCodeShort {
return 0, fmt.Errorf("invalid type for short %02x", type_)
}
n, err := r.ReadUint16()
return int16(n), err
}
func readSbyte(r *buffer.Buffer) (int8, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
if type_ != TypeCodeByte {
return 0, fmt.Errorf("invalid type for int8 %02x", type_)
}
n, err := r.ReadByte()
return int8(n), err
}
func ReadUbyte(r *buffer.Buffer) (uint8, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
if type_ != TypeCodeUbyte {
return 0, fmt.Errorf("invalid type for ubyte %02x", type_)
}
return r.ReadByte()
}
func readUshort(r *buffer.Buffer) (uint16, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
if type_ != TypeCodeUshort {
return 0, fmt.Errorf("invalid type for ushort %02x", type_)
}
return r.ReadUint16()
}
func readUint32(r *buffer.Buffer) (uint32, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
switch type_ {
case TypeCodeUint0:
return 0, nil
case TypeCodeSmallUint:
n, err := r.ReadByte()
return uint32(n), err
case TypeCodeUint:
return r.ReadUint32()
default:
return 0, fmt.Errorf("invalid type for uint32 %02x", type_)
}
}
func readUlong(r *buffer.Buffer) (uint64, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
switch type_ {
case TypeCodeUlong0:
return 0, nil
case TypeCodeSmallUlong:
n, err := r.ReadByte()
return uint64(n), err
case TypeCodeUlong:
return r.ReadUint64()
default:
return 0, fmt.Errorf("invalid type for uint32 %02x", type_)
}
}
func readFloat(r *buffer.Buffer) (float32, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
if type_ != TypeCodeFloat {
return 0, fmt.Errorf("invalid type for float32 %02x", type_)
}
bits, err := r.ReadUint32()
return math.Float32frombits(bits), err
}
func readDouble(r *buffer.Buffer) (float64, error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
if type_ != TypeCodeDouble {
return 0, fmt.Errorf("invalid type for float64 %02x", type_)
}
bits, err := r.ReadUint64()
return math.Float64frombits(bits), err
}
func readBool(r *buffer.Buffer) (bool, error) {
type_, err := readType(r)
if err != nil {
return false, err
}
switch type_ {
case TypeCodeBool:
b, err := r.ReadByte()
return b != 0, err
case TypeCodeBoolTrue:
return true, nil
case TypeCodeBoolFalse:
return false, nil
default:
return false, fmt.Errorf("type code %#02x is not a recognized bool type", type_)
}
}
func readUint(r *buffer.Buffer) (value uint64, _ error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
switch type_ {
case TypeCodeUint0, TypeCodeUlong0:
return 0, nil
case TypeCodeUbyte, TypeCodeSmallUint, TypeCodeSmallUlong:
n, err := r.ReadByte()
return uint64(n), err
case TypeCodeUshort:
n, err := r.ReadUint16()
return uint64(n), err
case TypeCodeUint:
n, err := r.ReadUint32()
return uint64(n), err
case TypeCodeUlong:
return r.ReadUint64()
default:
return 0, fmt.Errorf("type code %#02x is not a recognized number type", type_)
}
}
func readUUID(r *buffer.Buffer) (UUID, error) {
var uuid UUID
type_, err := readType(r)
if err != nil {
return uuid, err
}
if type_ != TypeCodeUUID {
return uuid, fmt.Errorf("type code %#00x is not a UUID", type_)
}
buf, ok := r.Next(16)
if !ok {
return uuid, errors.New("invalid length")
}
copy(uuid[:], buf)
return uuid, nil
}
func readMapHeader(r *buffer.Buffer) (count uint32, _ error) {
type_, err := readType(r)
if err != nil {
return 0, err
}
length := r.Len()
switch type_ {
case TypeCodeMap8:
buf, ok := r.Next(2)
if !ok {
return 0, errors.New("invalid length")
}
_ = buf[1]
size := int(buf[0])
if size > length-1 {
return 0, errors.New("invalid length")
}
count = uint32(buf[1])
case TypeCodeMap32:
buf, ok := r.Next(8)
if !ok {
return 0, errors.New("invalid length")
}
_ = buf[7]
size := int(binary.BigEndian.Uint32(buf[:4]))
if size > length-4 {
return 0, errors.New("invalid length")
}
count = binary.BigEndian.Uint32(buf[4:8])
default:
return 0, fmt.Errorf("invalid map type %#02x", type_)
}
if int(count) > r.Len() {
return 0, errors.New("invalid length")
}
return count, nil
}