// ------------------------------------------------------------------------------------------------ #include "Core/Buffer.hpp" // ------------------------------------------------------------------------------------------------ #include // ------------------------------------------------------------------------------------------------ namespace SqMod { /* ------------------------------------------------------------------------------------------------ * Compute the next power of two for the specified number. */ SQMOD_NODISCARD inline unsigned int NextPow2(unsigned int num) { --num; num |= num >> 1u; num |= num >> 2u; num |= num >> 4u; num |= num >> 8u; num |= num >> 16u; return ++num; } // ------------------------------------------------------------------------------------------------ Buffer::Buffer(const Buffer & o) : m_Ptr(nullptr), m_Cap(o.m_Cap), m_Cur(o.m_Cur) { if (m_Cap) { Request(o.m_Cap); std::memcpy(m_Ptr, o.m_Ptr, o.m_Cap); } } // ------------------------------------------------------------------------------------------------ Buffer::~Buffer() { // Do we have a buffer? if (m_Ptr) { Release(); // Release it! } } // ------------------------------------------------------------------------------------------------ Buffer & Buffer::operator = (const Buffer & o) // NOLINT(cert-oop54-cpp) { if (m_Ptr != o.m_Ptr) { // Can we work in the current buffer? if (m_Cap && o.m_Cap <= m_Cap) { // It's safe to copy the data std::memcpy(m_Ptr, o.m_Ptr, o.m_Cap); } // Do we even have data to copy? else if (!o.m_Cap) { // Do we have a buffer? if (m_Ptr) { Release(); // Release it! } } else { // Do we have a buffer? if (m_Ptr) { Release(); // Release it! } // Request a larger buffer Request(o.m_Cap); // Now it's safe to copy the data std::memcpy(m_Ptr, o.m_Ptr, o.m_Cap); } // Also copy the edit cursor m_Cur = o.m_Cur; } return *this; } // ------------------------------------------------------------------------------------------------ void Buffer::Grow(SzType n) { // Backup the current memory Buffer bkp(m_Ptr, m_Cap, m_Cur); // Acquire a bigger buffer Request(bkp.m_Cap + n); // Copy the data from the old buffer std::memcpy(m_Ptr, bkp.m_Ptr, bkp.m_Cap); // Copy the previous edit cursor m_Cur = bkp.m_Cur; } // ------------------------------------------------------------------------------------------------ void Buffer::Request(SzType n) { // NOTE: Function assumes (n > 0) assert(n > 0); // Round up the size to a power of two number n = (n & (n - 1)) ? NextPow2(n) : n; // Release previous memory if any delete[] m_Ptr; // Implicitly handles null! // Attempt to allocate memory m_Ptr = new Value[n]; // If no errors occurred then we can set the size m_Cap = n; } // ------------------------------------------------------------------------------------------------ void Buffer::Release() { // Deallocate the memory delete[] m_Ptr; // Implicitly handles null! // Explicitly reset the buffer m_Ptr = nullptr; m_Cap = 0; m_Cur = 0; } // ------------------------------------------------------------------------------------------------ Buffer::SzType Buffer::Write(SzType pos, ConstPtr data, SzType size) { // Do we have what to write? if (!data || !size) { return 0; } // See if the buffer size must be adjusted else if ((pos + size) >= m_Cap) { // Acquire a larger buffer Grow((pos + size) - m_Cap + 32); } // Copy the data into the internal buffer std::memcpy(m_Ptr + pos, data, size); // Return the amount of data written to the buffer return size; } // ------------------------------------------------------------------------------------------------ Buffer::SzType Buffer::WriteF(SzType pos, const char * fmt, ...) { // Initialize the variable argument list va_list args; va_start(args, fmt); // Call the function that takes the variable argument list const SzType ret = WriteF(pos, fmt, args); // Finalize the variable argument list va_end(args); // Return the result return ret; } // ------------------------------------------------------------------------------------------------ Buffer::SzType Buffer::WriteF(SzType pos, const char * fmt, va_list args) { // Is the specified position within range? if (pos >= m_Cap) { // Acquire a larger buffer Grow(pos - m_Cap + 32); } // Backup the variable argument list va_list args_cpy; va_copy(args_cpy, args); // Attempt to write to the current buffer // (if empty, it should tell us the necessary size) int ret = std::vsnprintf(m_Ptr + pos, m_Cap, fmt, args); // Do we need a bigger buffer? if ((pos + ret) >= m_Cap) { // Acquire a larger buffer Grow((pos + ret) - m_Cap + 32); // Retry writing the requested information ret = std::vsnprintf(m_Ptr + pos, m_Cap, fmt, args_cpy); } // Return the value 0 if data could not be written if (ret < 0) { return 0; } // Return the number of written characters return static_cast< SzType >(ret); } // ------------------------------------------------------------------------------------------------ Buffer::SzType Buffer::WriteS(SzType pos, ConstPtr str) { // Is there any string to write? if (str && *str != '\0') { // Forward this to the regular write function return Write(pos, str, static_cast< SzType >(std::strlen(str))); } // Nothing to write return 0; } // ------------------------------------------------------------------------------------------------ void Buffer::AppendF(const char * fmt, ...) { // Initialize the variable argument list va_list args; va_start(args, fmt); // Forward this to the regular write function m_Cur += WriteF(m_Cur, fmt, args); // Finalize the variable argument list va_end(args); } // ------------------------------------------------------------------------------------------------ void Buffer::AppendS(const char * str) { // Is there any string to write? if (str) { m_Cur += Write(m_Cur, str, static_cast< SzType >(std::strlen(str))); } } } // Namespace:: SqMod