make api.Channel as interface
[govpp.git] / codec / msg_codec.go
1 // Copyright (c) 2017 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package codec
16
17 import (
18         "bytes"
19         "errors"
20         "fmt"
21         "reflect"
22
23         "git.fd.io/govpp.git/api"
24         "github.com/lunixbochs/struc"
25 )
26
27 // MsgCodec provides encoding and decoding functionality of `api.Message` structs into/from
28 // binary format as accepted by VPP.
29 type MsgCodec struct{}
30
31 // VppRequestHeader struct contains header fields implemented by all VPP requests.
32 type VppRequestHeader struct {
33         VlMsgID     uint16
34         ClientIndex uint32
35         Context     uint32
36 }
37
38 // VppReplyHeader struct contains header fields implemented by all VPP replies.
39 type VppReplyHeader struct {
40         VlMsgID uint16
41         Context uint32
42 }
43
44 // VppEventHeader struct contains header fields implemented by all VPP events.
45 type VppEventHeader struct {
46         VlMsgID uint16
47         Context uint32
48 }
49
50 // VppOtherHeader struct contains header fields implemented by other VPP messages (not requests nor replies).
51 type VppOtherHeader struct {
52         VlMsgID uint16
53 }
54
55 const (
56         vppRequestHeaderSize = 10 // size of a VPP request header
57         vppReplyHeaderSize   = 6  // size of a VPP reply header
58         vppEventHeaderSize   = 6  // size of a VPP event header
59         vppOtherHeaderSize   = 2  // size of the header of other VPP messages
60 )
61
62 // EncodeMsg encodes provided `Message` structure into its binary-encoded data representation.
63 func (*MsgCodec) EncodeMsg(msg api.Message, msgID uint16) ([]byte, error) {
64         if msg == nil {
65                 return nil, errors.New("nil message passed in")
66         }
67
68         buf := new(bytes.Buffer)
69
70         // encode message header
71         var header interface{}
72         if msg.GetMessageType() == api.RequestMessage {
73                 header = &VppRequestHeader{VlMsgID: msgID}
74         } else if msg.GetMessageType() == api.ReplyMessage {
75                 header = &VppReplyHeader{VlMsgID: msgID}
76         } else if msg.GetMessageType() == api.EventMessage {
77                 header = &VppEventHeader{VlMsgID: msgID}
78         } else {
79                 header = &VppOtherHeader{VlMsgID: msgID}
80         }
81         err := struc.Pack(buf, header)
82         if err != nil {
83                 return nil, fmt.Errorf("unable to encode message: header: %v, error %v", header, err)
84         }
85
86         // encode message content
87         if reflect.Indirect(reflect.ValueOf(msg)).NumField() > 0 {
88                 err := struc.Pack(buf, msg)
89                 if err != nil {
90                         return nil, fmt.Errorf("unable to encode message: header %v, error %v", header, err)
91                 }
92         }
93
94         return buf.Bytes(), nil
95 }
96
97 // DecodeMsg decodes binary-encoded data of a message into provided `Message` structure.
98 func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) error {
99         if msg == nil {
100                 return errors.New("nil message passed in")
101         }
102
103         buf := bytes.NewReader(data)
104
105         // check which header is expected
106         var header interface{}
107         if msg.GetMessageType() == api.RequestMessage {
108                 header = &VppRequestHeader{}
109         } else if msg.GetMessageType() == api.ReplyMessage {
110                 header = &VppReplyHeader{}
111         } else if msg.GetMessageType() == api.EventMessage {
112                 header = &VppEventHeader{}
113         } else {
114                 header = &VppOtherHeader{}
115         }
116
117         // decode message header
118         err := struc.Unpack(buf, header)
119         if err != nil {
120                 return fmt.Errorf("unable to decode message: data %v, error %v", data, err)
121         }
122
123         // get rid of the message header
124         if msg.GetMessageType() == api.RequestMessage {
125                 buf = bytes.NewReader(data[vppRequestHeaderSize:])
126         } else if msg.GetMessageType() == api.ReplyMessage {
127                 buf = bytes.NewReader(data[vppReplyHeaderSize:])
128         } else if msg.GetMessageType() == api.EventMessage {
129                 buf = bytes.NewReader(data[vppEventHeaderSize:])
130         } else {
131                 buf = bytes.NewReader(data[vppOtherHeaderSize:])
132         }
133
134         // decode message content
135         err = struc.Unpack(buf, msg)
136         if err != nil {
137                 return fmt.Errorf("unable to decode message: data %v, error %v", data, err)
138         }
139
140         return nil
141 }