internal/encoding/types.go (1,263 lines of code) (raw):
package encoding
import (
"encoding/binary"
"errors"
"fmt"
"math"
"reflect"
"time"
"unicode/utf8"
"github.com/Azure/go-amqp/internal/buffer"
)
type AMQPType uint8
// Type codes
const (
TypeCodeNull AMQPType = 0x40
// Bool
TypeCodeBool AMQPType = 0x56 // boolean with the octet 0x00 being false and octet 0x01 being true
TypeCodeBoolTrue AMQPType = 0x41
TypeCodeBoolFalse AMQPType = 0x42
// Unsigned
TypeCodeUbyte AMQPType = 0x50 // 8-bit unsigned integer (1)
TypeCodeUshort AMQPType = 0x60 // 16-bit unsigned integer in network byte order (2)
TypeCodeUint AMQPType = 0x70 // 32-bit unsigned integer in network byte order (4)
TypeCodeSmallUint AMQPType = 0x52 // unsigned integer value in the range 0 to 255 inclusive (1)
TypeCodeUint0 AMQPType = 0x43 // the uint value 0 (0)
TypeCodeUlong AMQPType = 0x80 // 64-bit unsigned integer in network byte order (8)
TypeCodeSmallUlong AMQPType = 0x53 // unsigned long value in the range 0 to 255 inclusive (1)
TypeCodeUlong0 AMQPType = 0x44 // the ulong value 0 (0)
// Signed
TypeCodeByte AMQPType = 0x51 // 8-bit two's-complement integer (1)
TypeCodeShort AMQPType = 0x61 // 16-bit two's-complement integer in network byte order (2)
TypeCodeInt AMQPType = 0x71 // 32-bit two's-complement integer in network byte order (4)
TypeCodeSmallint AMQPType = 0x54 // 8-bit two's-complement integer (1)
TypeCodeLong AMQPType = 0x81 // 64-bit two's-complement integer in network byte order (8)
TypeCodeSmalllong AMQPType = 0x55 // 8-bit two's-complement integer
// Decimal
TypeCodeFloat AMQPType = 0x72 // IEEE 754-2008 binary32 (4)
TypeCodeDouble AMQPType = 0x82 // IEEE 754-2008 binary64 (8)
TypeCodeDecimal32 AMQPType = 0x74 // IEEE 754-2008 decimal32 using the Binary Integer Decimal encoding (4)
TypeCodeDecimal64 AMQPType = 0x84 // IEEE 754-2008 decimal64 using the Binary Integer Decimal encoding (8)
TypeCodeDecimal128 AMQPType = 0x94 // IEEE 754-2008 decimal128 using the Binary Integer Decimal encoding (16)
// Other
TypeCodeChar AMQPType = 0x73 // a UTF-32BE encoded Unicode character (4)
TypeCodeTimestamp AMQPType = 0x83 // 64-bit two's-complement integer representing milliseconds since the unix epoch
TypeCodeUUID AMQPType = 0x98 // UUID as defined in section 4.1.2 of RFC-4122
// Variable Length
TypeCodeVbin8 AMQPType = 0xa0 // up to 2^8 - 1 octets of binary data (1 + variable)
TypeCodeVbin32 AMQPType = 0xb0 // up to 2^32 - 1 octets of binary data (4 + variable)
TypeCodeStr8 AMQPType = 0xa1 // up to 2^8 - 1 octets worth of UTF-8 Unicode (with no byte order mark) (1 + variable)
TypeCodeStr32 AMQPType = 0xb1 // up to 2^32 - 1 octets worth of UTF-8 Unicode (with no byte order mark) (4 +variable)
TypeCodeSym8 AMQPType = 0xa3 // up to 2^8 - 1 seven bit ASCII characters representing a symbolic value (1 + variable)
TypeCodeSym32 AMQPType = 0xb3 // up to 2^32 - 1 seven bit ASCII characters representing a symbolic value (4 + variable)
// Compound
TypeCodeList0 AMQPType = 0x45 // the empty list (i.e. the list with no elements) (0)
TypeCodeList8 AMQPType = 0xc0 // up to 2^8 - 1 list elements with total size less than 2^8 octets (1 + compound)
TypeCodeList32 AMQPType = 0xd0 // up to 2^32 - 1 list elements with total size less than 2^32 octets (4 + compound)
TypeCodeMap8 AMQPType = 0xc1 // up to 2^8 - 1 octets of encoded map data (1 + compound)
TypeCodeMap32 AMQPType = 0xd1 // up to 2^32 - 1 octets of encoded map data (4 + compound)
TypeCodeArray8 AMQPType = 0xe0 // up to 2^8 - 1 array elements with total size less than 2^8 octets (1 + array)
TypeCodeArray32 AMQPType = 0xf0 // up to 2^32 - 1 array elements with total size less than 2^32 octets (4 + array)
// Composites
TypeCodeOpen AMQPType = 0x10
TypeCodeBegin AMQPType = 0x11
TypeCodeAttach AMQPType = 0x12
TypeCodeFlow AMQPType = 0x13
TypeCodeTransfer AMQPType = 0x14
TypeCodeDisposition AMQPType = 0x15
TypeCodeDetach AMQPType = 0x16
TypeCodeEnd AMQPType = 0x17
TypeCodeClose AMQPType = 0x18
TypeCodeSource AMQPType = 0x28
TypeCodeTarget AMQPType = 0x29
TypeCodeError AMQPType = 0x1d
TypeCodeMessageHeader AMQPType = 0x70
TypeCodeDeliveryAnnotations AMQPType = 0x71
TypeCodeMessageAnnotations AMQPType = 0x72
TypeCodeMessageProperties AMQPType = 0x73
TypeCodeApplicationProperties AMQPType = 0x74
TypeCodeApplicationData AMQPType = 0x75
TypeCodeAMQPSequence AMQPType = 0x76
TypeCodeAMQPValue AMQPType = 0x77
TypeCodeFooter AMQPType = 0x78
TypeCodeStateReceived AMQPType = 0x23
TypeCodeStateAccepted AMQPType = 0x24
TypeCodeStateRejected AMQPType = 0x25
TypeCodeStateReleased AMQPType = 0x26
TypeCodeStateModified AMQPType = 0x27
TypeCodeSASLMechanism AMQPType = 0x40
TypeCodeSASLInit AMQPType = 0x41
TypeCodeSASLChallenge AMQPType = 0x42
TypeCodeSASLResponse AMQPType = 0x43
TypeCodeSASLOutcome AMQPType = 0x44
TypeCodeDeleteOnClose AMQPType = 0x2b
TypeCodeDeleteOnNoLinks AMQPType = 0x2c
TypeCodeDeleteOnNoMessages AMQPType = 0x2d
TypeCodeDeleteOnNoLinksOrMessages AMQPType = 0x2e
)
func ValidateExpiryPolicy(e ExpiryPolicy) error {
switch e {
case ExpiryLinkDetach,
ExpirySessionEnd,
ExpiryConnectionClose,
ExpiryNever:
return nil
default:
return fmt.Errorf("unknown expiry-policy %q", e)
}
}
type Role bool
const (
RoleSender Role = false
RoleReceiver Role = true
)
func (rl Role) String() string {
if rl {
return "Receiver"
}
return "Sender"
}
func (rl *Role) Unmarshal(r *buffer.Buffer) error {
b, err := readBool(r)
*rl = Role(b)
return err
}
func (rl Role) Marshal(wr *buffer.Buffer) error {
return Marshal(wr, (bool)(rl))
}
type SASLCode uint8
// SASL Codes
const (
CodeSASLOK SASLCode = iota // Connection authentication succeeded.
CodeSASLAuth // Connection authentication failed due to an unspecified problem with the supplied credentials.
CodeSASLSysPerm // Connection authentication failed due to a system error that is unlikely to be corrected without intervention.
)
func (s SASLCode) Marshal(wr *buffer.Buffer) error {
return Marshal(wr, uint8(s))
}
func (s *SASLCode) Unmarshal(r *buffer.Buffer) error {
n, err := ReadUbyte(r)
*s = SASLCode(n)
return err
}
type Unsettled map[string]DeliveryState
func (u Unsettled) Marshal(wr *buffer.Buffer) error {
return writeMap(wr, u)
}
func (u *Unsettled) Unmarshal(r *buffer.Buffer) error {
count, err := readMapHeader(r)
if err != nil {
return err
}
m := make(Unsettled, count/2)
for i := uint32(0); i < count; i += 2 {
key, err := ReadString(r)
if err != nil {
return err
}
var value DeliveryState
err = Unmarshal(r, &value)
if err != nil {
return err
}
m[key] = value
}
*u = m
return nil
}
// peekMessageType reads the message type without
// modifying any data.
func PeekMessageType(buf []byte) (uint8, uint8, error) {
if len(buf) < 3 {
return 0, 0, errors.New("invalid message")
}
if buf[0] != 0 {
return 0, 0, fmt.Errorf("invalid composite header %02x", buf[0])
}
// copied from readUlong to avoid allocations
t := AMQPType(buf[1])
if t == TypeCodeUlong0 {
return 0, 2, nil
}
if t == TypeCodeSmallUlong {
if len(buf[2:]) == 0 {
return 0, 0, errors.New("invalid ulong")
}
return buf[2], 3, nil
}
if t != TypeCodeUlong {
return 0, 0, fmt.Errorf("invalid type for uint32 %02x", t)
}
if len(buf[2:]) < 8 {
return 0, 0, errors.New("invalid ulong")
}
v := binary.BigEndian.Uint64(buf[2:10])
return uint8(v), 10, nil
}
func tryReadNull(r *buffer.Buffer) bool {
if r.Len() > 0 && AMQPType(r.Bytes()[0]) == TypeCodeNull {
r.Skip(1)
return true
}
return false
}
type Milliseconds time.Duration
func (m Milliseconds) Marshal(wr *buffer.Buffer) error {
writeUint32(wr, uint32(m/Milliseconds(time.Millisecond)))
return nil
}
func (m *Milliseconds) Unmarshal(r *buffer.Buffer) error {
n, err := readUint(r)
*m = Milliseconds(time.Duration(n) * time.Millisecond)
return err
}
// mapAnyAny is used to decode AMQP maps who's keys are undefined or
// inconsistently typed.
type mapAnyAny map[any]any
func (m mapAnyAny) Marshal(wr *buffer.Buffer) error {
return writeMap(wr, map[any]any(m))
}
func (m *mapAnyAny) Unmarshal(r *buffer.Buffer) error {
count, err := readMapHeader(r)
if err != nil {
return err
}
mm := make(mapAnyAny, count/2)
for i := uint32(0); i < count; i += 2 {
key, err := ReadAny(r)
if err != nil {
return err
}
value, err := ReadAny(r)
if err != nil {
return err
}
// https://golang.org/ref/spec#Map_types:
// The comparison operators == and != must be fully defined
// for operands of the key type; thus the key type must not
// be a function, map, or slice.
switch reflect.ValueOf(key).Kind() {
case reflect.Slice, reflect.Func, reflect.Map:
return errors.New("invalid map key")
}
mm[key] = value
}
*m = mm
return nil
}
// mapStringAny is used to decode AMQP maps that have string keys
type mapStringAny map[string]any
func (m mapStringAny) Marshal(wr *buffer.Buffer) error {
return writeMap(wr, map[string]any(m))
}
func (m *mapStringAny) Unmarshal(r *buffer.Buffer) error {
count, err := readMapHeader(r)
if err != nil {
return err
}
mm := make(mapStringAny, count/2)
for i := uint32(0); i < count; i += 2 {
key, err := ReadString(r)
if err != nil {
return err
}
value, err := ReadAny(r)
if err != nil {
return err
}
mm[key] = value
}
*m = mm
return nil
}
// mapStringAny is used to decode AMQP maps that have Symbol keys
type mapSymbolAny map[Symbol]any
func (m mapSymbolAny) Marshal(wr *buffer.Buffer) error {
return writeMap(wr, map[Symbol]any(m))
}
func (m *mapSymbolAny) Unmarshal(r *buffer.Buffer) error {
count, err := readMapHeader(r)
if err != nil {
return err
}
mm := make(mapSymbolAny, count/2)
for i := uint32(0); i < count; i += 2 {
key, err := ReadString(r)
if err != nil {
return err
}
value, err := ReadAny(r)
if err != nil {
return err
}
mm[Symbol(key)] = value
}
*m = mm
return nil
}
type LifetimePolicy uint8
const (
DeleteOnClose = LifetimePolicy(TypeCodeDeleteOnClose)
DeleteOnNoLinks = LifetimePolicy(TypeCodeDeleteOnNoLinks)
DeleteOnNoMessages = LifetimePolicy(TypeCodeDeleteOnNoMessages)
DeleteOnNoLinksOrMessages = LifetimePolicy(TypeCodeDeleteOnNoLinksOrMessages)
)
func (p LifetimePolicy) Marshal(wr *buffer.Buffer) error {
wr.Append([]byte{
0x0,
byte(TypeCodeSmallUlong),
byte(p),
byte(TypeCodeList0),
})
return nil
}
func (p *LifetimePolicy) Unmarshal(r *buffer.Buffer) error {
typ, fields, err := readCompositeHeader(r)
if err != nil {
return err
}
if fields != 0 {
return fmt.Errorf("invalid size %d for lifetime-policy", fields)
}
*p = LifetimePolicy(typ)
return nil
}
// SLICES
// ArrayUByte allows encoding []uint8/[]byte as an array
// rather than binary data.
type ArrayUByte []uint8
func (a ArrayUByte) Marshal(wr *buffer.Buffer) error {
const typeSize = 1
writeArrayHeader(wr, len(a), typeSize, TypeCodeUbyte)
wr.Append(a)
return nil
}
func (a *ArrayUByte) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
type_, err := readType(r)
if err != nil {
return err
}
if type_ != TypeCodeUbyte {
return fmt.Errorf("invalid type for []uint16 %02x", type_)
}
buf, ok := r.Next(length)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
*a = append([]byte(nil), buf...)
return nil
}
type arrayInt8 []int8
func (a arrayInt8) Marshal(wr *buffer.Buffer) error {
const typeSize = 1
writeArrayHeader(wr, len(a), typeSize, TypeCodeByte)
for _, value := range a {
wr.AppendByte(uint8(value))
}
return nil
}
func (a *arrayInt8) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
type_, err := readType(r)
if err != nil {
return err
}
if type_ != TypeCodeByte {
return fmt.Errorf("invalid type for []uint16 %02x", type_)
}
buf, ok := r.Next(length)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]int8, length)
} else {
aa = aa[:length]
}
for i, value := range buf {
aa[i] = int8(value)
}
*a = aa
return nil
}
type arrayUint16 []uint16
func (a arrayUint16) Marshal(wr *buffer.Buffer) error {
const typeSize = 2
writeArrayHeader(wr, len(a), typeSize, TypeCodeUshort)
for _, element := range a {
wr.AppendUint16(element)
}
return nil
}
func (a *arrayUint16) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
type_, err := readType(r)
if err != nil {
return err
}
if type_ != TypeCodeUshort {
return fmt.Errorf("invalid type for []uint16 %02x", type_)
}
const typeSize = 2
buf, ok := r.Next(length * typeSize)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]uint16, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
aa[i] = binary.BigEndian.Uint16(buf[bufIdx:])
bufIdx += 2
}
*a = aa
return nil
}
type arrayInt16 []int16
func (a arrayInt16) Marshal(wr *buffer.Buffer) error {
const typeSize = 2
writeArrayHeader(wr, len(a), typeSize, TypeCodeShort)
for _, element := range a {
wr.AppendUint16(uint16(element))
}
return nil
}
func (a *arrayInt16) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
type_, err := readType(r)
if err != nil {
return err
}
if type_ != TypeCodeShort {
return fmt.Errorf("invalid type for []uint16 %02x", type_)
}
const typeSize = 2
buf, ok := r.Next(length * typeSize)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]int16, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
aa[i] = int16(binary.BigEndian.Uint16(buf[bufIdx : bufIdx+2]))
bufIdx += 2
}
*a = aa
return nil
}
type arrayUint32 []uint32
func (a arrayUint32) Marshal(wr *buffer.Buffer) error {
var (
typeSize = 1
TypeCode = TypeCodeSmallUint
)
for _, n := range a {
if n > math.MaxUint8 {
typeSize = 4
TypeCode = TypeCodeUint
break
}
}
writeArrayHeader(wr, len(a), typeSize, TypeCode)
if TypeCode == TypeCodeUint {
for _, element := range a {
wr.AppendUint32(element)
}
} else {
for _, element := range a {
wr.AppendByte(byte(element))
}
}
return nil
}
func (a *arrayUint32) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
aa := (*a)[:0]
type_, err := readType(r)
if err != nil {
return err
}
switch type_ {
case TypeCodeUint0:
if int64(cap(aa)) < length {
aa = make([]uint32, length)
} else {
aa = aa[:length]
for i := range aa {
aa[i] = 0
}
}
case TypeCodeSmallUint:
buf, ok := r.Next(length)
if !ok {
return errors.New("invalid length")
}
if int64(cap(aa)) < length {
aa = make([]uint32, length)
} else {
aa = aa[:length]
}
for i, n := range buf {
aa[i] = uint32(n)
}
case TypeCodeUint:
const typeSize = 4
buf, ok := r.Next(length * typeSize)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
if int64(cap(aa)) < length {
aa = make([]uint32, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
aa[i] = binary.BigEndian.Uint32(buf[bufIdx : bufIdx+4])
bufIdx += 4
}
default:
return fmt.Errorf("invalid type for []uint32 %02x", type_)
}
*a = aa
return nil
}
type arrayInt32 []int32
func (a arrayInt32) Marshal(wr *buffer.Buffer) error {
var (
typeSize = 1
TypeCode = TypeCodeSmallint
)
for _, n := range a {
if n > math.MaxInt8 {
typeSize = 4
TypeCode = TypeCodeInt
break
}
}
writeArrayHeader(wr, len(a), typeSize, TypeCode)
if TypeCode == TypeCodeInt {
for _, element := range a {
wr.AppendUint32(uint32(element))
}
} else {
for _, element := range a {
wr.AppendByte(byte(element))
}
}
return nil
}
func (a *arrayInt32) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
aa := (*a)[:0]
type_, err := readType(r)
if err != nil {
return err
}
switch type_ {
case TypeCodeSmallint:
buf, ok := r.Next(length)
if !ok {
return errors.New("invalid length")
}
if int64(cap(aa)) < length {
aa = make([]int32, length)
} else {
aa = aa[:length]
}
for i, n := range buf {
aa[i] = int32(int8(n))
}
case TypeCodeInt:
const typeSize = 4
buf, ok := r.Next(length * typeSize)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
if int64(cap(aa)) < length {
aa = make([]int32, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
aa[i] = int32(binary.BigEndian.Uint32(buf[bufIdx:]))
bufIdx += 4
}
default:
return fmt.Errorf("invalid type for []int32 %02x", type_)
}
*a = aa
return nil
}
type arrayUint64 []uint64
func (a arrayUint64) Marshal(wr *buffer.Buffer) error {
var (
typeSize = 1
TypeCode = TypeCodeSmallUlong
)
for _, n := range a {
if n > math.MaxUint8 {
typeSize = 8
TypeCode = TypeCodeUlong
break
}
}
writeArrayHeader(wr, len(a), typeSize, TypeCode)
if TypeCode == TypeCodeUlong {
for _, element := range a {
wr.AppendUint64(element)
}
} else {
for _, element := range a {
wr.AppendByte(byte(element))
}
}
return nil
}
func (a *arrayUint64) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
aa := (*a)[:0]
type_, err := readType(r)
if err != nil {
return err
}
switch type_ {
case TypeCodeUlong0:
if int64(cap(aa)) < length {
aa = make([]uint64, length)
} else {
aa = aa[:length]
for i := range aa {
aa[i] = 0
}
}
case TypeCodeSmallUlong:
buf, ok := r.Next(length)
if !ok {
return errors.New("invalid length")
}
if int64(cap(aa)) < length {
aa = make([]uint64, length)
} else {
aa = aa[:length]
}
for i, n := range buf {
aa[i] = uint64(n)
}
case TypeCodeUlong:
const typeSize = 8
buf, ok := r.Next(length * typeSize)
if !ok {
return errors.New("invalid length")
}
if int64(cap(aa)) < length {
aa = make([]uint64, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
aa[i] = binary.BigEndian.Uint64(buf[bufIdx : bufIdx+8])
bufIdx += 8
}
default:
return fmt.Errorf("invalid type for []uint64 %02x", type_)
}
*a = aa
return nil
}
type arrayInt64 []int64
func (a arrayInt64) Marshal(wr *buffer.Buffer) error {
var (
typeSize = 1
TypeCode = TypeCodeSmalllong
)
for _, n := range a {
if n > math.MaxInt8 {
typeSize = 8
TypeCode = TypeCodeLong
break
}
}
writeArrayHeader(wr, len(a), typeSize, TypeCode)
if TypeCode == TypeCodeLong {
for _, element := range a {
wr.AppendUint64(uint64(element))
}
} else {
for _, element := range a {
wr.AppendByte(byte(element))
}
}
return nil
}
func (a *arrayInt64) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
aa := (*a)[:0]
type_, err := readType(r)
if err != nil {
return err
}
switch type_ {
case TypeCodeSmalllong:
buf, ok := r.Next(length)
if !ok {
return errors.New("invalid length")
}
if int64(cap(aa)) < length {
aa = make([]int64, length)
} else {
aa = aa[:length]
}
for i, n := range buf {
aa[i] = int64(int8(n))
}
case TypeCodeLong:
const typeSize = 8
buf, ok := r.Next(length * typeSize)
if !ok {
return errors.New("invalid length")
}
if int64(cap(aa)) < length {
aa = make([]int64, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
aa[i] = int64(binary.BigEndian.Uint64(buf[bufIdx:]))
bufIdx += 8
}
default:
return fmt.Errorf("invalid type for []uint64 %02x", type_)
}
*a = aa
return nil
}
type arrayFloat []float32
func (a arrayFloat) Marshal(wr *buffer.Buffer) error {
const typeSize = 4
writeArrayHeader(wr, len(a), typeSize, TypeCodeFloat)
for _, element := range a {
wr.AppendUint32(math.Float32bits(element))
}
return nil
}
func (a *arrayFloat) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
type_, err := readType(r)
if err != nil {
return err
}
if type_ != TypeCodeFloat {
return fmt.Errorf("invalid type for []float32 %02x", type_)
}
const typeSize = 4
buf, ok := r.Next(length * typeSize)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]float32, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
bits := binary.BigEndian.Uint32(buf[bufIdx:])
aa[i] = math.Float32frombits(bits)
bufIdx += typeSize
}
*a = aa
return nil
}
type arrayDouble []float64
func (a arrayDouble) Marshal(wr *buffer.Buffer) error {
const typeSize = 8
writeArrayHeader(wr, len(a), typeSize, TypeCodeDouble)
for _, element := range a {
wr.AppendUint64(math.Float64bits(element))
}
return nil
}
func (a *arrayDouble) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
type_, err := readType(r)
if err != nil {
return err
}
if type_ != TypeCodeDouble {
return fmt.Errorf("invalid type for []float64 %02x", type_)
}
const typeSize = 8
buf, ok := r.Next(length * typeSize)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]float64, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
bits := binary.BigEndian.Uint64(buf[bufIdx:])
aa[i] = math.Float64frombits(bits)
bufIdx += typeSize
}
*a = aa
return nil
}
type arrayBool []bool
func (a arrayBool) Marshal(wr *buffer.Buffer) error {
const typeSize = 1
writeArrayHeader(wr, len(a), typeSize, TypeCodeBool)
for _, element := range a {
value := byte(0)
if element {
value = 1
}
wr.AppendByte(value)
}
return nil
}
func (a *arrayBool) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]bool, length)
} else {
aa = aa[:length]
}
type_, err := readType(r)
if err != nil {
return err
}
switch type_ {
case TypeCodeBool:
buf, ok := r.Next(length)
if !ok {
return errors.New("invalid length")
}
for i, value := range buf {
if value == 0 {
aa[i] = false
} else {
aa[i] = true
}
}
case TypeCodeBoolTrue:
for i := range aa {
aa[i] = true
}
case TypeCodeBoolFalse:
for i := range aa {
aa[i] = false
}
default:
return fmt.Errorf("invalid type for []bool %02x", type_)
}
*a = aa
return nil
}
type arrayString []string
func (a arrayString) Marshal(wr *buffer.Buffer) error {
var (
elementType = TypeCodeStr8
elementsSizeTotal int
)
for _, element := range a {
if !utf8.ValidString(element) {
return errors.New("not a valid UTF-8 string")
}
elementsSizeTotal += len(element)
if len(element) > math.MaxUint8 {
elementType = TypeCodeStr32
}
}
writeVariableArrayHeader(wr, len(a), elementsSizeTotal, elementType)
if elementType == TypeCodeStr32 {
for _, element := range a {
wr.AppendUint32(uint32(len(element)))
wr.AppendString(element)
}
} else {
for _, element := range a {
wr.AppendByte(byte(len(element)))
wr.AppendString(element)
}
}
return nil
}
func (a *arrayString) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
const typeSize = 2 // assume all strings are at least 2 bytes
if length*typeSize > int64(r.Len()) {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]string, length)
} else {
aa = aa[:length]
}
type_, err := readType(r)
if err != nil {
return err
}
switch type_ {
case TypeCodeStr8:
for i := range aa {
size, err := r.ReadByte()
if err != nil {
return err
}
buf, ok := r.Next(int64(size))
if !ok {
return errors.New("invalid length")
}
aa[i] = string(buf)
}
case TypeCodeStr32:
for i := range aa {
buf, ok := r.Next(4)
if !ok {
return errors.New("invalid length")
}
size := int64(binary.BigEndian.Uint32(buf))
buf, ok = r.Next(size)
if !ok {
return errors.New("invalid length")
}
aa[i] = string(buf)
}
default:
return fmt.Errorf("invalid type for []string %02x", type_)
}
*a = aa
return nil
}
type arraySymbol []Symbol
func (a arraySymbol) Marshal(wr *buffer.Buffer) error {
var (
elementType = TypeCodeSym8
elementsSizeTotal int
)
for _, element := range a {
elementsSizeTotal += len(element)
if len(element) > math.MaxUint8 {
elementType = TypeCodeSym32
}
}
writeVariableArrayHeader(wr, len(a), elementsSizeTotal, elementType)
if elementType == TypeCodeSym32 {
for _, element := range a {
wr.AppendUint32(uint32(len(element)))
wr.AppendString(string(element))
}
} else {
for _, element := range a {
wr.AppendByte(byte(len(element)))
wr.AppendString(string(element))
}
}
return nil
}
func (a *arraySymbol) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
const typeSize = 2 // assume all symbols are at least 2 bytes
if length*typeSize > int64(r.Len()) {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]Symbol, length)
} else {
aa = aa[:length]
}
type_, err := readType(r)
if err != nil {
return err
}
switch type_ {
case TypeCodeSym8:
for i := range aa {
size, err := r.ReadByte()
if err != nil {
return err
}
buf, ok := r.Next(int64(size))
if !ok {
return errors.New("invalid length")
}
aa[i] = Symbol(buf)
}
case TypeCodeSym32:
for i := range aa {
buf, ok := r.Next(4)
if !ok {
return errors.New("invalid length")
}
size := int64(binary.BigEndian.Uint32(buf))
buf, ok = r.Next(size)
if !ok {
return errors.New("invalid length")
}
aa[i] = Symbol(buf)
}
default:
return fmt.Errorf("invalid type for []Symbol %02x", type_)
}
*a = aa
return nil
}
type arrayBinary [][]byte
func (a arrayBinary) Marshal(wr *buffer.Buffer) error {
var (
elementType = TypeCodeVbin8
elementsSizeTotal int
)
for _, element := range a {
elementsSizeTotal += len(element)
if len(element) > math.MaxUint8 {
elementType = TypeCodeVbin32
}
}
writeVariableArrayHeader(wr, len(a), elementsSizeTotal, elementType)
if elementType == TypeCodeVbin32 {
for _, element := range a {
wr.AppendUint32(uint32(len(element)))
wr.Append(element)
}
} else {
for _, element := range a {
wr.AppendByte(byte(len(element)))
wr.Append(element)
}
}
return nil
}
func (a *arrayBinary) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
const typeSize = 2 // assume all binary is at least 2 bytes
if length*typeSize > int64(r.Len()) {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([][]byte, length)
} else {
aa = aa[:length]
}
type_, err := readType(r)
if err != nil {
return err
}
switch type_ {
case TypeCodeVbin8:
for i := range aa {
size, err := r.ReadByte()
if err != nil {
return err
}
buf, ok := r.Next(int64(size))
if !ok {
return fmt.Errorf("invalid length %d", length)
}
aa[i] = append([]byte(nil), buf...)
}
case TypeCodeVbin32:
for i := range aa {
buf, ok := r.Next(4)
if !ok {
return errors.New("invalid length")
}
size := binary.BigEndian.Uint32(buf)
buf, ok = r.Next(int64(size))
if !ok {
return errors.New("invalid length")
}
aa[i] = append([]byte(nil), buf...)
}
default:
return fmt.Errorf("invalid type for [][]byte %02x", type_)
}
*a = aa
return nil
}
type arrayTimestamp []time.Time
func (a arrayTimestamp) Marshal(wr *buffer.Buffer) error {
const typeSize = 8
writeArrayHeader(wr, len(a), typeSize, TypeCodeTimestamp)
for _, element := range a {
ms := element.UnixNano() / int64(time.Millisecond)
wr.AppendUint64(uint64(ms))
}
return nil
}
func (a *arrayTimestamp) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
type_, err := readType(r)
if err != nil {
return err
}
if type_ != TypeCodeTimestamp {
return fmt.Errorf("invalid type for []time.Time %02x", type_)
}
const typeSize = 8
buf, ok := r.Next(length * typeSize)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]time.Time, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
ms := int64(binary.BigEndian.Uint64(buf[bufIdx:]))
bufIdx += typeSize
aa[i] = time.Unix(ms/1000, (ms%1000)*1000000).UTC()
}
*a = aa
return nil
}
type arrayUUID []UUID
func (a arrayUUID) Marshal(wr *buffer.Buffer) error {
const typeSize = 16
writeArrayHeader(wr, len(a), typeSize, TypeCodeUUID)
for _, element := range a {
wr.Append(element[:])
}
return nil
}
func (a *arrayUUID) Unmarshal(r *buffer.Buffer) error {
length, err := readArrayHeader(r)
if err != nil {
return err
}
type_, err := readType(r)
if err != nil {
return err
}
if type_ != TypeCodeUUID {
return fmt.Errorf("invalid type for []UUID %#02x", type_)
}
const typeSize = 16
buf, ok := r.Next(length * typeSize)
if !ok {
return fmt.Errorf("invalid length %d", length)
}
aa := (*a)[:0]
if int64(cap(aa)) < length {
aa = make([]UUID, length)
} else {
aa = aa[:length]
}
var bufIdx int
for i := range aa {
copy(aa[i][:], buf[bufIdx:bufIdx+16])
bufIdx += 16
}
*a = aa
return nil
}
// LIST
type list []any
func (l list) Marshal(wr *buffer.Buffer) error {
length := len(l)
// type
if length == 0 {
wr.AppendByte(byte(TypeCodeList0))
return nil
}
wr.AppendByte(byte(TypeCodeList32))
// size
sizeIdx := wr.Len()
wr.Append([]byte{0, 0, 0, 0})
// length
wr.AppendUint32(uint32(length))
for _, element := range l {
err := Marshal(wr, element)
if err != nil {
return err
}
}
// overwrite size
binary.BigEndian.PutUint32(wr.Bytes()[sizeIdx:], uint32(wr.Len()-(sizeIdx+4)))
return nil
}
func (l *list) Unmarshal(r *buffer.Buffer) error {
length, err := readListHeader(r)
if err != nil {
return err
}
// assume that all types are at least 1 byte
if length > int64(r.Len()) {
return fmt.Errorf("invalid length %d", length)
}
ll := *l
if int64(cap(ll)) < length {
ll = make([]any, length)
} else {
ll = ll[:length]
}
for i := range ll {
ll[i], err = ReadAny(r)
if err != nil {
return err
}
}
*l = ll
return nil
}
// multiSymbol can decode a single symbol or an array.
type MultiSymbol []Symbol
func (ms MultiSymbol) Marshal(wr *buffer.Buffer) error {
return Marshal(wr, []Symbol(ms))
}
func (ms *MultiSymbol) Unmarshal(r *buffer.Buffer) error {
type_, err := peekType(r)
if err != nil {
return err
}
if type_ == TypeCodeSym8 || type_ == TypeCodeSym32 {
s, err := ReadString(r)
if err != nil {
return err
}
*ms = []Symbol{Symbol(s)}
return nil
}
return Unmarshal(r, (*[]Symbol)(ms))
}