30c754818ea27f9305cea6d73145cff3ba48f882
[ric-plt/xapp-frame-cpp.git] / src / messaging / message.cpp
1 // vi: ts=4 sw=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2020 Nokia
5         Copyright (c) 2020 AT&T Intellectual Property.
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 ==================================================================================
19 */
20
21 /*
22         Mnemonic:       message.cpp
23         Abstract:       A message wrapper. This should completely hide the
24                                 underlying transport (RMR) message structure from
25                                 the user application. For the most part, the getters
26                                 are used by the framwork; it is unlikely that other
27                                 than adding/extracting the MEID, the user app will
28                                 be completely unaware of information that is not
29                                 presented in the callback parms.
30
31         Date:           12 March 2020
32         Author:         E. Scott Daniels
33 */
34
35 #include <string.h>
36 #include <unistd.h>
37
38 #include <rmr/rmr.h>
39
40 #include <iostream>
41
42 #include "message.hpp"
43
44 namespace xapp {
45
46
47
48 // --------------- private ------------------------------------------------
49
50 // --------------- builders/operators  -------------------------------------
51
52 /*
53         Create a new message wrapper for an existing RMR msg buffer.
54 */
55 xapp::Message::Message( rmr_mbuf_t* mbuf, void* mrc ) :
56         mrc(  mrc ),                    // the message router context for sends
57         mbuf(  mbuf )
58 {  /* empty body */ }
59
60 xapp::Message::Message( void* mrctx, int payload_len ) :
61         mrc(  mrctx ),                                                                          // the message router context for sends
62         mbuf(  rmr_alloc_msg( mrc, payload_len ) )
63 { /* empty body */ }
64 //      this->mrc = mrc;
65         //this->mbuf = rmr_alloc_msg( mrc, payload_len );
66
67 /*
68         Copy builder.  Given a source object instance (soi), create a copy.
69         Creating a copy should be avoided as it can be SLOW!
70 */
71 xapp::Message::Message( const Message& soi ) :
72         mrc( soi.mrc )
73 {
74         int payload_size;
75
76         payload_size = rmr_payload_size( soi.mbuf );            // rmr can handle a nil pointer
77         mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE );
78 }
79
80 /*
81         Assignment operator. Simiolar to the copycat, but "this" object exists and
82         may have data that needs to be released prior to making a copy of the soi.
83 */
84 Message& xapp::Message::operator=( const Message& soi ) {
85         int     payload_size;
86
87         if( this != &soi ) {                            // cannot do self assignment
88                 if( mbuf != NULL ) {
89                         rmr_free_msg( mbuf );           // release the old one so we don't leak
90                 }
91
92                 payload_size = rmr_payload_size( soi.mbuf );            // rmr can handle a nil pointer
93                 mrc = soi.mrc;
94                 mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE );
95         }
96
97         return *this;
98 }
99
100 /*
101         Move builder.  Given a source object instance (soi), move the information from
102         the soi ensuring that the destriction of the soi doesn't trash things from
103         under us.
104 */
105 xapp::Message::Message( Message&& soi ) :
106         mrc(  soi.mrc ),
107         mbuf(  soi.mbuf )
108 {
109         soi.mrc = NULL;         // prevent closing of RMR stuff on soi destroy
110         soi.mbuf = NULL;
111 }
112
113 /*
114         Move Assignment operator. Move the message data to the existing object
115         ensure the object reference is cleaned up, and ensuring that the source
116         object references are removed.
117 */
118 Message& xapp::Message::operator=( Message&& soi ) {
119         if( this != &soi ) {                            // cannot do self assignment
120                 if( mbuf != NULL ) {
121                         rmr_free_msg( mbuf );           // release the old one so we don't leak
122                 }
123
124                 mrc = soi.mrc;
125                 mbuf = soi.mbuf;
126
127                 soi.mrc = NULL;
128                 soi.mbuf = NULL;
129         }
130
131         return *this;
132 }
133
134
135 /*
136         Destroyer.
137 */
138 xapp::Message::~Message() {
139         if( mbuf != NULL ) {
140                 rmr_free_msg( mbuf );
141         }
142
143         mbuf = NULL;
144 }
145
146
147 // --- getters/setters -----------------------------------------------------
148 /*
149         Copy the payload bytes, and return a smart pointer (unique) to it.
150         If the application needs to update the payload in place for a return
151         to sender call, or just to access the payload in a more efficent manner
152         (without the copy), the Get_payload() function should be considered.
153
154         This function will return a NULL pointer if malloc fails.
155 */
156 //char* Message::Copy_payload( ){
157 std::unique_ptr<unsigned char> xapp::Message::Copy_payload( ){
158
159         if( mbuf != NULL ) {
160                 unsigned char*  new_payload = new unsigned char[mbuf->len];
161                 memcpy( new_payload, mbuf->payload, mbuf->len );
162                 return std::unique_ptr<unsigned char>( new_payload );
163         }
164
165         return NULL;
166 }
167
168 /*
169         Makes a copy of the MEID and returns a smart pointer to it.
170 */
171 std::unique_ptr<unsigned char> xapp::Message::Get_meid() const {
172         unsigned char* m = NULL;
173
174         m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_MEID );
175         rmr_get_meid( mbuf, m );
176
177         return std::unique_ptr<unsigned char>( m );
178 }
179
180 /*
181         Return the total size of the payload (the amount that can be written to
182         as opposed to the portion of the payload which is currently in use.
183         If mbuf isn't valid (nil, or message has a broken header) the return
184         will be -1.
185 */
186 int xapp::Message::Get_available_size() const {
187         return rmr_payload_size( mbuf );                // rmr can handle a nil pointer
188 }
189
190 int     xapp::Message::Get_mtype() const {
191         int rval = INVALID_MTYPE;
192
193         if( mbuf != NULL ) {
194                 rval = mbuf->mtype;
195         }
196
197         return rval;
198 }
199
200 /*
201         Makes a copy of the source field and returns a smart pointer to it.
202 */
203 std::unique_ptr<unsigned char> xapp::Message::Get_src() const {
204         unsigned char* m = new unsigned char[RMR_MAX_SRC];
205
206         if( m != NULL ) {
207                 rmr_get_src( mbuf, m );
208         }
209
210         return std::unique_ptr<unsigned char>( m );
211 }
212
213 int     xapp::Message::Get_state( ) const {
214         int state = INVALID_STATUS;
215
216         if( mbuf != NULL ) {
217                 state =  mbuf->state;
218         }
219
220         return state;
221 }
222
223 int     xapp::Message::Get_subid() const {
224         int     rval = INVALID_SUBID;
225
226         if( mbuf != NULL ) {
227                 rval =mbuf->sub_id;
228         }
229
230         return rval;
231 }
232
233 /*
234         Return the amount of the payload (bytes) which is used. See
235         Get_available_size() to get the total usable space in the payload.
236 */
237 int     xapp::Message::Get_len() const {
238         int rval = 0;
239
240         if( mbuf != NULL ) {
241                 rval = mbuf->len;
242         }
243
244         return rval;
245 }
246
247 /*
248         This returns a smart (unique) pointer to the payload portion of the
249         message. This provides the user application with the means to
250         update the payload in place to avoid multiple copies.  The
251         user programme is responsible to determing the usable payload
252         length by calling Message:Get_available_size(), and ensuring that
253         writing beyond the indicated size does not happen.
254 */
255 Msg_component xapp::Message::Get_payload() const {
256         if( mbuf != NULL ) {
257                 return std::unique_ptr<unsigned char, unfreeable>( mbuf->payload );
258         }
259
260         return NULL;
261 }
262
263 void xapp::Message::Set_meid( std::shared_ptr<unsigned char> new_meid ) {
264         if( mbuf != NULL ) {
265                 rmr_str2meid( mbuf, new_meid.get() );
266         }
267 }
268
269 void xapp::Message::Set_mtype( int new_type ){
270         if( mbuf != NULL ) {
271                 mbuf->mtype = new_type;
272         }
273 }
274
275 void xapp::Message::Set_len( int new_len ){
276         if( mbuf != NULL  && new_len >= 0 ) {
277                 mbuf->len = new_len;
278         }
279 }
280
281 void xapp::Message::Set_subid( int new_subid ){
282         if( mbuf != NULL ) {
283                 mbuf->sub_id = new_subid;
284         }
285 }
286
287
288 // -------------- send functions ---------------------------------
289
290 /*
291         This assumes that the contents of the mbuf were set by either a send attempt that
292         failed with a retry and thus is ready to be processed by RMR.
293         Exposed to the user, but not expected to be frequently used.
294 */
295 bool xapp::Message::Send( ) {
296         bool state = false;
297
298         if( mbuf != NULL ) {
299                 mbuf =  rmr_send_msg( mrc, mbuf );      // send and pick up new mbuf
300                 state = mbuf->state == RMR_OK;          // overall state for caller
301         }
302
303         return state;
304 }
305
306 /*
307         Similar to Send(), this assumes that the message is already set up and this is a retry.
308         Exposed to the user, but not expected to be frequently used.
309 */
310 bool xapp::Message::Reply( ) {
311         bool state = false;
312
313         if( mbuf != NULL ) {
314                 mbuf =  rmr_rts_msg( mrc, mbuf );               // send and pick up new mbuf
315                 state = mbuf->state == RMR_OK;                  // state for caller based on send
316         }
317
318         return state;
319 }
320
321 /*
322         Send workhorse.
323         This will setup the message (type etc.) ensure the message payload space is
324         large enough and copy in the payload (if a new payload is given), then will
325         either send or rts the message based on the stype parm.
326
327         If payload is nil, then we assume the user updated the payload in place and
328         no copy is needed.
329
330         This is public, but most users should use Send_msg or Send_response functions.
331 */
332 bool xapp::Message::Send( int mtype, int subid, int payload_len, unsigned char* payload, int stype, rmr_whid_t whid ) {
333         bool state = false;
334
335         if( mbuf != NULL ) {
336                 if( mtype != NO_CHANGE ) {
337                         mbuf->mtype = mtype;
338                 }
339                 if( subid != NO_CHANGE ) {
340                         mbuf->sub_id = subid;
341                 }
342
343                 if( payload_len != NO_CHANGE ) {
344                         mbuf->len = payload_len;
345                 }
346
347                 if( payload != NULL ) {                 // if we have a payload, ensure msg has room, realloc if needed, then copy
348                         mbuf = rmr_realloc_payload( mbuf, payload_len, RMR_NO_COPY, RMR_NO_CLONE );             // ensure message is large enough
349                         if( mbuf == NULL ) {
350                                 return false;
351                         }
352
353                         memcpy( mbuf->payload, payload, mbuf->len );
354                 }
355
356                 switch( stype ) {
357                         case RESPONSE:
358                                 mbuf = rmr_rts_msg( mrc, mbuf );
359                                 break;
360
361                         case MESSAGE:
362                                 mbuf = rmr_send_msg( mrc, mbuf );
363                                 break;
364
365                         case WORMHOLE_MSG:
366                                 mbuf = rmr_wh_send_msg( mrc, whid,  mbuf );
367                                 break;
368
369                         default:
370                                 break;                                  // because sonar doesn't like defaultless switches even when there is no work :(
371                 }
372
373                 state = mbuf->state == RMR_OK;
374         }
375
376         return state;
377 }
378
379 /*
380         Send a response to the endpoint that sent the original message.
381
382         Response can be null and the assumption will be that the message payload
383         was updated in place and no additional copy is needed before sending the message.
384
385         The second form of the call allows for a stack allocated buffer (e.g. char foo[120]) to
386         be passed as the payload.
387 */
388 bool xapp::Message::Send_response(  int mtype, int subid, int response_len, std::shared_ptr<unsigned char> response ) {
389         return Send( mtype, subid, response_len, response.get(), RESPONSE, NO_WHID );
390 }
391
392 bool xapp::Message::Send_response(  int mtype, int subid, int response_len, unsigned char* response ) {
393         return Send( mtype, subid, response_len, response, RESPONSE, NO_WHID );
394 }
395
396 /*
397         These allow a response message to be sent without changing the mtype/subid.
398 */
399 bool xapp::Message::Send_response(  int response_len, std::shared_ptr<unsigned char> response ) {
400         return Send( NO_CHANGE, NO_CHANGE, response_len, response.get(), RESPONSE, NO_WHID );
401 }
402
403 bool xapp::Message::Send_response(  int response_len, unsigned char* response ) {
404         return Send( NO_CHANGE, NO_CHANGE, response_len, response, RESPONSE, NO_WHID );
405 }
406
407
408 /*
409         Send a message based on message type routing.
410
411         Payload can be null and the assumption will be that the message payload
412         was updated in place and no additional copy is needed before sending the message.
413
414         Return is a new mbuf suitable for sending another message, or the original buffer with
415         a bad state sent if there was a failure.
416 */
417 bool xapp::Message::Send_msg(  int mtype, int subid, int payload_len, std::shared_ptr<unsigned char> payload ) {
418         return Send( mtype, subid, payload_len, payload.get(), MESSAGE, NO_WHID );
419 }
420
421 bool xapp::Message::Send_msg(  int mtype, int subid, int payload_len, unsigned char* payload ) {
422         return Send( mtype, subid, payload_len, payload, MESSAGE, NO_WHID );
423 }
424
425 /*
426         Similar send functions that allow the message type/subid to remain unchanged
427 */
428 bool xapp::Message::Send_msg(  int payload_len, std::shared_ptr<unsigned char> payload ) {
429         return Send( NO_CHANGE, NO_CHANGE, payload_len, payload.get(), MESSAGE, NO_WHID );
430 }
431
432 bool xapp::Message::Send_msg(  int payload_len, unsigned char* payload ) {
433         return Send( NO_CHANGE, NO_CHANGE, payload_len, payload, MESSAGE, NO_WHID );
434 }
435
436
437 /*
438         Wormhole send allows an xAPP to send a message directly based on an existing
439         wormhole ID (use xapp::Wormhole_open() to get one). Wormholes should NOT be
440         used for reponse messages, but are intended for things like route tables and
441         alarm messages where routing doesn't exist/apply.
442 */
443 bool xapp::Message::Wormhole_send( int whid, int mtype, int subid, int payload_len, std::shared_ptr<unsigned char> payload ) {
444         return Send( mtype, subid, payload_len, payload.get(), WORMHOLE_MSG, whid );
445 }
446
447
448 } // namespace