Merge "Add .gitreview"
[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         ClientIndex 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 // EncodeMsg encodes provided `Message` structure into its binary-encoded data representation.
56 func (*MsgCodec) EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) {
57         if msg == nil {
58                 return nil, errors.New("nil message passed in")
59         }
60
61         // try to recover panic which might possibly occur in struc.Pack call
62         defer func() {
63                 if r := recover(); r != nil {
64                         var ok bool
65                         if err, ok = r.(error); !ok {
66                                 err = fmt.Errorf("%v", r)
67                         }
68                         err = fmt.Errorf("panic occurred: %v", err)
69                 }
70         }()
71
72         var header interface{}
73
74         // encode message header
75         switch msg.GetMessageType() {
76         case api.RequestMessage:
77                 header = &VppRequestHeader{VlMsgID: msgID}
78         case api.ReplyMessage:
79                 header = &VppReplyHeader{VlMsgID: msgID}
80         case api.EventMessage:
81                 header = &VppEventHeader{VlMsgID: msgID}
82         default:
83                 header = &VppOtherHeader{VlMsgID: msgID}
84         }
85
86         buf := new(bytes.Buffer)
87
88         // encode message header
89         if err := struc.Pack(buf, header); err != nil {
90                 return nil, fmt.Errorf("failed to encode message header: %+v, error: %v", header, err)
91         }
92
93         // encode message content
94         if reflect.TypeOf(msg).Elem().NumField() > 0 {
95                 if err := struc.Pack(buf, msg); err != nil {
96                         return nil, fmt.Errorf("failed to encode message data: %+v, error: %v", data, err)
97                 }
98         }
99
100         return buf.Bytes(), nil
101 }
102
103 // DecodeMsg decodes binary-encoded data of a message into provided `Message` structure.
104 func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) error {
105         if msg == nil {
106                 return errors.New("nil message passed in")
107         }
108
109         var header interface{}
110
111         // check which header is expected
112         switch msg.GetMessageType() {
113         case api.RequestMessage:
114                 header = new(VppRequestHeader)
115         case api.ReplyMessage:
116                 header = new(VppReplyHeader)
117         case api.EventMessage:
118                 header = new(VppEventHeader)
119         default:
120                 header = new(VppOtherHeader)
121         }
122
123         buf := bytes.NewReader(data)
124
125         // decode message header
126         if err := struc.Unpack(buf, header); err != nil {
127                 return fmt.Errorf("failed to decode message header: %+v, error: %v", header, err)
128         }
129
130         // decode message content
131         if err := struc.Unpack(buf, msg); err != nil {
132                 return fmt.Errorf("failed to decode message data: %+v, error: %v", data, err)
133         }
134
135         return nil
136 }
137
138 func (*MsgCodec) DecodeMsgContext(data []byte, msg api.Message) (uint32, error) {
139         if msg == nil {
140                 return 0, errors.New("nil message passed in")
141         }
142
143         var header interface{}
144         var getContext func() uint32
145
146         // check which header is expected
147         switch msg.GetMessageType() {
148         case api.RequestMessage:
149                 header = new(VppRequestHeader)
150                 getContext = func() uint32 { return header.(*VppRequestHeader).Context }
151
152         case api.ReplyMessage:
153                 header = new(VppReplyHeader)
154                 getContext = func() uint32 { return header.(*VppReplyHeader).Context }
155
156         default:
157                 return 0, nil
158         }
159
160         buf := bytes.NewReader(data)
161
162         // decode message header
163         if err := struc.Unpack(buf, header); err != nil {
164                 return 0, fmt.Errorf("decoding message header failed: %v", err)
165         }
166
167         return getContext(), nil
168 }