blob: 41f5a78bf15546a8b8d49853b0f79b7841f4a4cc [file] [log] [blame]
// vi: ts=4 sw=4 noet:
/*
==================================================================================
Copyright (c) 2020 Nokia
Copyright (c) 2020 AT&T Intellectual Property.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================================
*/
/*
Mnemonic: message.cpp
Abstract: A message wrapper. This should completely hide the
underlying transport (RMR) message structure from
the user application. For the most part, the getters
are used by the framwork; it is unlikely that other
than adding/extracting the MEID, the user app will
be completely unaware of information that is not
presented in the callback parms.
Date: 12 March 2020
Author: E. Scott Daniels
*/
#include <string.h>
#include <unistd.h>
#include <rmr/rmr.h>
#include <iostream>
#include "message.hpp"
namespace xapp {
// --------------- private ------------------------------------------------
// --------------- builders/operators -------------------------------------
/*
Create a new message wrapper for an existing RMR msg buffer.
*/
xapp::Message::Message( rmr_mbuf_t* mbuf, void* mrc ) {
this->mrc = mrc; // the message router context for sends
this->mbuf = mbuf;
}
xapp::Message::Message( void* mrc, int payload_len ) {
this->mrc = mrc;
this->mbuf = rmr_alloc_msg( mrc, payload_len );
}
/*
Copy builder. Given a source object instance (soi), create a copy.
Creating a copy should be avoided as it can be SLOW!
*/
xapp::Message::Message( const Message& soi ) {
int payload_size;
mrc = soi.mrc;
payload_size = rmr_payload_size( soi.mbuf ); // rmr can handle a nil pointer
mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE );
}
/*
Assignment operator. Simiolar to the copycat, but "this" object exists and
may have data that needs to be released prior to making a copy of the soi.
*/
Message& xapp::Message::operator=( const Message& soi ) {
int payload_size;
if( this != &soi ) { // cannot do self assignment
if( mbuf != NULL ) {
rmr_free_msg( mbuf ); // release the old one so we don't leak
}
payload_size = rmr_payload_size( soi.mbuf ); // rmr can handle a nil pointer
mrc = soi.mrc;
mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE );
}
return *this;
}
/*
Move builder. Given a source object instance (soi), move the information from
the soi ensuring that the destriction of the soi doesn't trash things from
under us.
*/
xapp::Message::Message( Message&& soi ) {
mrc = soi.mrc;
mbuf = soi.mbuf;
soi.mrc = NULL; // prevent closing of RMR stuff on soi destroy
soi.mbuf = NULL;
}
/*
Move Assignment operator. Move the message data to the existing object
ensure the object reference is cleaned up, and ensuring that the source
object references are removed.
*/
Message& xapp::Message::operator=( Message&& soi ) {
if( this != &soi ) { // cannot do self assignment
if( mbuf != NULL ) {
rmr_free_msg( mbuf ); // release the old one so we don't leak
}
mrc = soi.mrc;
mbuf = soi.mbuf;
soi.mrc = NULL;
soi.mbuf = NULL;
}
return *this;
}
/*
Destroyer.
*/
xapp::Message::~Message() {
if( mbuf != NULL ) {
rmr_free_msg( mbuf );
}
mbuf = NULL;
}
// --- getters/setters -----------------------------------------------------
/*
Copy the payload bytes, and return a smart pointer (unique) to it.
If the application needs to update the payload in place for a return
to sender call, or just to access the payload in a more efficent manner
(without the copy), the Get_payload() function should be considered.
This function will return a NULL pointer if malloc fails.
*/
//char* Message::Copy_payload( ){
std::unique_ptr<unsigned char> xapp::Message::Copy_payload( ){
unsigned char* new_payload = NULL;
if( mbuf != NULL ) {
new_payload = (unsigned char *) malloc( sizeof( unsigned char ) * mbuf->len );
memcpy( new_payload, mbuf->payload, mbuf->len );
}
return std::unique_ptr<unsigned char>( new_payload );
}
/*
Makes a copy of the MEID and returns a smart pointer to it.
*/
std::unique_ptr<unsigned char> xapp::Message::Get_meid(){
unsigned char* m = NULL;
m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_MEID );
rmr_get_meid( mbuf, m );
return std::unique_ptr<unsigned char>( m );
}
/*
Return the total size of the payload (the amount that can be written to
as opposed to the portion of the payload which is currently in use.
If mbuf isn't valid (nil, or message has a broken header) the return
will be -1.
*/
int xapp::Message::Get_available_size(){
return rmr_payload_size( mbuf ); // rmr can handle a nil pointer
}
int xapp::Message::Get_mtype(){
int rval = INVALID_MTYPE;
if( mbuf != NULL ) {
rval = mbuf->mtype;
}
return rval;
}
/*
Makes a copy of the source field and returns a smart pointer to it.
*/
std::unique_ptr<unsigned char> xapp::Message::Get_src(){
unsigned char* m = NULL;
m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_SRC );
memset( m, 0, sizeof( unsigned char ) * RMR_MAX_SRC );
if( m != NULL ) {
rmr_get_src( mbuf, m );
}
return std::unique_ptr<unsigned char>( m );
}
int xapp::Message::Get_state( ){
int state = INVALID_STATUS;
if( mbuf != NULL ) {
state = mbuf->state;
}
return state;
}
int xapp::Message::Get_subid(){
int rval = INVALID_SUBID;
if( mbuf != NULL ) {
rval =mbuf->sub_id;
}
return rval;
}
/*
Return the amount of the payload (bytes) which is used. See
Get_available_size() to get the total usable space in the payload.
*/
int xapp::Message::Get_len(){
int rval = 0;
if( mbuf != NULL ) {
rval = mbuf->len;
}
return rval;
}
/*
This returns a smart (unique) pointer to the payload portion of the
message. This provides the user application with the means to
update the payload in place to avoid multiple copies. The
user programme is responsible to determing the usable payload
length by calling Message:Get_available_size(), and ensuring that
writing beyond the indicated size does not happen.
*/
Msg_component xapp::Message::Get_payload(){
if( mbuf != NULL ) {
return std::unique_ptr<unsigned char, unfreeable>( mbuf->payload );
}
return NULL;
}
void xapp::Message::Set_meid( std::shared_ptr<unsigned char> new_meid ) {
if( mbuf != NULL ) {
rmr_str2meid( mbuf, (unsigned char *) new_meid.get() );
}
}
void xapp::Message::Set_mtype( int new_type ){
if( mbuf != NULL ) {
mbuf->mtype = new_type;
}
}
void xapp::Message::Set_len( int new_len ){
if( mbuf != NULL && new_len >= 0 ) {
mbuf->len = new_len;
}
}
void xapp::Message::Set_subid( int new_subid ){
if( mbuf != NULL ) {
mbuf->sub_id = new_subid;
}
}
// -------------- send functions ---------------------------------
/*
This assumes that the contents of the mbuf were set by either a send attempt that
failed with a retry and thus is ready to be processed by RMR.
Exposed to the user, but not expected to be frequently used.
*/
bool xapp::Message::Send( ) {
bool state = false;
if( mbuf != NULL ) {
mbuf = rmr_send_msg( mrc, mbuf ); // send and pick up new mbuf
state = mbuf->state == RMR_OK; // overall state for caller
}
return state;
}
/*
Similar to Send(), this assumes that the message is already set up and this is a retry.
Exposed to the user, but not expected to be frequently used.
*/
bool xapp::Message::Reply( ) {
bool state = false;
if( mbuf != NULL ) {
mbuf = rmr_rts_msg( mrc, mbuf ); // send and pick up new mbuf
state = mbuf->state == RMR_OK; // state for caller based on send
}
return state;
}
/*
Send workhorse.
This will setup the message (type etc.) ensure the message payload space is
large enough and copy in the payload (if a new payload is given), then will
either send or rts the message based on the stype parm.
If payload is nil, then we assume the user updated the payload in place and
no copy is needed.
This is public, but most users should use Send_msg or Send_response functions.
*/
bool xapp::Message::Send( int mtype, int subid, int payload_len, unsigned char* payload, int stype, rmr_whid_t whid ) {
bool state = false;
if( mbuf != NULL ) {
if( mtype != NO_CHANGE ) {
mbuf->mtype = mtype;
}
if( subid != NO_CHANGE ) {
mbuf->sub_id = subid;
}
if( payload_len != NO_CHANGE ) {
mbuf->len = payload_len;
}
if( payload != NULL ) { // if we have a payload, ensure msg has room, realloc if needed, then copy
mbuf = rmr_realloc_payload( mbuf, payload_len, RMR_NO_COPY, RMR_NO_CLONE ); // ensure message is large enough
if( mbuf == NULL ) {
return false;
}
memcpy( mbuf->payload, payload, mbuf->len );
}
switch( stype ) {
case RESPONSE:
mbuf = rmr_rts_msg( mrc, mbuf );
break;
case MESSAGE:
mbuf = rmr_send_msg( mrc, mbuf );
break;
case WORMHOLE_MSG:
mbuf = rmr_wh_send_msg( mrc, whid, mbuf );
break;
}
state = mbuf->state == RMR_OK;
}
return state;
}
/*
Send a response to the endpoint that sent the original message.
Response can be null and the assumption will be that the message payload
was updated in place and no additional copy is needed before sending the message.
The second form of the call allows for a stack allocated buffer (e.g. char foo[120]) to
be passed as the payload.
*/
bool xapp::Message::Send_response( int mtype, int subid, int response_len, std::shared_ptr<unsigned char> response ) {
return Send( mtype, subid, response_len, response.get(), RESPONSE, NO_WHID );
}
bool xapp::Message::Send_response( int mtype, int subid, int response_len, unsigned char* response ) {
return Send( mtype, subid, response_len, response, RESPONSE, NO_WHID );
}
/*
These allow a response message to be sent without changing the mtype/subid.
*/
bool xapp::Message::Send_response( int response_len, std::shared_ptr<unsigned char> response ) {
return Send( NO_CHANGE, NO_CHANGE, response_len, response.get(), RESPONSE, NO_WHID );
}
bool xapp::Message::Send_response( int response_len, unsigned char* response ) {
return Send( NO_CHANGE, NO_CHANGE, response_len, response, RESPONSE, NO_WHID );
}
/*
Send a message based on message type routing.
Payload can be null and the assumption will be that the message payload
was updated in place and no additional copy is needed before sending the message.
Return is a new mbuf suitable for sending another message, or the original buffer with
a bad state sent if there was a failure.
*/
bool xapp::Message::Send_msg( int mtype, int subid, int payload_len, std::shared_ptr<unsigned char> payload ) {
return Send( mtype, subid, payload_len, payload.get(), MESSAGE, NO_WHID );
}
bool xapp::Message::Send_msg( int mtype, int subid, int payload_len, unsigned char* payload ) {
return Send( mtype, subid, payload_len, payload, MESSAGE, NO_WHID );
}
/*
Similar send functions that allow the message type/subid to remain unchanged
*/
bool xapp::Message::Send_msg( int payload_len, std::shared_ptr<unsigned char> payload ) {
return Send( NO_CHANGE, NO_CHANGE, payload_len, payload.get(), MESSAGE, NO_WHID );
}
bool xapp::Message::Send_msg( int payload_len, unsigned char* payload ) {
return Send( NO_CHANGE, NO_CHANGE, payload_len, payload, MESSAGE, NO_WHID );
}
/*
Wormhole send allows an xAPP to send a message directly based on an existing
wormhole ID (use xapp::Wormhole_open() to get one). Wormholes should NOT be
used for reponse messages, but are intended for things like route tables and
alarm messages where routing doesn't exist/apply.
*/
bool xapp::Message::Wormhole_send( int whid, int mtype, int subid, int payload_len, std::shared_ptr<unsigned char> payload ) {
return Send( mtype, subid, payload_len, payload.get(), WORMHOLE_MSG, whid );
}
} // namespace