mirror of https://github.com/poanetwork/gecko.git
82 lines
1.7 KiB
Go
82 lines
1.7 KiB
Go
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||
|
// See the file LICENSE for licensing terms.
|
||
|
|
||
|
package networking
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"math"
|
||
|
|
||
|
"github.com/ava-labs/salticidae-go"
|
||
|
|
||
|
"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.
|
||
|
func (Codec) Pack(op salticidae.Opcode, fields map[Field]interface{}) (Msg, error) {
|
||
|
message, ok := Messages[op]
|
||
|
if !ok {
|
||
|
return nil, errBadOp
|
||
|
}
|
||
|
|
||
|
p := wrappers.Packer{MaxSize: math.MaxInt32}
|
||
|
for _, field := range message {
|
||
|
data, ok := fields[field]
|
||
|
if !ok {
|
||
|
return nil, errMissingField
|
||
|
}
|
||
|
field.Packer()(&p, data)
|
||
|
}
|
||
|
|
||
|
if p.Errored() { // Prevent the datastream from leaking
|
||
|
return nil, p.Err
|
||
|
}
|
||
|
|
||
|
return &msg{
|
||
|
op: op,
|
||
|
ds: salticidae.NewDataStreamFromBytes(p.Bytes, false),
|
||
|
fields: fields,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// Parse attempts to convert a byte stream into a message.
|
||
|
func (Codec) Parse(op salticidae.Opcode, ds salticidae.DataStream) (Msg, error) {
|
||
|
message, ok := Messages[op]
|
||
|
if !ok {
|
||
|
return nil, errBadOp
|
||
|
}
|
||
|
|
||
|
// TODO: make this work without copy
|
||
|
size := ds.Size()
|
||
|
p := wrappers.Packer{Bytes: make([]byte, size)}
|
||
|
|
||
|
byteHandle := ds.GetDataInPlace(size)
|
||
|
defer byteHandle.Release()
|
||
|
|
||
|
copy(p.Bytes, byteHandle.Get())
|
||
|
|
||
|
fields := make(map[Field]interface{}, len(message))
|
||
|
for _, field := range message {
|
||
|
fields[field] = field.Unpacker()(&p)
|
||
|
}
|
||
|
|
||
|
if p.Offset != size {
|
||
|
return nil, errBadLength
|
||
|
}
|
||
|
|
||
|
return &msg{
|
||
|
op: op,
|
||
|
ds: ds,
|
||
|
fields: fields,
|
||
|
}, p.Err
|
||
|
}
|