omni/extras/ScratchBuffer.h

File members: omni/extras/ScratchBuffer.h

// Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#pragma once

#include "../../carb/Defines.h"

#include <algorithm>
#include <iterator>

namespace omni
{
namespace extras
{

#ifdef DOXYGEN_BUILD // Sphinx does not like the computation and reports errors, so special case for documentation.
template <typename T, size_t BaseSize_ = 16, size_t ShrinkThreshold_ = 100>
#else
template <typename T, size_t BaseSize_ = CARB_MAX(512ull / sizeof(T), 16ull), size_t ShrinkThreshold_ = 100>
#endif
class ScratchBuffer
{
public:
    using DataType = T;

    static constexpr size_t BaseSize = BaseSize_;

    ScratchBuffer()
    {
        m_dynamicArray = m_localArray;
        m_size = BaseSize;
        m_capacity = BaseSize;
    }

    ScratchBuffer(const ScratchBuffer& right) : ScratchBuffer()
    {
        *this = right;
    }

    ScratchBuffer(ScratchBuffer&& right) : ScratchBuffer()
    {
        *this = std::move(right);
    }

    ~ScratchBuffer()
    {
        if (m_dynamicArray != nullptr && m_dynamicArray != m_localArray)
            delete[] m_dynamicArray;
    }

    ScratchBuffer& operator=(const ScratchBuffer& right)
    {
        if (&right == this)
            return *this;

        if (!resize(right.m_size))
            return *this;

        std::copy_n(right.m_dynamicArray, m_size, m_dynamicArray);

        return *this;
    }

    ScratchBuffer& operator=(ScratchBuffer&& right)
    {
        if (&right == this)
            return *this;

        if (!resize(right.m_size))
            return *this;

        std::copy_n(std::make_move_iterator(right.m_dynamicArray), m_size, m_dynamicArray);

        return *this;
    }

    T& operator[](size_t index)
    {
        return m_dynamicArray[index];
    }

    T* data()
    {
        return m_dynamicArray;
    }

    const T* data() const
    {
        return m_dynamicArray;
    }

    size_t size() const
    {
        return m_size;
    }

    bool resize(size_t count)
    {
        T* tmp = m_localArray;
        size_t copyCount;

        if (count == 0)
            return true;

        // the buffer is already the requested size -> nothing to do => succeed.
        if (count == m_size)
            return true;

        // the buffer is already large enough => don't resize unless the change is drastic.
        if (count <= m_capacity)
        {
            if ((count * 100) >= ShrinkThreshold_ * m_size)
            {
                m_size = count;
                return true;
            }
        }

        if (count > BaseSize)
        {
            tmp = new (std::nothrow) T[count];

            if (tmp == nullptr)
                return false;
        }

        // the buffer didn't change -> nothing to copy => update parameters and succeed.
        if (tmp == m_dynamicArray)
        {
            m_size = count;
            m_capacity = BaseSize;
            return true;
        }

        copyCount = m_size;

        if (m_size > count)
            copyCount = count;

        std::copy_n(std::make_move_iterator(m_dynamicArray), copyCount, tmp);

        if (m_dynamicArray != nullptr && m_dynamicArray != m_localArray)
            delete[] m_dynamicArray;

        m_dynamicArray = tmp;
        m_size = count;
        m_capacity = count;
        return true;
    }

private:
    T m_localArray[BaseSize];

    T* m_dynamicArray;

    size_t m_size = BaseSize;

    size_t m_capacity = BaseSize;
};

} // namespace extras
} // namespace omni