package codec
import (
- "bytes"
+ "encoding/binary"
"errors"
"fmt"
- "reflect"
"git.fd.io/govpp.git/api"
- "github.com/lunixbochs/struc"
)
-// MsgCodec provides encoding and decoding functionality of `api.Message` structs into/from
-// binary format as accepted by VPP.
-type MsgCodec struct{}
-
-// VppRequestHeader struct contains header fields implemented by all VPP requests.
-type VppRequestHeader struct {
- VlMsgID uint16
- ClientIndex uint32
- Context uint32
-}
+var DefaultCodec = new(MsgCodec)
-// VppReplyHeader struct contains header fields implemented by all VPP replies.
-type VppReplyHeader struct {
- VlMsgID uint16
- Context uint32
+func EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) {
+ return DefaultCodec.EncodeMsg(msg, msgID)
}
-
-// VppEventHeader struct contains header fields implemented by all VPP events.
-type VppEventHeader struct {
- VlMsgID uint16
- Context uint32
+func DecodeMsg(data []byte, msg api.Message) (err error) {
+ return DefaultCodec.DecodeMsg(data, msg)
}
-
-// VppOtherHeader struct contains header fields implemented by other VPP messages (not requests nor replies).
-type VppOtherHeader struct {
- VlMsgID uint16
+func DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) {
+ return DefaultCodec.DecodeMsgContext(data, msgType)
}
-const (
- vppRequestHeaderSize = 10 // size of a VPP request header
- vppReplyHeaderSize = 6 // size of a VPP reply header
- vppEventHeaderSize = 6 // size of a VPP event header
- vppOtherHeaderSize = 2 // size of the header of other VPP messages
-)
+// MsgCodec provides encoding and decoding functionality of `api.Message` structs into/from
+// binary format as accepted by VPP.
+type MsgCodec struct{}
-// EncodeMsg encodes provided `Message` structure into its binary-encoded data representation.
-func (*MsgCodec) EncodeMsg(msg api.Message, msgID uint16) ([]byte, error) {
+func (*MsgCodec) EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) {
if msg == nil {
return nil, errors.New("nil message passed in")
}
- buf := new(bytes.Buffer)
-
- // encode message header
- var header interface{}
- if msg.GetMessageType() == api.RequestMessage {
- header = &VppRequestHeader{VlMsgID: msgID}
- } else if msg.GetMessageType() == api.ReplyMessage {
- header = &VppReplyHeader{VlMsgID: msgID}
- } else if msg.GetMessageType() == api.EventMessage {
- header = &VppEventHeader{VlMsgID: msgID}
- } else {
- header = &VppOtherHeader{VlMsgID: msgID}
- }
- err := struc.Pack(buf, header)
- if err != nil {
- return nil, fmt.Errorf("unable to encode message: header: %v, error %v", header, err)
+ // try to recover panic which might possibly occur
+ defer func() {
+ if r := recover(); r != nil {
+ var ok bool
+ if err, ok = r.(error); !ok {
+ err = fmt.Errorf("%v", r)
+ }
+ err = fmt.Errorf("panic occurred during encoding message %s: %v", msg.GetMessageName(), err)
+ }
+ }()
+
+ marshaller, ok := msg.(Marshaler)
+ if !ok {
+ marshaller = Wrapper{msg}
}
- // encode message content
- if reflect.Indirect(reflect.ValueOf(msg)).NumField() > 0 {
- err := struc.Pack(buf, msg)
- if err != nil {
- return nil, fmt.Errorf("unable to encode message: header %v, error %v", header, err)
- }
+ size := marshaller.Size()
+ offset := getOffset(msg)
+
+ // encode msg ID
+ b := make([]byte, size+offset)
+ b[0] = byte(msgID >> 8)
+ b[1] = byte(msgID)
+
+ data, err = marshaller.Marshal(b[offset:])
+ if err != nil {
+ return nil, err
}
- return buf.Bytes(), nil
+ return b[0:len(b):len(b)], nil
}
-// DecodeMsg decodes binary-encoded data of a message into provided `Message` structure.
-func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) error {
+func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) (err error) {
if msg == nil {
return errors.New("nil message passed in")
}
- buf := bytes.NewReader(data)
-
- // check which header is expected
- var header interface{}
- if msg.GetMessageType() == api.RequestMessage {
- header = &VppRequestHeader{}
- } else if msg.GetMessageType() == api.ReplyMessage {
- header = &VppReplyHeader{}
- } else if msg.GetMessageType() == api.EventMessage {
- header = &VppEventHeader{}
- } else {
- header = &VppOtherHeader{}
- }
+ // try to recover panic which might possibly occur
+ defer func() {
+ if r := recover(); r != nil {
+ var ok bool
+ if err, ok = r.(error); !ok {
+ err = fmt.Errorf("%v", r)
+ }
+ err = fmt.Errorf("panic occurred during decoding message %s: %v", msg.GetMessageName(), err)
+ }
+ }()
- // decode message header
- err := struc.Unpack(buf, header)
- if err != nil {
- return fmt.Errorf("unable to decode message: data %v, error %v", data, err)
+ marshaller, ok := msg.(Unmarshaler)
+ if !ok {
+ marshaller = Wrapper{msg}
}
- // get rid of the message header
- if msg.GetMessageType() == api.RequestMessage {
- buf = bytes.NewReader(data[vppRequestHeaderSize:])
- } else if msg.GetMessageType() == api.ReplyMessage {
- buf = bytes.NewReader(data[vppReplyHeaderSize:])
- } else if msg.GetMessageType() == api.EventMessage {
- buf = bytes.NewReader(data[vppEventHeaderSize:])
- } else {
- buf = bytes.NewReader(data[vppOtherHeaderSize:])
- }
+ offset := getOffset(msg)
- // decode message content
- err = struc.Unpack(buf, msg)
+ err = marshaller.Unmarshal(data[offset:len(data)])
if err != nil {
- return fmt.Errorf("unable to decode message: data %v, error %v", data, err)
+ return err
}
return nil
}
+
+func (*MsgCodec) DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) {
+ switch msgType {
+ case api.RequestMessage:
+ return binary.BigEndian.Uint32(data[6:10]), nil
+ case api.ReplyMessage:
+ return binary.BigEndian.Uint32(data[2:6]), nil
+ }
+
+ return 0, nil
+}
+
+func getOffset(msg api.Message) (offset int) {
+ switch msg.GetMessageType() {
+ case api.RequestMessage:
+ return 10
+ case api.ReplyMessage:
+ return 6
+ case api.EventMessage:
+ return 6
+ }
+ return 2
+}