Skip to content
This repository has been archived by the owner on Dec 14, 2020. It is now read-only.

Commit

Permalink
Minimized encoded size of arrays of variable length types.
Browse files Browse the repository at this point in the history
  • Loading branch information
vcabbage committed Feb 12, 2018
1 parent 749c2de commit 7036f82
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 72 deletions.
33 changes: 31 additions & 2 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,12 @@ func writeMap(wr *buffer, m interface{}) error {
return nil
}

// type length sizes
const (
array8TLSize = 2
array32TLSize = 5
)

func writeArrayHeader(wr *buffer, length, typeSize int, type_ amqpType) {
size := length * typeSize

Expand All @@ -546,13 +552,36 @@ func writeArrayHeader(wr *buffer, length, typeSize int, type_ amqpType) {
byte(typeCodeArray8), // type
byte(size + array8TLSize), // size
byte(length), // length
byte(type_), // element type
})
} else {
wr.writeByte(byte(typeCodeArray32)) //type
wr.writeUint32(uint32(size + array32TLSize)) // size
wr.writeUint32(uint32(length)) // length
wr.writeByte(byte(type_)) // element type
}
}

func writeVariableArrayHeader(wr *buffer, length, elementsSizeTotal int, type_ amqpType) {
// 0xA_ == 1, 0xB_ == 4
// http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html#doc-idp82960
elementTypeSize := 1
if type_&0xf0 == 0xb0 {
elementTypeSize = 4
}

// element type
wr.writeByte(byte(type_))
size := elementsSizeTotal + (length * elementTypeSize) // size excluding array length
if size+array8TLSize <= math.MaxUint8 {
wr.write([]byte{
byte(typeCodeArray8), // type
byte(size + array8TLSize), // size
byte(length), // length
byte(type_), // element type
})
} else {
wr.writeByte(byte(typeCodeArray32)) // type
wr.writeUint32(uint32(size + array32TLSize)) // size
wr.writeUint32(uint32(length)) // length
wr.writeByte(byte(type_)) // element type
}
}
133 changes: 63 additions & 70 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2710,12 +2710,6 @@ func (t describedType) String() string {

// SLICES

const (
// type length sizes
array8TLSize = 2
array32TLSize = 5
)

// ArrayUByte allows encoding []uint8/[]byte as an array
// rather than binary data.
type ArrayUByte []uint8
Expand Down Expand Up @@ -3429,34 +3423,35 @@ func (a *arrayBool) unmarshal(r *buffer) error {
type arrayString []string

func (a arrayString) marshal(wr *buffer) error {
length := len(a)

// TODO: check sizes to minimize encoding size

// type
wr.writeByte(byte(typeCodeArray32))

// size
sizeIdx := wr.len()
wr.write([]byte{0, 0, 0, 0})

// length
wr.writeUint32(uint32(length))

// element type
wr.writeByte(byte(typeCodeStr32))

var (
elementType = typeCodeStr8
elementsSizeTotal int
)
for _, element := range a {
if !utf8.ValidString(element) {
return errorNew("not a valid UTF-8 string")
}

wr.writeUint32(uint32(len(element)))
wr.writeString(element)
elementsSizeTotal += len(element)

if len(element) > math.MaxUint8 {
elementType = typeCodeStr32
}
}

// overwrite size
binary.BigEndian.PutUint32(wr.bytes()[sizeIdx:], uint32(wr.len()-(sizeIdx+4)))
writeVariableArrayHeader(wr, len(a), elementsSizeTotal, elementType)

if elementType == typeCodeStr32 {
for _, element := range a {
wr.writeUint32(uint32(len(element)))
wr.writeString(element)
}
} else {
for _, element := range a {
wr.writeByte(byte(len(element)))
wr.writeString(element)
}
}

return nil
}
Expand Down Expand Up @@ -3523,35 +3518,32 @@ func (a *arrayString) unmarshal(r *buffer) error {
type arraySymbol []symbol

func (a arraySymbol) marshal(wr *buffer) error {
length := len(a)

// TODO: check sizes to minimize encoding size

// type
wr.writeByte(byte(typeCodeArray32))

// size
sizeIdx := wr.len()
wr.write([]byte{0, 0, 0, 0})
var (
elementType = typeCodeSym8
elementsSizeTotal int
)
for _, element := range a {
elementsSizeTotal += len(element)

// length
wr.writeUint32(uint32(length))
if len(element) > math.MaxUint8 {
elementType = typeCodeSym32
}
}

// element type
wr.writeByte(byte(typeCodeSym32))
writeVariableArrayHeader(wr, len(a), elementsSizeTotal, elementType)

for _, element := range a {
if !utf8.ValidString(string(element)) {
return errorNew("not a valid UTF-8 string")
if elementType == typeCodeSym32 {
for _, element := range a {
wr.writeUint32(uint32(len(element)))
wr.writeString(string(element))
}
} else {
for _, element := range a {
wr.writeByte(byte(len(element)))
wr.writeString(string(element))
}

wr.writeUint32(uint32(len(element)))
wr.writeString(string(element))
}

// overwrite size
binary.BigEndian.PutUint32(wr.bytes()[sizeIdx:], uint32(wr.len()-(sizeIdx+4)))

return nil
}

Expand Down Expand Up @@ -3616,31 +3608,32 @@ func (a *arraySymbol) unmarshal(r *buffer) error {
type arrayBinary [][]byte

func (a arrayBinary) marshal(wr *buffer) error {
length := len(a)

// TODO: check sizes to minimize encoding size

// type
wr.writeByte(byte(typeCodeArray32))

// size
sizeIdx := wr.len()
wr.write([]byte{0, 0, 0, 0})
var (
elementType = typeCodeVbin8
elementsSizeTotal int
)
for _, element := range a {
elementsSizeTotal += len(element)

// length
wr.writeUint32(uint32(length))
if len(element) > math.MaxUint8 {
elementType = typeCodeVbin32
}
}

// element type
wr.writeByte(byte(typeCodeVbin32))
writeVariableArrayHeader(wr, len(a), elementsSizeTotal, elementType)

for _, element := range a {
wr.writeUint32(uint32(len(element)))
wr.write(element)
if elementType == typeCodeVbin32 {
for _, element := range a {
wr.writeUint32(uint32(len(element)))
wr.write(element)
}
} else {
for _, element := range a {
wr.writeByte(byte(len(element)))
wr.write(element)
}
}

// overwrite size
binary.BigEndian.PutUint32(wr.bytes()[sizeIdx:], uint32(wr.len()-(sizeIdx+4)))

return nil
}

Expand Down

0 comments on commit 7036f82

Please sign in to comment.