3f60cae34173ffe1313913f2c0e51f0d99daff47
[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         "github.com/lunixbochs/struc"
24
25         "git.fd.io/govpp.git/api"
26 )
27
28 // MsgCodec provides encoding and decoding functionality of `api.Message` structs into/from
29 // binary format as accepted by VPP.
30 type MsgCodec struct{}
31
32 // VppRequestHeader struct contains header fields implemented by all VPP requests.
33 type VppRequestHeader struct {
34         VlMsgID     uint16
35         ClientIndex uint32
36         Context     uint32
37 }
38
39 // VppReplyHeader struct contains header fields implemented by all VPP replies.
40 type VppReplyHeader struct {
41         VlMsgID uint16
42         Context uint32
43 }
44
45 // VppEventHeader struct contains header fields implemented by all VPP events.
46 type VppEventHeader struct {
47         VlMsgID     uint16
48         ClientIndex uint32
49 }
50
51 // VppOtherHeader struct contains header fields implemented by other VPP messages (not requests nor replies).
52 type VppOtherHeader struct {
53         VlMsgID uint16
54 }
55
56 // EncodeMsg encodes provided `Message` structure into its binary-encoded data representation.
57 func (*MsgCodec) EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) {
58         if msg == nil {
59                 return nil, errors.New("nil message passed in")
60         }
61
62         // try to recover panic which might possibly occur in struc.Pack call
63         defer func() {
64                 if r := recover(); r != nil {
65                         var ok bool
66                         if err, ok = r.(error); !ok {
67                                 err = fmt.Errorf("%v", r)
68                         }
69                         err = fmt.Errorf("panic occurred during encoding message %s: %v", msg.GetMessageName(), err)
70                 }
71         }()
72
73         var header interface{}
74
75         // encode message header
76         switch msg.GetMessageType() {
77         case api.RequestMessage:
78                 header = &VppRequestHeader{VlMsgID: msgID}
79         case api.ReplyMessage:
80                 header = &VppReplyHeader{VlMsgID: msgID}
81         case api.EventMessage:
82                 header = &VppEventHeader{VlMsgID: msgID}
83         default:
84                 header = &VppOtherHeader{VlMsgID: msgID}
85         }
86
87         buf := new(bytes.Buffer)
88
89         // encode message header
90         if err := struc.Pack(buf, header); err != nil {
91                 return nil, fmt.Errorf("failed to encode message header: %+v, error: %v", header, err)
92         }
93
94         // encode message content
95         if reflect.TypeOf(msg).Elem().NumField() > 0 {
96                 if err := struc.Pack(buf, msg); err != nil {
97                         return nil, fmt.Errorf("failed to encode message data: %+v, error: %v", data, err)
98                 }
99         }
100
101         return buf.Bytes(), nil
102 }
103
104 // DecodeMsg decodes binary-encoded data of a message into provided `Message` structure.
105 func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) (err error) {
106         if msg == nil {
107                 return errors.New("nil message passed in")
108         }
109
110         // try to recover panic which might possibly occur
111         defer func() {
112                 if r := recover(); r != nil {
113                         var ok bool
114                         if err, ok = r.(error); !ok {
115                                 err = fmt.Errorf("%v", r)
116                         }
117                         err = fmt.Errorf("panic occurred during decoding message %s: %v", msg.GetMessageName(), err)
118                 }
119         }()
120
121         var header interface{}
122
123         // check which header is expected
124         switch msg.GetMessageType() {
125         case api.RequestMessage:
126                 header = new(VppRequestHeader)
127         case api.ReplyMessage:
128                 header = new(VppReplyHeader)
129         case api.EventMessage:
130                 header = new(VppEventHeader)
131         default:
132                 header = new(VppOtherHeader)
133         }
134
135         buf := bytes.NewReader(data)
136
137         // decode message header
138         if err = struc.Unpack(buf, header); err != nil {
139                 return fmt.Errorf("failed to decode message header: %+v, error: %v", header, err)
140         }
141
142         // decode message content
143         if err := struc.Unpack(buf, msg); err != nil {
144                 return fmt.Errorf("failed to decode message data: %+v, error: %v", data, err)
145         }
146
147         return nil
148 }
149
150 func (*MsgCodec) DecodeMsgContext(data []byte, msg api.Message) (uint32, error) {
151         if msg == nil {
152                 return 0, errors.New("nil message passed in")
153         }
154
155         var header interface{}
156         var getContext func() uint32
157
158         // check which header is expected
159         switch msg.GetMessageType() {
160         case api.RequestMessage:
161                 header = new(VppRequestHeader)
162                 getContext = func() uint32 { return header.(*VppRequestHeader).Context }
163
164         case api.ReplyMessage:
165                 header = new(VppReplyHeader)
166                 getContext = func() uint32 { return header.(*VppReplyHeader).Context }
167
168         default:
169                 return 0, nil
170         }
171
172         buf := bytes.NewReader(data)
173
174         // decode message header
175         if err := struc.Unpack(buf, header); err != nil {
176                 return 0, fmt.Errorf("decoding message header failed: %v", err)
177         }
178
179         return getContext(), nil
180 }