/* * (c) Copyright, Real-Time Innovations, 2022. * All rights reserved. * * No duplications, whole or partial, manual or electronic, may be made * without express written permission. Any such copies, or * revisions thereof, must display this notice unaltered. * This code contains trade secrets of Real-Time Innovations, Inc. */ #ifndef STRING_BUFFER_HPP #define STRING_BUFFER_HPP #include "rti/apputils/ServiceException.hpp" namespace rti { namespace apputils { // Default value of a StringBuffer constexpr size_t STRING_BUFFER_LENGTH = 1000; /** * A GenericStringBuffer is a smart buffer that allocates an INITIAL_CAPACITY * in the stack and allocates more capacity in the heap if needed. The template * parameter INITIAL_CAPACITY determines the capacity that is allocated in the * stack. In case there is a resize which involves heap allocation, the * destructor of the GenericStringBuffer class frees the memory. */ template class GenericStringBuffer { public: /** * This constructor creates a new empty GenericStringBuffer with * INITIAL_CAPACITY. */ GenericStringBuffer() : capacity_(INITIAL_CAPACITY), c_str_ptr_(nullptr) { RTIOsapiMemory_zero(c_str_, capacity_); c_str_ptr_ = c_str_; } /** * This constructor creates a new GenericStringBuffer ensuring the size * of the input parameter. It allocates memory in the heap if necessary. * @param size Size to ensure. */ GenericStringBuffer(const size_t size) : capacity_(INITIAL_CAPACITY), c_str_ptr_(nullptr) { RTIOsapiMemory_zero(c_str_, capacity_); c_str_ptr_ = c_str_; ensure_capacity(size); } /** * This constructor creates a new GenericStringBuffer object containing * an input string. It ensures whatever capacity is necessary for the input * string. * @param str std::string that initializes the GenericStringBuffer. */ GenericStringBuffer(const std::string& str) : capacity_(INITIAL_CAPACITY), c_str_ptr_(nullptr) { RTIOsapiMemory_zero(c_str_, capacity_); c_str_ptr_ = c_str_; ensure_capacity(str.length() + 1); RTIOsapiUtility_unusedReturnValue( RTIOsapiUtility_strcpy(c_str_ptr_, str.length(), str.c_str()), char *); } /** * Copy constructor. * @param other String buffer that we want to copy */ GenericStringBuffer(const GenericStringBuffer& other) : capacity_(INITIAL_CAPACITY), c_str_ptr_(nullptr) { RTIOsapiMemory_zero(c_str_, capacity_); c_str_ptr_ = c_str_; ensure_capacity(other.length() + 1); RTIOsapiUtility_unusedReturnValue( RTIOsapiUtility_strcpy( c_str_ptr_, other.length(), other.c_str()), char *); } /** * Move constructor. * @param other String buffer that we want to move */ GenericStringBuffer(GenericStringBuffer&& other) : capacity_(INITIAL_CAPACITY), c_str_ptr_(nullptr) { *this = std::move(other); } /** * Destructor of the GenericStringBuffer. It frees whatever memory may * have been allocated by the ensure_capacity method(). */ ~GenericStringBuffer() { if (capacity_ > INITIAL_CAPACITY) { DDS_String_free(c_str_ptr_); c_str_ptr_ = nullptr; } } /** * Copy-assignment operator * @param other The buffer we want to copy. * @return Object in which we have copied. */ GenericStringBuffer& operator=(const GenericStringBuffer& other) { if (this != &other) { ensure_capacity(other.length() + 1); RTIOsapiUtility_unusedReturnValue( RTIOsapiUtility_strcpy( c_str_ptr_, other.length(), other.c_str()), char *); } return *this; } /** * Move-assignment operator * @param other The buffer we want to move. * @return Object in which we have moved. */ GenericStringBuffer& operator=(GenericStringBuffer&& other) { if (this != &other) { if (other.capacity_ <= INITIAL_CAPACITY) { RTIOsapiMemory_move(c_str_, other.c_str_, INITIAL_CAPACITY); c_str_ptr_ = c_str_; } else { capacity_ = other.capacity_; c_str_ptr_ = other.c_str_ptr_; other.capacity_ = INITIAL_CAPACITY; other.c_str_ptr_ = nullptr; } } return *this; } /** * Returns the number of bytes that can be written in the internal char * * buffer * @return Capacity of the internal char * buffer. */ size_t capacity() const { return capacity_; } /** * Returns the length of the internal char * buffer. That is the strlen * of whatever is in there. * @return Size of the string stored in the internal char * buffer. */ size_t length() const { return strlen(c_str_ptr_); } /** * Returns a const pointer to the internal char * buffer. * @return Const pointer to the internal char * buffer. */ const char *c_str() const { return c_str_ptr_; } /** * Returns a pointer to the internal char * buffer * @return Pointer to the internal char * buffer. */ char *c_str_ptr() { return c_str_ptr_; } /** * This method ensures a capacity by either asserting that the * GenericStringBuffer has enough space to allocate a string, or * reallocating some space in the heap. If that is the case more space is * allocated in the heap via DDS_String_alloc(). * @param capacity Size that needs to be ensured. */ void ensure_capacity(const size_t capacity) { if (capacity > capacity_) { // We need to free the string if it was already allocated, this // happens when _capacity > INITIAL_CAPACITY if (capacity_ > INITIAL_CAPACITY) { DDS_String_free(c_str_ptr_); } // DDS_String_alloc adds one to the input parameter. We could // spare a byte, but we want to be precise. c_str_ptr_ = DDS_String_alloc(capacity - 1); if (c_str_ptr_ == nullptr) { RTI_THROW_SERVICE_EXCEPTION( &RTI_LOG_ANY_FAILURE_s, "Error allocating string."); } RTIOsapiMemory_zero(c_str_ptr_, capacity_); capacity_ = capacity; } } private: size_t capacity_; char *c_str_ptr_; char c_str_[INITIAL_CAPACITY]; }; /** * A StringBuffer is a GenericStringBuffer with an initial STRING_BUFFER_LENGTH. */ typedef GenericStringBuffer StringBuffer; /** * This method saves a "freeform string" into a StringBuffer object. * @param [in,out] string_buffer StringBuffer in which the input string will * be saved. Note that if there's not enough capacity, the method will * automatically allocate more space for the string. * @param format Format of the string to be saved. * @param ... Parameters to the formatted string. */ inline void StringBuffer_save_freeform( StringBuffer& string_buffer, const char *format, ...) { char *c_str_ptr = string_buffer.c_str_ptr(); size_t capacity = string_buffer.capacity(); va_list args, args_aux; RTIXMLSaveContext save_ctxt = RTIXMLSaveContext_INITIALIZER; RTIXMLSaveContext_initialize(&save_ctxt); save_ctxt.sout = c_str_ptr; if (!RTIOsapiUtility_sizeToUInt32(save_ctxt.ssize, capacity)) { RTI_THROW_SERVICE_EXCEPTION( &RTI_LOG_ANY_FAILURE_s, "Error allocating string."); } // Wrap RTIXMLSaveContext_freeform_w_argsI call with va_start and va_end // of the va_list that we need to pass in. va_start(args, format); va_start(args_aux, format); RTIXMLSaveContext_freeform_v(&save_ctxt, format, args, args_aux); va_end(args); va_end(args_aux); if (save_ctxt.outputStringLength + 1 > capacity) { capacity = save_ctxt.outputStringLength + 1; string_buffer.ensure_capacity(capacity); RTIXMLSaveContext_initialize(&save_ctxt); save_ctxt.sout = string_buffer.c_str_ptr(); if (!RTIOsapiUtility_sizeToUInt32(save_ctxt.ssize, capacity)) { RTI_THROW_SERVICE_EXCEPTION( &RTI_LOG_ANY_FAILURE_s, "Error allocating string."); } // Wrap RTIXMLSaveContext_freeform_w_argsI call with va_start and // va_end of the va_list that we need to pass in. va_start(args, format); va_start(args_aux, format); RTIXMLSaveContext_freeform_v(&save_ctxt, format, args, args_aux); va_end(args); va_end(args_aux); } } }} // namespace rti::apputils #endif // STRING_BUFFER_HPP