/********************************************************************************
 * Copyright (c) 2025-2026 ZF Friedrichshafen AG
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contributors:
 *   Erik Verhoeven - initial API and implementation
 ********************************************************************************/

#ifndef SDV_PTR_INL
#define SDV_PTR_INL

#ifdef min
#undef min
#endif

#ifndef SDV_PTR_H
#include "pointer.h"
#endif //! defined SDV_PTR_H

#include "pointer.h"
#include <iostream>
#include <algorithm>

// 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 <typename T, size_t nFixedSize>
    inline pointer<T, nFixedSize>::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 <typename T, size_t nFixedSize>
    inline pointer<T, nFixedSize>::pointer(const pointer& rptr)
    {
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
    }

    template <typename T, size_t nFixedSize>
    template <size_t nFixedSize2>
    inline pointer<T, nFixedSize>::pointer(const pointer<T, nFixedSize2>& rptr)
    {
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
    }

    template <typename T, size_t nFixedSize>
    inline pointer<T, nFixedSize>::pointer(pointer&& rptr)
    {
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
        rptr.reset();
    }

    template <typename T, size_t nFixedSize>
    template <size_t nFixedSize2>
    inline pointer<T, nFixedSize>::pointer(pointer<T, nFixedSize2>&& rptr)
    {
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
        rptr.reset();
    }

    template <typename T, size_t nFixedSize>
    inline pointer<T, nFixedSize>::~pointer()
    {
        if constexpr (!std::is_scalar_v<T>)
        {
            for (size_t nIndex = 0; nIndex < nFixedSize; nIndex++)
                m_rgtData[nIndex].~T();
        }
    }

    template <typename T, size_t nFixedSize>
    // Member m_uiReserved is not copied. Suppress static code analysis warning.
    // cppcheck-suppress operatorEqVarError
    inline pointer<T, nFixedSize>& pointer<T, nFixedSize>::operator=(const pointer& rptr)
    {
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
        return *this;
    }

    template <typename T, size_t nFixedSize>
    template <size_t nFixedSize2>
    // Member m_uiReserved is not copied. Suppress static code analysis warning.
    // cppcheck-suppress operatorEqVarError
    inline pointer<T, nFixedSize>& pointer<T, nFixedSize>::operator=(const pointer<T, nFixedSize2>& rptr)
    {
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
        return *this;
    }

    template <typename T, size_t nFixedSize>
    // Member m_uiReserved is not copied. Suppress static code analysis warning.
    // cppcheck-suppress operatorEqVarError
    inline pointer<T, nFixedSize>& pointer<T, nFixedSize>::operator=(pointer&& rptr)
    {
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
        rptr.reset();
        return *this;
    }

    template <typename T, size_t nFixedSize>
    template <size_t nFixedSize2>
    // Member m_uiReserved is not copied. Suppress static code analysis warning.
    // cppcheck-suppress operatorEqVarError
    inline pointer<T, nFixedSize>& pointer<T, nFixedSize>::operator=(pointer<T, nFixedSize2>&& rptr)
    {
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
        rptr.reset();
        return *this;
    }

    template <typename T, size_t nFixedSize>
    inline void pointer<T, nFixedSize>::reset()
    {
        resize(0);
    }
    
    template <typename T, size_t nFixedSize>
    template <size_t nFixedSizeRight>
    inline void pointer<T, nFixedSize>::swap(pointer<T, nFixedSizeRight>& 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 <typename T, size_t nFixedSize>
    inline typename pointer<T, nFixedSize>::element_type* pointer<T, nFixedSize>::get() const noexcept
    {
        return const_cast<element_type*>(m_rgtData);
    }

    template <typename T, size_t nFixedSize>
    inline T& pointer<T, nFixedSize>::operator*() const
    {
        return *get();
    }

    template <typename T, size_t nFixedSize>
    inline T* pointer<T, nFixedSize>::operator->() const
    {
        return get();
    }

    template <typename T, size_t nFixedSize>
    inline typename pointer<T, nFixedSize>::element_type& pointer<T, nFixedSize>::operator[](size_t nIndex) const
    {
        if (nIndex >= size())
        {
            XIndexOutOfRange exception;
            exception.uiIndex = static_cast<uint32_t>(nIndex);
            exception.uiSize = static_cast<uint32_t>(size());
            throw exception;
        }
        return get()[nIndex];
    }

    template <typename T, size_t nFixedSize>
    inline pointer<T, nFixedSize>::operator bool() const noexcept
    {
        // Always true
        return true;
    }

    template <typename T, size_t nFixedSize>
    inline size_t pointer<T, nFixedSize>::size() const noexcept
    {
        return m_uiSize;
    }

    template <typename T, size_t nFixedSize>
    inline void pointer<T, nFixedSize>::resize(size_t nSize)
    {
        // Replace all deleted elements with the default constructed elements
        for (size_t nIndex = nSize; nIndex < std::min(nFixedSize, static_cast<size_t>(m_uiSize)); nIndex++)
        {
            // Destruct the existing and construct the new
            if constexpr (!std::is_scalar_v<T>)
            {
                m_rgtData[nIndex].~T();
                new (m_rgtData + nIndex) T();
            }
            else
                if (nIndex < nFixedSize)
                    m_rgtData[nIndex] = static_cast<T>(0);

        }

        if (nSize > nFixedSize)
        {
            XBufferTooSmall exception;
            exception.uiSize = static_cast<uint64_t>(nSize);
            exception.uiCapacity = static_cast<uint64_t>(nFixedSize);
            throw exception;
        }

        m_uiSize = static_cast<uint32_t>(nSize);
    }

    template <typename T, size_t nFixedSize>
    inline size_t pointer<T, nFixedSize>::capacity() const noexcept
    {
        return nFixedSize;
    }

    template <typename T>
    inline pointer<T, 0>::pointer() noexcept
    {
        // The size of the pointer class should not be larger than the size of a pointer.
        static_assert(sizeof(pointer<T>) == 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<uint8_t> 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 <typename T>
    inline pointer<T, 0>::pointer(const pointer& rptr)
    {
        m_psAllocation = rptr.m_psAllocation;
        if (m_psAllocation)
            m_psAllocation->uiRefCnt++;
    }

    template <typename T>
    template <size_t nFixedSize2>
    inline pointer<T, 0>::pointer(const pointer<T, nFixedSize2>& rptr)
    {
        // Fixed sized buffer; copy the content
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
    }

    template <typename T>
    inline pointer<T, 0>::pointer(pointer&& rptr) noexcept
    {
        m_psAllocation = rptr.m_psAllocation;
        rptr.m_psAllocation = nullptr;
    }

    template <typename T>
    template <size_t nFixedSize2>
    inline pointer<T, 0>::pointer(const pointer<T, nFixedSize2>&& 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 <typename T>
    inline pointer<T, 0>::~pointer()
    {
        try
        {
            reset();
        }
        catch (const sdv::XSysExcept&)
        {
        }
        catch (const std::exception&)
        {
        }
    }

    template <typename T>
    inline pointer<T>& pointer<T, 0>::operator=(const pointer& rptr)
    {
        // Dynamic buffer, share the allocation
        reset();
        m_psAllocation = rptr.m_psAllocation;
        if (m_psAllocation)
            m_psAllocation->uiRefCnt++;
        return *this;
    }

    template <typename T>
    template <size_t nFixedSize2>
    inline pointer<T>& pointer<T, 0>::operator=(const pointer<T, nFixedSize2>& rptr)
    {
        // Fixed sized buffer; copy the content
        resize(rptr.size());
        std::copy_n(rptr.get(), rptr.size(), get());
        return *this;
    }

    template <typename T>
    inline pointer<T>& pointer<T, 0>::operator=(pointer<T>&& 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 <typename T>
    template <size_t nFixedSize2>
    inline pointer<T>& pointer<T, 0>::operator=(pointer<T, nFixedSize2>&& 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 <typename T>
    inline void pointer<T, 0>::attach(pointer<uint8_t>&& rptrBuffer)
    {
        // Move buffer pointer (structures are identical).
        operator=(std::move(*reinterpret_cast<pointer<T>*>(&rptrBuffer)));
    }

    template <typename T>
    inline pointer<uint8_t>&& pointer<T, 0>::detach()
    {
        // Move this pointer (structures are identical).
        return std::move(*reinterpret_cast<pointer<uint8_t>*>(this));
    }

    template <typename T>
    inline void pointer<T, 0>::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<T>)
            {
                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 <typename T>
    template <size_t nFixedSizeRight>
    inline void pointer<T, 0>::swap(pointer<T, nFixedSizeRight>& 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 <typename T>
    inline typename pointer<T, 0>::element_type* pointer<T, 0>::get() const noexcept
    {
        return m_psAllocation ? m_psAllocation->pData : nullptr;
    }

    template <typename T>
    inline T& pointer<T, 0>::operator*() const
    {
        if (!m_psAllocation) throw XNullPointer();
        return *get();
    }

    template <typename T>
    inline T* pointer<T, 0>::operator->() const
    {
        if (!m_psAllocation) throw XNullPointer();
        return get();
    }

    template <typename T>
    inline typename pointer<T, 0>::element_type& pointer<T, 0>::operator[](size_t nIndex) const
    {
        if (!m_psAllocation) throw XNullPointer();
        if (nIndex >= size())
        {
            XIndexOutOfRange exception;
            exception.uiIndex = static_cast<uint32_t>(nIndex);
            exception.uiSize = static_cast<uint32_t>(size());
            throw exception;
        }
        return get()[nIndex];
    }

    template <typename T>
    inline pointer<T, 0>::operator bool() const noexcept
    {
        return m_psAllocation && m_psAllocation->pData;
    }
    
    template <typename T>
    inline size_t pointer<T, 0>::size() const noexcept
    {
        return m_psAllocation ? m_psAllocation->uiSize / sizeof(T) : 0;
    }

    template <typename T>
    inline void pointer<T, 0>::resize(size_t nSize)
    {
        if (!m_psAllocation || !m_psAllocation->pAllocator || !m_psAllocation->pData)
        {
            operator=(std::move(make_ptr<T>(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<T>)
            {
                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<uint32_t>(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<T>)
        {
            ptAllocation = reinterpret_cast<T*>(m_psAllocation->pAllocator->Realloc(m_psAllocation->pData, nTotalSize));
            if (nTotalSize && !ptAllocation)
            {
                core::XAllocFailed exception;
                exception.uiSize = static_cast<uint32_t>(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<T>(0);
        }
        else
        {
            // Allocate new size
            ptAllocation = reinterpret_cast<T*>(m_psAllocation->pAllocator->Alloc(nTotalSize));
            if (nTotalSize && !ptAllocation)
            {
                core::XAllocFailed exception;
                exception.uiSize = static_cast<uint32_t>(nTotalSize);
                throw exception;
            }

            // Move the elements from the existing allocation to the new allocation.
            for (size_t nIndex = 0; nIndex < std::min(static_cast<size_t>(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<uint32_t>(nSize * sizeof(T));
    }

    template <typename T>
    inline size_t pointer<T, 0>::capacity() const noexcept
    {
        return m_psAllocation ? m_psAllocation->uiSize / sizeof(T) : 0;
    }

    template <typename T>
    inline size_t pointer<T, 0>::ref_count() const noexcept
    {
        return m_psAllocation ? static_cast<size_t>(m_psAllocation->uiRefCnt) : 0;
    }

    template <class T, size_t nFixedSizeLeft, size_t nFixedSizeRight>
    inline void swap(pointer<T, nFixedSizeLeft>& rptrLeft, pointer<T, nFixedSizeRight>& rptrRight)
    {
        rptrLeft.swap(rptrRight);
    }

    template <class T, size_t nFixedSizeLeft, size_t nFixedSizeRight>
    inline bool operator==(const pointer<T, nFixedSizeLeft>& rptrLeft, const pointer<T, nFixedSizeRight>& 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 <class T, size_t nFixedSizeLeft, size_t nFixedSizeRight>
    inline bool operator!=(const pointer<T, nFixedSizeLeft>& rptrLeft, const pointer<T, nFixedSizeRight>& rptrRight) noexcept
    {
        return !operator==(rptrLeft, rptrRight);
    }

    template <class T, size_t nFixedSizeLeft, size_t nFixedSizeRight>
    inline bool operator<(const pointer<T, nFixedSizeLeft>& rptrLeft, const pointer<T, nFixedSizeRight>& 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 <class T, size_t nFixedSizeLeft, size_t nFixedSizeRight>
    inline bool operator<=(const pointer<T, nFixedSizeLeft>& rptrLeft, const pointer<T, nFixedSizeRight>& 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 <class T, size_t nFixedSizeLeft, size_t nFixedSizeRight>
    inline bool operator>(const pointer<T, nFixedSizeLeft>& rptrLeft, const pointer<T, nFixedSizeRight>& 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 <class T, size_t nFixedSizeLeft, size_t nFixedSizeRight>
    inline bool operator>=(const pointer<T, nFixedSizeLeft>& rptrLeft, const pointer<T, nFixedSizeRight>& 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 <class T, size_t nFixedSizeLeft>
    inline bool operator==(const pointer<T, nFixedSizeLeft>& rptrLeft, std::nullptr_t) noexcept
    {
        return rptrLeft.get() == nullptr;
    }

    template <class T, size_t nFixedSizeRight>
    inline bool operator==(std::nullptr_t, const pointer<T, nFixedSizeRight>& rptrRight) noexcept
    {
        return nullptr == rptrRight.get();
    }

    template <class T, size_t nFixedSizeLeft>
    inline bool operator!=(const pointer<T, nFixedSizeLeft>& rptrLeft, std::nullptr_t) noexcept
    {
        return rptrLeft.get() != nullptr;
    }

    template <class T, size_t nFixedSizeRight>
    inline bool operator!=(std::nullptr_t, const pointer<T, nFixedSizeRight>& rptrRight) noexcept
    {
        return nullptr != rptrRight.get();
    }

    template <class T, size_t nFixedSizeLeft>
    inline bool operator<(const pointer<T, nFixedSizeLeft>& /*rptrLeft*/, std::nullptr_t) noexcept
    {
        return false;
    }

    template <class T, size_t nFixedSizeRight>
    inline bool operator<(std::nullptr_t, const pointer<T, nFixedSizeRight>& rptrRight) noexcept
    {
        return nullptr != rptrRight.get();
    }

    template <class T, size_t nFixedSizeLeft>
    inline bool operator<=(const pointer<T, nFixedSizeLeft>& rptrLeft, std::nullptr_t) noexcept
    {
        return rptrLeft.get() == nullptr;
    }

    template <class T, size_t nFixedSizeRight>
    inline bool operator<=(std::nullptr_t, const pointer<T, nFixedSizeRight>& /*rptrRight*/) noexcept
    {
        return true;
    }

    template <class T, size_t nFixedSizeLeft>
    inline bool operator>(const pointer<T, nFixedSizeLeft>& rptrLeft, std::nullptr_t) noexcept
    {
        return rptrLeft.get() != nullptr;
    }

    template <class T, size_t nFixedSizeRight>
    inline bool operator>(std::nullptr_t, const pointer<T, nFixedSizeRight>& /*rptrRight*/) noexcept
    {
        return false;
    }

    template <class T, size_t nFixedSizeLeft>
    inline bool operator>=(const pointer<T, nFixedSizeLeft>& /*rptrLeft*/, std::nullptr_t) noexcept
    {
        return true;
    }

    template <class T, size_t nFixedSizeRight>
    inline bool operator>=(std::nullptr_t, const pointer<T, nFixedSizeRight>& rptrRight) noexcept
    {
        return nullptr == rptrRight.get();
    }

    template <class T, class U, class V, size_t nFixedSize>
    inline std::basic_ostream<U, V>& operator<<(std::basic_ostream<U, V>& rstream, const pointer<T, nFixedSize>& rptr)
    {
        for (size_t nIndex = 0; nIndex < rptr.size(); nIndex++)
            rstream << rptr[nIndex];
        return rstream;
    }

    template <typename T>
    inline pointer<T> make_ptr(size_t nSize /*= 1*/)
    {
        pointer<uint8_t> ptrBuffer = core::AllocMemBytes(nSize * sizeof(T));
        T* pT = reinterpret_cast<T*>(ptrBuffer.get());
        for (size_t nIndex = 0; nIndex < nSize; nIndex++)
            new (pT + nIndex) T();
        pointer<T> 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 <typename T>
        inline pointer<T> make_ptr(internal::IInternalMemAlloc* pAllocator, size_t nSize /*= 1*/)
        {
            pointer<T> pointer;
            if (!pAllocator) throw core::XNoMemMgr();
            size_t nTotalSize  = nSize * sizeof(T);

            // Create a new allocation structure
            using SAllocation = std::remove_pointer_t<decltype(pointer.m_psAllocation)>;
            pointer.m_psAllocation = new SAllocation;
            if (!pointer.m_psAllocation)
            {
                core::XAllocFailed exception;
                exception.uiSize = static_cast<uint32_t>(sizeof(SAllocation));
                throw exception;
            }
            pointer.m_psAllocation->pAllocator = pAllocator;

            // Allocate the amount of elements
            pointer.m_psAllocation->pData = reinterpret_cast<T*>(pAllocator->Alloc(nTotalSize));
            if (!pointer.m_psAllocation->pData)
            {
                core::XAllocFailed exception;
                exception.uiSize = static_cast<uint32_t>(nTotalSize);
                throw exception;
            }

            // Call the constructors for each memory allocation
            if constexpr (std::is_scalar_v<T>)
                std::fill(pointer.m_psAllocation->pData, pointer.m_psAllocation->pData + nSize, static_cast<T>(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<uint32_t>(nSize * sizeof(T));
            pointer.m_psAllocation->uiRefCnt = 1;
            return pointer;
        }
    } // namespace internal

    template <typename TTraits /*= std::char_traits<char>*/>
    inline pointer<uint8_t> from_stream(std::basic_streambuf<char, TTraits>* 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<uint8_t, 0> 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<char*>(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<uint8_t>(nVal);
                    nPos++;
                }
            }
        }

        // Resize needed?
        if (nPos != ptr.size())
            ptr.resize(nPos);

        return ptr;
    }

    template <typename TTraits /*= std::char_traits<char>*/>
    inline void to_stream(const pointer<uint8_t>& rptr, std::basic_streambuf<char, TTraits>* pStreamBuf)
    {
        if (!pStreamBuf) throw sdv::XNullPointer();
        if (rptr.size())
            pStreamBuf->sputn(reinterpret_cast<const char*>(rptr.get()), rptr.size());
    }

    template <typename T>
    inline T* cast(pointer<uint8_t>& rptr, size_t nOffset /*= 0*/)
    {
        if (nOffset + sizeof(T) > rptr.size()) return nullptr;
        return reinterpret_cast<T*>(rptr.get() + nOffset);
    }

    template <typename T>
    inline const T* cast(const pointer<uint8_t>& rptr, size_t nOffset /*= 0*/)
    {
        if (nOffset + sizeof(T) > rptr.size()) return nullptr;
        return reinterpret_cast<const T*>(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