// Copyright (C) 2000, International Business Machines // Corporation and others. All Rights Reserved. #ifndef _BCP_BUFFER_H #define _BCP_BUFFER_H #include #include // This file is fully docified. #include "BCP_error.hpp" #include "BCP_string.hpp" #include "BCP_message_tag.hpp" #include "BCP_message.hpp" #include "BCP_vector.hpp" /** This class describes the message buffer used for all processes of BCP. This buffer is a character array; the components of a message are simply copied into this array one after the other. Note that each process has only one buffer, which serves both for outgoing and incoming messages. This can be done since when a message arrives it is completely unpacked before anything else is done and conversely, once a message is started to be packed together it will be sent out before another message is received. NOTE: Only the following type of objects can be packed with the various pack() member methods: Everything else that needs to be packed at any time must have a pack() member method. */ class BCP_buffer{ public: /* The data members are public for efficiency reasons. The message environment's receiving function should be able to directly manipulate these fields. However, making the virtual base class of all message passing environment to be a friend doesn't help... Anyway, access these fields sparingly.
THINK: maybe it's not that inefficient to access the fields thru functions... */ /**@name Data members The data members are public for efficiency reasons. Access these fields sparingly.
THINK: maybe it's not that inefficient to access the fields thru functions... Note that:
  1. The max size of the buffer never decreases. The buffer max size increases when the current max size is not sufficient for the message.
  2. With normal usage for incoming messages _size stays constant while reading out the message and _pos moves forward in the buffer.
  3. With normal usage for outgoing messages _size and _pos moves forward together as the buffer is filled with the message.
*/ /*@{*/ /** The message tag of the last received message. This member has no meaning if the buffer holds an outgoing message. */ BCP_message_tag _msgtag; /** The process id of the sender of the last received message. This member has no meaning if the buffer holds an outgoing message. */ int _sender; /** The next read position in the buffer. */ size_t _pos; /** The amount of memory allocated for the buffer. */ size_t _max_size; /** The current size of the message (the first _size bytes of the buffer). */ size_t _size; /** Pointer to the buffer itself. */ char* _data; /*@}*/ public: //========================================================================= /**@name Query methods */ /*@{*/ /** Return the message tag of the message in the buffer. */ inline BCP_message_tag msgtag() const { return _msgtag; } /** Return a const pointer to the process id of the sender of the message in the buffer. */ inline int sender() const { return _sender; } /** Return the size of the current message in the buffer. */ inline int size() const { return _size; } /** Return a const pointer to the data stored in the buffer. */ inline const char* data() const { return _data; } /*@}*/ //========================================================================= /**@name Modifying methods */ /*@{*/ /** Position the read head in the buffer. Must be between 0 and size(). */ inline void set_position(const int pos) throw(BCP_fatal_error) { if (pos < 0 || pos > size()) throw BCP_fatal_error("Incorrest buffer position setting.\n"); _pos = pos; } /** Cut off the end of the buffer. Must be between 0 and size(). */ inline void set_size(const int s) throw(BCP_fatal_error) { if (s < 0 || s > size()) throw BCP_fatal_error("Incorrest buffer position setting.\n"); _size = s; } /** Set the message tag on the buffer */ inline void set_msgtag(const BCP_message_tag tag) { _msgtag = tag; } /** Set the buffer to be a copy of the given data. Use this with care! */ void set_content(const char* data, const size_t size, int sender, BCP_message_tag msgtag) { _sender = sender; _msgtag = msgtag; if (_max_size < size) { delete[] _data; _data = new char[size]; _max_size = size; } _pos = 0; _size = size; if (_size) memcpy(_data, data, size * sizeof(char)); } /** Make an exact replica of the other buffer. */ BCP_buffer& operator=(const BCP_buffer& buf) { _msgtag = buf._msgtag; _sender = buf._sender; _pos = buf._pos; if (_max_size < buf._max_size) { delete[] _data; _data = new char[buf._max_size]; _max_size = buf._max_size; } _size = buf._size; if (_size) memcpy(_data, buf._data, _size * sizeof(char)); return *this; } /** Reallocate the buffer if necessary so that at least add_size number of additional bytes will fit into the buffer. */ inline void make_fit(const int add_size){ if (_max_size < _size + add_size) { _max_size = _size + add_size; /* If > 1M then have spare space of 1/16th (~6%) of used space, if <= 1M then have 64K spare space */ _max_size += (_max_size > 1<<20) ? (_max_size >> 4) : (1 << 16) ; char *new_data = new char[_max_size]; if (_size) memcpy(new_data, _data, _size); delete[] _data; _data = new_data; } } /** Completely clear the buffer. Delete and zero out _msgtag, _size, _pos and _sender. */ inline void clear(){ _msgtag = BCP_Msg_NoMessage; _size = 0; _pos = 0; _sender = -1; } /** Pack a single object of type T. Copies sizeof(T) bytes from the address of the object. */ template BCP_buffer& pack(const T& value) { make_fit( sizeof(T) ); memcpy(_data + _size, &value, sizeof(T)); _size += sizeof(T); return *this; } /** Unpack a single object of type T. Copies sizeof(T) bytes to the address of the object. */ template BCP_buffer& unpack(T& value){ #ifdef PARANOID if (_pos + sizeof(T) > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif memcpy(&value, _data + _pos, sizeof(T)); _pos += sizeof(T); return *this; } /** Pack a C style array of objects of type T. */ template BCP_buffer& pack(const T* const values, const int length){ make_fit( sizeof(int) + sizeof(T) * length ); memcpy(_data + _size, &length, sizeof(int)); _size += sizeof(int); if (length > 0){ memcpy(_data + _size, values, sizeof(T) * length); _size += sizeof(T) * length; } return *this; } /** Unpack an array of objects of type T, where T must be a built-in type (ar at least something that can be copied with memcpy). If the third argument is true then memory is allocated for the array and the array pointer and the length of the array are returned in the arguments. If the third argument is false then the arriving array's length is compared to length and an exception is thrown if they are not the same. Also, the array passed as the first argument will be filled with the arriving array. */ template BCP_buffer& unpack(T*& values, int& length, bool allocate = true) throw(BCP_fatal_error) { if (allocate) { #ifdef PARANOID if (_pos + sizeof(int) > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif memcpy(&length, _data + _pos, sizeof(int)); _pos += sizeof(int); if (length > 0){ #ifdef PARANOID if (_pos + sizeof(T)*length > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif values = new T[length]; memcpy(values, _data + _pos, sizeof(T)*length); _pos += sizeof(T) * length; } } else { /* ! allocate */ int l; #ifdef PARANOID if (_pos + sizeof(int) > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif memcpy(&l, _data + _pos, sizeof(int)); _pos += sizeof(int); if (l != length) throw BCP_fatal_error("BCP_buffer::unpack() : bad array lentgh.\n"); if (length > 0){ #ifdef PARANOID if (_pos + sizeof(T)*length > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif memcpy(values, _data + _pos, sizeof(T)*length); _pos += sizeof(T) * length; } } return *this; } /** Pack a BCP_string into the buffer. */ BCP_buffer& pack(const BCP_string& value){ // must define here, 'cos in BCP_message.C we have only templated members int len = value.length(); make_fit( sizeof(int) + len ); memcpy(_data + _size, &len, sizeof(int)); _size += sizeof(int); if (len > 0){ memcpy(_data + _size, value.c_str(), len); _size += len; } return *this; } /** Pack a BCP_string into the buffer. */ BCP_buffer& pack(BCP_string& value){ // must define here, 'cos in BCP_message.C we have only templated members int len = value.length(); make_fit( sizeof(int) + len ); memcpy(_data + _size, &len, sizeof(int)); _size += sizeof(int); if (len > 0){ memcpy(_data + _size, value.c_str(), len); _size += len; } return *this; } /** Unpack a BCP_string from the buffer. */ BCP_buffer& unpack(BCP_string& value){ int len; unpack(len); value.assign(_data + _pos, len); _pos += len; return *this; } // packing/unpacking for BCP_vec /** Pack a BCP_vec into the buffer. */ template BCP_buffer& pack(const BCP_vec& vec) { int objnum = vec.size(); int new_bytes = objnum * sizeof(T); make_fit( sizeof(int) + new_bytes ); memcpy(_data + _size, &objnum, sizeof(int)); _size += sizeof(int); if (objnum > 0){ memcpy(_data + _size, vec.begin(), new_bytes); _size += new_bytes; } return *this; } /** Pack a std::vector into the buffer. */ template BCP_buffer& pack(const std::vector& vec) { int objnum = vec.size(); int new_bytes = objnum * sizeof(T); make_fit( sizeof(int) + new_bytes ); memcpy(_data + _size, &objnum, sizeof(int)); _size += sizeof(int); if (objnum > 0){ memcpy(_data + _size, &vec[0], new_bytes); _size += new_bytes; } return *this; } /** Unpack a BCP_vec from the buffer. */ template BCP_buffer& unpack(BCP_vec& vec) { int objnum; #ifdef PARANOID if (_pos + sizeof(int) > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif memcpy(&objnum, _data + _pos, sizeof(int)); _pos += sizeof(int); vec.clear(); if (objnum > 0){ #ifdef PARANOID if (_pos + sizeof(T)*objnum > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif vec.reserve(objnum); vec.insert(vec.end(), _data + _pos, objnum); _pos += objnum * sizeof(T); } return *this; } /** Unpack a std::vector from the buffer. */ template BCP_buffer& unpack(std::vector& vec) { int objnum; #ifdef PARANOID if (_pos + sizeof(int) > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif memcpy(&objnum, _data + _pos, sizeof(int)); _pos += sizeof(int); vec.clear(); if (objnum > 0){ #ifdef PARANOID if (_pos + sizeof(T)*objnum > _size) throw BCP_fatal_error("Reading over the end of buffer.\n"); #endif vec.insert(vec.end(), objnum, T()); memcpy(&vec[0], _data + _pos, objnum * sizeof(T)); _pos += objnum * sizeof(T); } return *this; } /*@}*/ /**@name Constructors and destructor */ /*@{*/ /** The default constructor creates a buffer of size 16 Kbytes with no message in it. */ BCP_buffer() : _msgtag(BCP_Msg_NoMessage), _sender(-1), _pos(0), _max_size(1<<16/*64K*/), _size(0), _data(new char[_max_size]) {} /** The copy constructor makes an exact replica of the other buffer. */ BCP_buffer(const BCP_buffer& buf) : _msgtag(BCP_Msg_NoMessage), _sender(-1), _pos(0), _max_size(0), _size(0), _data(0){ operator=(buf); } /** The desctructor deletes all data members (including freeing the buffer). */ ~BCP_buffer() { delete[] _data; } /*@}*/ }; #endif