#ifndef SDV_PTR_INL #define SDV_PTR_INL #ifdef min #undef min #endif #ifndef SDV_PTR_H #error Do not include "pointer.inl" directly. Include "pointer.h" instead! #endif //!defined SDV_PTR_H #include "pointer.h" #include #include // There are some versions of GCC that produce bogus warnings for -Wstringop-overflow (e.g. version 9.4 warns, 11.4 not - changing // the compile order without changing the logical behavior, will produce different results). // See also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100477 // And https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115074 // Suppress this warning for the string class. // NOTE 03.08.2025: Additional bogus warnigs/errors are suppressed for newer versions of the compiler. #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #pragma GCC diagnostic ignored "-Warray-bounds" #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif #ifdef _MSC_VER // Prevent static code analysis warnings about noexcept for mode constructors and operators. Due to a constexpr if statement, // the constructor might or might not use exceptions and therefore noexcept might or might not be applied. #pragma warning(push) #pragma warning(disable : 26439) #endif /// @cond DOXYGEN_IGNORE namespace sdv { template inline pointer::pointer() noexcept { // Ensure the layout is the same on all platforms. static_assert(offsetof(pointer, m_uiSize) == 0); static_assert(offsetof(pointer, m_rgtData) == 8); static_assert(sizeof(pointer) == 8 + aligned_size_bytes); } template inline pointer::pointer(const pointer& rptr) { resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); } template template inline pointer::pointer(const pointer& rptr) { resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); } template inline pointer::pointer(pointer&& rptr) { resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); rptr.reset(); } template template inline pointer::pointer(pointer&& rptr) { resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); rptr.reset(); } template inline pointer::~pointer() { if constexpr (!std::is_scalar_v) { for (size_t nIndex = 0; nIndex < nFixedSize; nIndex++) m_rgtData[nIndex].~T(); } } template // Member m_uiReserved is not copied. Suppress static code analysis warning. // cppcheck-suppress operatorEqVarError inline pointer& pointer::operator=(const pointer& rptr) { resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); return *this; } template template // Member m_uiReserved is not copied. Suppress static code analysis warning. // cppcheck-suppress operatorEqVarError inline pointer& pointer::operator=(const pointer& rptr) { resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); return *this; } template // Member m_uiReserved is not copied. Suppress static code analysis warning. // cppcheck-suppress operatorEqVarError inline pointer& pointer::operator=(pointer&& rptr) { resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); rptr.reset(); return *this; } template template // Member m_uiReserved is not copied. Suppress static code analysis warning. // cppcheck-suppress operatorEqVarError inline pointer& pointer::operator=(pointer&& rptr) { resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); rptr.reset(); return *this; } template inline void pointer::reset() { resize(0); } template template inline void pointer::swap(pointer& rptr) { // Copy the provided content into a temporary buffer. T rgtTemp[nFixedSizeRight]; size_t nSizeRight = rptr.size(); std::copy_n(rptr.get(), nSizeRight, rgtTemp); // Copy the member buffer into the buffer of the provided pointer rptr.resize(size()); std::copy_n(get(), size(), rptr.get()); // Copy the temporary buffer into this class' buffer. resize(nSizeRight); std::copy_n(rgtTemp, nSizeRight, get()); } template inline typename pointer::element_type* pointer::get() const noexcept { return const_cast(m_rgtData); } template inline T& pointer::operator*() const { return *get(); } template inline T* pointer::operator->() const { return get(); } template inline typename pointer::element_type& pointer::operator[](size_t nIndex) const { if (nIndex >= size()) { XIndexOutOfRange exception; exception.uiIndex = static_cast(nIndex); exception.uiSize = static_cast(size()); throw exception; } return get()[nIndex]; } template inline pointer::operator bool() const noexcept { // Always true return true; } template inline size_t pointer::size() const noexcept { return m_uiSize; } template inline void pointer::resize(size_t nSize) { // Replace all deleted elements with the default constructed elements for (size_t nIndex = nSize; nIndex < std::min(nFixedSize, static_cast(m_uiSize)); nIndex++) { // Destruct the existing and construct the new if constexpr (!std::is_scalar_v) { m_rgtData[nIndex].~T(); new (m_rgtData + nIndex) T(); } else if (nIndex < nFixedSize) m_rgtData[nIndex] = static_cast(0); } if (nSize > nFixedSize) { XBufferTooSmall exception; exception.uiSize = static_cast(nSize); exception.uiCapacity = static_cast(nFixedSize); throw exception; } m_uiSize = static_cast(nSize); } template inline size_t pointer::capacity() const noexcept { return nFixedSize; } template inline pointer::pointer() noexcept { // The size of the pointer class should not be larger than the size of a pointer. static_assert(sizeof(pointer) == sizeof(T*)); // The size of the allocation class should be the size of two pointers and two ints. This is important to ensure that the // location of each element is exactly the same for every process regardless of compiler and library version. static_assert(sizeof(SAllocation) == sizeof(internal::IInternalMemAlloc*) + sizeof(T*) + sizeof(uint32_t) * 2); } namespace core { // Forward declarations pointer AllocMemBytes(size_t nAmount); } // namespace core namespace internal { /** * @brief Internal memory allocation interface doing the actual allocation. This interface is not exposed publicly and * should only be used by the memory manager. */ interface IInternalMemAlloc { /** * @brief Allocate memory. * @param[in] nSize The size of the memory to allocate (in bytes). * @return Pointer to the memory allocation or NULL when memory allocation failed. */ virtual void* Alloc(size_t nSize) = 0; /** * @brief Reallocate memory. * @param[in] pData Pointer to a previous allocation or NULL when no previous allocation was available. * @param[in] nSize The size of the memory to allocate (in bytes). * @return Pointer to the memory allocation or NULL when memory allocation failed. */ virtual void* Realloc(void* pData, size_t nSize) = 0; /** * @brief Free a memory allocation. * @param[in] pData Pointer to a previous allocation. */ virtual void Free(void* pData) = 0; }; } // namespace internal template inline pointer::pointer(const pointer& rptr) { m_psAllocation = rptr.m_psAllocation; if (m_psAllocation) m_psAllocation->uiRefCnt++; } template template inline pointer::pointer(const pointer& rptr) { // Fixed sized buffer; copy the content resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); } template inline pointer::pointer(pointer&& rptr) noexcept { m_psAllocation = rptr.m_psAllocation; rptr.m_psAllocation = nullptr; } template template inline pointer::pointer(const pointer&& rptr) { // Fixed sized buffer; copy the content and reset supplied pointer resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); rptr.reset(); } template inline pointer::~pointer() { try { reset(); } catch (const sdv::XSysExcept&) { } catch (const std::exception&) { } } template inline pointer& pointer::operator=(const pointer& rptr) { // Dynamic buffer, share the allocation reset(); m_psAllocation = rptr.m_psAllocation; if (m_psAllocation) m_psAllocation->uiRefCnt++; return *this; } template template inline pointer& pointer::operator=(const pointer& rptr) { // Fixed sized buffer; copy the content resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); return *this; } template inline pointer& pointer::operator=(pointer&& rptr) noexcept { // Dynamic buffer, take over the allocation. try { reset(); } catch (const sdv::XSysExcept&) { } catch (const std::exception&) { } m_psAllocation = rptr.m_psAllocation; rptr.m_psAllocation = nullptr; return *this; } template template inline pointer& pointer::operator=(pointer&& rptr) { // Fixed sized buffer; copy the content and reset supplied pointer resize(rptr.size()); std::copy_n(rptr.get(), rptr.size(), get()); rptr.reset(); return *this; } template inline void pointer::attach(pointer&& rptrBuffer) { // Move buffer pointer (structures are identical). operator=(std::move(*reinterpret_cast*>(&rptrBuffer))); } template inline pointer&& pointer::detach() { // Move this pointer (structures are identical). return std::move(*reinterpret_cast*>(this)); } template inline void pointer::reset() { // Reduce allocation sharing reference counter. If 0, no more share; delete allocation. if (!m_psAllocation) return; if (!m_psAllocation->pAllocator) throw core::XNoMemMgr(); if (!m_psAllocation->uiRefCnt) { XInvalidRefCount exception; exception.uiCount = 0; throw exception; } if (!--m_psAllocation->uiRefCnt) { // Call the destructor on all elements if constexpr (!std::is_scalar_v) { for (size_t nIndex = 0; m_psAllocation->pData && nIndex < m_psAllocation->uiSize / sizeof(T); nIndex++) m_psAllocation->pData[nIndex].~T(); } // Delete the data if (m_psAllocation->pData) m_psAllocation->pAllocator->Free(m_psAllocation->pData); // Delete the allocation structure delete m_psAllocation; } m_psAllocation = nullptr; } template template inline void pointer::swap(pointer& rptr) { if constexpr (nFixedSizeRight != 0) { // Fixed sized buffer; copy the content // Copy the provided content into a temporary buffer. T rgtTemp[nFixedSizeRight]; size_t nSizeRight = rptr.size(); std::copy_n(rptr.get(), nSizeRight, rgtTemp); // Copy the member buffer into the buffer of the provided pointer rptr.resize(size()); std::copy_n(get(), size(), rptr.get()); // Copy the temporary buffer into this class' buffer. resize(nSizeRight); std::copy_n(rgtTemp, nSizeRight, get()); } else { // Dynamic sized buffer; exchange the pointer SAllocation* pTemp = m_psAllocation; m_psAllocation = rptr.m_psAllocation; rptr.m_psAllocation = pTemp; } } template inline typename pointer::element_type* pointer::get() const noexcept { return m_psAllocation ? m_psAllocation->pData : nullptr; } template inline T& pointer::operator*() const { if (!m_psAllocation) throw XNullPointer(); return *get(); } template inline T* pointer::operator->() const { if (!m_psAllocation) throw XNullPointer(); return get(); } template inline typename pointer::element_type& pointer::operator[](size_t nIndex) const { if (!m_psAllocation) throw XNullPointer(); if (nIndex >= size()) { XIndexOutOfRange exception; exception.uiIndex = static_cast(nIndex); exception.uiSize = static_cast(size()); throw exception; } return get()[nIndex]; } template inline pointer::operator bool() const noexcept { return m_psAllocation && m_psAllocation->pData; } template inline size_t pointer::size() const noexcept { return m_psAllocation ? m_psAllocation->uiSize / sizeof(T) : 0; } template inline void pointer::resize(size_t nSize) { if (!m_psAllocation || !m_psAllocation->pAllocator || !m_psAllocation->pData) { operator=(std::move(make_ptr(nSize))); return; } size_t nTotalSize = nSize * sizeof(T); // Is the new size smaller than the old size, call the destructors for the deleted if (nSize < m_psAllocation->uiSize / sizeof(T)) { if constexpr (!std::is_scalar_v) { for (size_t nIndex = nSize; nIndex < m_psAllocation->uiSize / sizeof(T); nIndex++) m_psAllocation->pData[nIndex].~T(); } // Since the destructors are called; update the size info already. m_psAllocation->uiSize = static_cast(nSize * sizeof(T)); } // Treat reallocation differently between objects and scalars. Objects might have pointers and therefore need to be // copied. Scalars can be reallocated without reinitialization. T* ptAllocation = nullptr; if constexpr (std::is_scalar_v) { ptAllocation = reinterpret_cast(m_psAllocation->pAllocator->Realloc(m_psAllocation->pData, nTotalSize)); if (nTotalSize && !ptAllocation) { core::XAllocFailed exception; exception.uiSize = static_cast(nTotalSize); throw exception; } // Is the new size larger than the old size, call the constructors for the new elements for (size_t nIndex = m_psAllocation->uiSize / sizeof(T); nIndex < nSize; nIndex++) ptAllocation[nIndex] = static_cast(0); } else { // Allocate new size ptAllocation = reinterpret_cast(m_psAllocation->pAllocator->Alloc(nTotalSize)); if (nTotalSize && !ptAllocation) { core::XAllocFailed exception; exception.uiSize = static_cast(nTotalSize); throw exception; } // Move the elements from the existing allocation to the new allocation. for (size_t nIndex = 0; nIndex < std::min(static_cast(m_psAllocation->uiSize / sizeof(T)), nSize); nIndex++) new (ptAllocation + nIndex) T(std::move(m_psAllocation->pData[nIndex])); // Is the new size larger than the old size, call the constructors for the new elements for (size_t nIndex = m_psAllocation->uiSize / sizeof(T); nIndex < nSize; nIndex++) new (ptAllocation + nIndex) T(); // Free the old data. if (m_psAllocation->pData) m_psAllocation->pAllocator->Free(m_psAllocation->pData); } // Set new information m_psAllocation->pData = ptAllocation; m_psAllocation->uiSize = static_cast(nSize * sizeof(T)); } template inline size_t pointer::capacity() const noexcept { return m_psAllocation ? m_psAllocation->uiSize / sizeof(T) : 0; } template inline size_t pointer::ref_count() const noexcept { return m_psAllocation ? static_cast(m_psAllocation->uiRefCnt) : 0; } template inline void swap(pointer& rptrLeft, pointer& rptrRight) { rptrLeft.swap(rptrRight); } template inline bool operator==(const pointer& rptrLeft, const pointer& rptrRight) noexcept { if constexpr (nFixedSizeLeft || nFixedSizeRight) // Compare content { // Prevent comparing NULL-pointer. if (rptrLeft.get() == rptrRight.get()) return true; // Pointers are identical; comparing same object. if (!rptrLeft.size() && !rptrRight.size()) return true; // Both pointers are empty. if (rptrLeft.get() && !rptrRight.get()) return false; // Right is NULL; left not. if (!rptrLeft.get() && rptrRight.get()) return false; // Left is NULL; right not. return std::equal(rptrLeft.get(), rptrLeft.get() + rptrLeft.size(), rptrRight.get(), rptrRight.get() + rptrRight.size()); } else // Compare pointers return rptrLeft.get() == rptrRight.get(); } template inline bool operator!=(const pointer& rptrLeft, const pointer& rptrRight) noexcept { return !operator==(rptrLeft, rptrRight); } template inline bool operator<(const pointer& rptrLeft, const pointer& rptrRight) noexcept { if constexpr (nFixedSizeLeft || nFixedSizeRight) // Compare content return std::lexicographical_compare(rptrLeft.get(), rptrLeft.get() + rptrLeft.size(), rptrRight.get(), rptrRight.get() + rptrRight.size()); else // Compare pointers return rptrLeft.get() < rptrRight.get(); } template inline bool operator<=(const pointer& rptrLeft, const pointer& rptrRight) noexcept { if constexpr (nFixedSizeLeft || nFixedSizeRight) // Compare content return !std::lexicographical_compare(rptrRight.get(), rptrRight.get() + rptrRight.size(), rptrLeft.get(), rptrLeft.get() + rptrLeft.size()); else // Compare pointers return rptrLeft.get() <= rptrRight.get(); } template inline bool operator>(const pointer& rptrLeft, const pointer& rptrRight) noexcept { if constexpr (nFixedSizeLeft || nFixedSizeRight) // Compare content return std::lexicographical_compare(rptrRight.get(), rptrRight.get() + rptrRight.size(), rptrLeft.get(), rptrLeft.get() + rptrLeft.size()); else // Compare pointers return rptrLeft.get() > rptrRight.get(); } template inline bool operator>=(const pointer& rptrLeft, const pointer& rptrRight) noexcept { if constexpr (nFixedSizeLeft || nFixedSizeRight) // Compare content return !std::lexicographical_compare(rptrLeft.get(), rptrLeft.get() + rptrLeft.size(), rptrRight.get(), rptrRight.get() + rptrRight.size()); else // Compare pointers return rptrLeft.get() >= rptrRight.get(); } template inline bool operator==(const pointer& rptrLeft, std::nullptr_t) noexcept { return rptrLeft.get() == nullptr; } template inline bool operator==(std::nullptr_t, const pointer& rptrRight) noexcept { return nullptr == rptrRight.get(); } template inline bool operator!=(const pointer& rptrLeft, std::nullptr_t) noexcept { return rptrLeft.get() != nullptr; } template inline bool operator!=(std::nullptr_t, const pointer& rptrRight) noexcept { return nullptr != rptrRight.get(); } template inline bool operator<(const pointer& /*rptrLeft*/, std::nullptr_t) noexcept { return false; } template inline bool operator<(std::nullptr_t, const pointer& rptrRight) noexcept { return nullptr != rptrRight.get(); } template inline bool operator<=(const pointer& rptrLeft, std::nullptr_t) noexcept { return rptrLeft.get() == nullptr; } template inline bool operator<=(std::nullptr_t, const pointer& /*rptrRight*/) noexcept { return true; } template inline bool operator>(const pointer& rptrLeft, std::nullptr_t) noexcept { return rptrLeft.get() != nullptr; } template inline bool operator>(std::nullptr_t, const pointer& /*rptrRight*/) noexcept { return false; } template inline bool operator>=(const pointer& /*rptrLeft*/, std::nullptr_t) noexcept { return true; } template inline bool operator>=(std::nullptr_t, const pointer& rptrRight) noexcept { return nullptr == rptrRight.get(); } template inline std::basic_ostream& operator<<(std::basic_ostream& rstream, const pointer& rptr) { for (size_t nIndex = 0; nIndex < rptr.size(); nIndex++) rstream << rptr[nIndex]; return rstream; } template inline pointer make_ptr(size_t nSize /*= 1*/) { pointer ptrBuffer = core::AllocMemBytes(nSize * sizeof(T)); T* pT = reinterpret_cast(ptrBuffer.get()); for (size_t nIndex = 0; nIndex < nSize; nIndex++) new (pT + nIndex) T(); pointer ptr; ptr.attach(std::move(ptrBuffer)); return ptr; } namespace internal { /** * @brief Create a pointer class using the allocator and allocating the memory with the given size. * @tparam T Type to use for the allocation. * @param[in] pAllocator Pointer to the allocator. Must not be NULL. * @param[in] nSize Size to allocate (in elements). * @return The pointer class. */ template inline pointer make_ptr(internal::IInternalMemAlloc* pAllocator, size_t nSize /*= 1*/) { pointer pointer; if (!pAllocator) throw core::XNoMemMgr(); size_t nTotalSize = nSize * sizeof(T); // Create a new allocation structure using SAllocation = std::remove_pointer_t; pointer.m_psAllocation = new SAllocation; if (!pointer.m_psAllocation) { core::XAllocFailed exception; exception.uiSize = static_cast(sizeof(SAllocation)); throw exception; } pointer.m_psAllocation->pAllocator = pAllocator; // Allocate the amount of elements pointer.m_psAllocation->pData = reinterpret_cast(pAllocator->Alloc(nTotalSize)); if (!pointer.m_psAllocation->pData) { core::XAllocFailed exception; exception.uiSize = static_cast(nTotalSize); throw exception; } // Call the constructors for each memory allocation if constexpr (std::is_scalar_v) std::fill(pointer.m_psAllocation->pData, pointer.m_psAllocation->pData + nSize, static_cast(0)); else { for (size_t nIndex = 0; nIndex < nSize; nIndex++) new (pointer.m_psAllocation->pData + nIndex) T(); } // Fill in allocation details pointer.m_psAllocation->uiSize = static_cast(nSize * sizeof(T)); pointer.m_psAllocation->uiRefCnt = 1; return pointer; } } // namespace internal template */> inline pointer from_stream(std::basic_streambuf* pStreamBuf) { if (!pStreamBuf) throw sdv::XNullPointer(); // Determine the size of the buffer. std::streampos current_pos = pStreamBuf->pubseekoff(0, std::ios::cur, std::ios::in); std::streampos end_pos = pStreamBuf->pubseekoff(0, std::ios::end, std::ios::in); pStreamBuf->pubseekpos(current_pos, std::ios::in); // Allocate the buffer pointer ptr; ptr.resize(end_pos); // Fill the buffer size_t nPos = 0; while (true) { auto nAvail = pStreamBuf->in_avail(); if (nAvail) { if (ptr.size() < nPos + nAvail) ptr.resize(nPos + nAvail); pStreamBuf->sgetn(reinterpret_cast(ptr.get() + nPos), nAvail); nPos += nAvail; } else { auto nVal = pStreamBuf->sbumpc(); if (nVal == TTraits::eof()) break; else { if (ptr.size() < nPos + 1) ptr.resize(nPos + 1); ptr[nPos] = static_cast(nVal); nPos++; } } } // Resize needed? if (nPos != ptr.size()) ptr.resize(nPos); return ptr; } template */> inline void to_stream(const pointer& rptr, std::basic_streambuf* pStreamBuf) { if (!pStreamBuf) throw sdv::XNullPointer(); if (rptr.size()) pStreamBuf->sputn(reinterpret_cast(rptr.get()), rptr.size()); } template inline T* cast(pointer& rptr, size_t nOffset /*= 0*/) { if (nOffset + sizeof(T) > rptr.size()) return nullptr; return reinterpret_cast(rptr.get() + nOffset); } template inline const T* cast(const pointer& rptr, size_t nOffset /*= 0*/) { if (nOffset + sizeof(T) > rptr.size()) return nullptr; return reinterpret_cast(rptr.get() + nOffset); } } // namespace sdv /// @endcond #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #endif // !defined SDV_PTR_INL