gecko/network/codec.go

73 lines
1.6 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package network
import (
"errors"
"fmt"
"math"
"github.com/ava-labs/gecko/utils/wrappers"
)
var (
errBadLength = errors.New("stream has unexpected length")
errMissingField = errors.New("message missing field")
errBadOp = errors.New("input field has invalid operation")
)
// Codec defines the serialization and deserialization of network messages
type Codec struct{}
// Pack attempts to pack a map of fields into a message.
// The first byte of the message is the opcode of the message.
func (Codec) Pack(op Op, fields map[Field]interface{}) (Msg, error) {
message, ok := Messages[op]
if !ok {
return nil, errBadOp
}
p := wrappers.Packer{MaxSize: math.MaxInt32}
p.PackByte(byte(op))
for _, field := range message {
data, ok := fields[field]
if !ok {
return nil, errMissingField
}
field.Packer()(&p, data)
}
return &msg{
op: op,
fields: fields,
bytes: p.Bytes,
}, p.Err
}
// Parse attempts to convert bytes into a message.
// The first byte of the message is the opcode of the message.
func (Codec) Parse(b []byte) (Msg, error) {
p := wrappers.Packer{Bytes: b}
op := Op(p.UnpackByte())
message, ok := Messages[op]
if !ok {
return nil, errBadOp
}
fields := make(map[Field]interface{}, len(message))
for _, field := range message {
fields[field] = field.Unpacker()(&p)
}
if p.Offset != len(b) {
p.Add(fmt.Errorf("expected length %d got %d", len(b), p.Offset))
}
return &msg{
op: op,
fields: fields,
bytes: b,
}, p.Err
}