gecko/utils/wrappers/packing.go

476 lines
11 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package wrappers
import (
"encoding/binary"
"errors"
"math"
"github.com/ava-labs/gecko/utils"
"github.com/ava-labs/gecko/utils/hashing"
)
const (
// MaxStringLen ...
MaxStringLen = math.MaxUint16
// ByteLen is the number of bytes per byte...
ByteLen = 1
// ShortLen is the number of bytes per short
ShortLen = 2
// IntLen is the number of bytes per int
IntLen = 4
// LongLen is the number of bytes per long
LongLen = 8
// BoolLen is the number of bytes per bool
BoolLen = 1
)
var (
errBadLength = errors.New("packer has insufficient length for input")
errNegativeOffset = errors.New("negative offset")
errInvalidInput = errors.New("input does not match expected format")
errBadType = errors.New("wrong type passed")
errBadBool = errors.New("unexpected value when unpacking bool")
)
// Packer packs and unpacks a byte array from/to standard values
type Packer struct {
Errs
// The largest allowed size of expanding the byte array
MaxSize int
// The current byte array
Bytes []byte
// The offset that is being written to in the byte array
Offset int
}
// CheckSpace requires that there is at least [bytes] of write space left in the
// byte array. If this is not true, an error is added to the packer
func (p *Packer) CheckSpace(bytes int) {
switch {
case p.Offset < 0:
p.Add(errNegativeOffset)
case bytes < 0:
p.Add(errInvalidInput)
case len(p.Bytes)-p.Offset < bytes:
p.Add(errBadLength)
}
}
// Expand ensures that there is [bytes] bytes left of space in the byte array.
// If this is not allowed due to the maximum size, an error is added to the
// packer
func (p *Packer) Expand(bytes int) {
p.CheckSpace(0)
if p.Errored() {
return
}
neededSize := bytes + p.Offset
if neededSize <= len(p.Bytes) {
return
}
if neededSize > p.MaxSize {
p.Add(errBadLength)
} else if neededSize > cap(p.Bytes) {
p.Bytes = append(p.Bytes[:cap(p.Bytes)], make([]byte, neededSize-cap(p.Bytes))...)
} else {
p.Bytes = p.Bytes[:neededSize]
}
}
// PackByte append a byte to the byte array
func (p *Packer) PackByte(val byte) {
p.Expand(ByteLen)
if p.Errored() {
return
}
p.Bytes[p.Offset] = val
p.Offset++
}
// UnpackByte unpack a byte from the byte array
func (p *Packer) UnpackByte() byte {
p.CheckSpace(ByteLen)
if p.Errored() {
return 0
}
val := p.Bytes[p.Offset]
p.Offset++
return val
}
// PackShort append a short to the byte array
func (p *Packer) PackShort(val uint16) {
p.Expand(ShortLen)
if p.Errored() {
return
}
binary.BigEndian.PutUint16(p.Bytes[p.Offset:], val)
p.Offset += ShortLen
}
// UnpackShort unpack a short from the byte array
func (p *Packer) UnpackShort() uint16 {
p.CheckSpace(ShortLen)
if p.Errored() {
return 0
}
val := binary.BigEndian.Uint16(p.Bytes[p.Offset:])
p.Offset += ShortLen
return val
}
// PackInt append an int to the byte array
func (p *Packer) PackInt(val uint32) {
p.Expand(IntLen)
if p.Errored() {
return
}
binary.BigEndian.PutUint32(p.Bytes[p.Offset:], val)
p.Offset += IntLen
}
// UnpackInt unpack an int from the byte array
func (p *Packer) UnpackInt() uint32 {
p.CheckSpace(IntLen)
if p.Errored() {
return 0
}
val := binary.BigEndian.Uint32(p.Bytes[p.Offset:])
p.Offset += IntLen
return val
}
// PackLong append a long to the byte array
func (p *Packer) PackLong(val uint64) {
p.Expand(LongLen)
if p.Errored() {
return
}
binary.BigEndian.PutUint64(p.Bytes[p.Offset:], val)
p.Offset += LongLen
}
// UnpackLong unpack a long from the byte array
func (p *Packer) UnpackLong() uint64 {
p.CheckSpace(LongLen)
if p.Errored() {
return 0
}
val := binary.BigEndian.Uint64(p.Bytes[p.Offset:])
p.Offset += LongLen
return val
}
// PackBool packs a bool into the byte array
func (p *Packer) PackBool(b bool) {
if b {
p.PackByte(1)
} else {
p.PackByte(0)
}
}
// UnpackBool unpacks a bool from the byte array
func (p *Packer) UnpackBool() bool {
b := p.UnpackByte()
switch b {
case 0:
return false
case 1:
return true
default:
p.Add(errBadBool)
return false
}
}
// PackFixedBytes append a byte slice, with no length descriptor to the byte
// array
func (p *Packer) PackFixedBytes(bytes []byte) {
p.Expand(len(bytes))
if p.Errored() {
return
}
copy(p.Bytes[p.Offset:], bytes)
p.Offset += len(bytes)
}
// UnpackFixedBytes unpack a byte slice, with no length descriptor from the byte
// array
func (p *Packer) UnpackFixedBytes(size int) []byte {
p.CheckSpace(size)
if p.Errored() {
return nil
}
bytes := p.Bytes[p.Offset : p.Offset+size]
p.Offset += size
return bytes
}
// PackBytes append a byte slice to the byte array
func (p *Packer) PackBytes(bytes []byte) {
p.PackInt(uint32(len(bytes)))
p.PackFixedBytes(bytes)
}
// UnpackBytes unpack a byte slice from the byte array
func (p *Packer) UnpackBytes() []byte {
size := p.UnpackInt()
return p.UnpackFixedBytes(int(size))
}
// PackFixedByteSlices append a byte slice slice to the byte array
func (p *Packer) PackFixedByteSlices(byteSlices [][]byte) {
p.PackInt(uint32(len(byteSlices)))
for _, bytes := range byteSlices {
p.PackFixedBytes(bytes)
}
}
// UnpackFixedByteSlices returns a byte slice slice from the byte array.
// Each byte slice has the specified size. The number of byte slices is
// read from the byte array.
func (p *Packer) UnpackFixedByteSlices(size int) [][]byte {
sliceSize := p.UnpackInt()
bytes := [][]byte(nil)
for i := uint32(0); i < sliceSize && !p.Errored(); i++ {
bytes = append(bytes, p.UnpackFixedBytes(size))
}
return bytes
}
// PackStr append a string to the byte array
func (p *Packer) PackStr(str string) {
strSize := len(str)
if strSize > MaxStringLen {
p.Add(errInvalidInput)
}
p.PackShort(uint16(strSize))
p.PackFixedBytes([]byte(str))
}
// UnpackStr unpacks a string from the byte array
func (p *Packer) UnpackStr() string {
strSize := p.UnpackShort()
return string(p.UnpackFixedBytes(int(strSize)))
}
// PackIP unpacks an ip port pair from the byte array
func (p *Packer) PackIP(ip utils.IPDesc) {
p.PackFixedBytes(ip.IP.To16())
p.PackShort(ip.Port)
}
// UnpackIP unpacks an ip port pair from the byte array
func (p *Packer) UnpackIP() utils.IPDesc {
ip := p.UnpackFixedBytes(16)
port := p.UnpackShort()
return utils.IPDesc{
IP: ip,
Port: port,
}
}
// PackIPs unpacks an ip port pair slice from the byte array
func (p *Packer) PackIPs(ips []utils.IPDesc) {
p.PackInt(uint32(len(ips)))
for i := 0; i < len(ips) && !p.Errored(); i++ {
p.PackIP(ips[i])
}
}
// UnpackIPs unpacks an ip port pair slice from the byte array
func (p *Packer) UnpackIPs() []utils.IPDesc {
sliceSize := p.UnpackInt()
ips := []utils.IPDesc(nil)
for i := uint32(0); i < sliceSize && !p.Errored(); i++ {
ips = append(ips, p.UnpackIP())
}
return ips
}
// TryPackByte attempts to pack the value as a byte
func TryPackByte(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.(uint8); ok {
packer.PackByte(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackByte attempts to unpack a value as a byte
func TryUnpackByte(packer *Packer) interface{} {
return packer.UnpackByte()
}
// TryPackShort attempts to pack the value as a short
func TryPackShort(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.(uint16); ok {
packer.PackShort(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackShort attempts to unpack a value as a short
func TryUnpackShort(packer *Packer) interface{} {
return packer.UnpackShort()
}
// TryPackInt attempts to pack the value as an int
func TryPackInt(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.(uint32); ok {
packer.PackInt(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackInt attempts to unpack a value as an int
func TryUnpackInt(packer *Packer) interface{} {
return packer.UnpackInt()
}
// TryPackLong attempts to pack the value as a long
func TryPackLong(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.(uint64); ok {
packer.PackLong(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackLong attempts to unpack a value as a long
func TryUnpackLong(packer *Packer) interface{} {
return packer.UnpackLong()
}
// TryPackHash attempts to pack the value as a 32-byte sequence
func TryPackHash(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.([]byte); ok {
packer.PackFixedBytes(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackHash attempts to unpack the value as a 32-byte sequence
func TryUnpackHash(packer *Packer) interface{} {
return packer.UnpackFixedBytes(hashing.HashLen)
}
// TryPackHashes attempts to pack the value as a list of 32-byte sequences
func TryPackHashes(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.([][]byte); ok {
packer.PackFixedByteSlices(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackHashes attempts to unpack the value as a list of 32-byte sequences
func TryUnpackHashes(packer *Packer) interface{} {
return packer.UnpackFixedByteSlices(hashing.HashLen)
}
// TryPackAddr attempts to pack the value as a 20-byte sequence
func TryPackAddr(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.([]byte); ok {
packer.PackFixedBytes(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackAddr attempts to unpack the value as a 20-byte sequence
func TryUnpackAddr(packer *Packer) interface{} {
return packer.UnpackFixedBytes(hashing.AddrLen)
}
// TryPackAddrList attempts to pack the value as a list of 20-byte sequences
func TryPackAddrList(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.([][]byte); ok {
packer.PackFixedByteSlices(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackAddrList attempts to unpack the value as a list of 20-byte sequences
func TryUnpackAddrList(packer *Packer) interface{} {
return packer.UnpackFixedByteSlices(hashing.AddrLen)
}
// TryPackBytes attempts to pack the value as a list of bytes
func TryPackBytes(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.([]byte); ok {
packer.PackBytes(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackBytes attempts to unpack the value as a list of bytes
func TryUnpackBytes(packer *Packer) interface{} {
return packer.UnpackBytes()
}
// TryPackStr attempts to pack the value as a string
func TryPackStr(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.(string); ok {
packer.PackStr(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackStr attempts to unpack the value as a string
func TryUnpackStr(packer *Packer) interface{} {
return packer.UnpackStr()
}
// TryPackIP attempts to pack the value as an ip port pair
func TryPackIP(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.(utils.IPDesc); ok {
packer.PackIP(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackIP attempts to unpack the value as an ip port pair
func TryUnpackIP(packer *Packer) interface{} {
return packer.UnpackIP()
}
// TryPackIPList attempts to pack the value as an ip port pair list
func TryPackIPList(packer *Packer, valIntf interface{}) {
if val, ok := valIntf.([]utils.IPDesc); ok {
packer.PackIPs(val)
} else {
packer.Add(errBadType)
}
}
// TryUnpackIPList attempts to unpack the value as an ip port pair list
func TryUnpackIPList(packer *Packer) interface{} {
return packer.UnpackIPs()
}