Files
rocm-systems/shared/amdgpu-windows-interop/pal/inc/util/palAutoBuffer.h
T
systems-assistant[bot] 321e497048 Update amdgpu-windows-interop with latest changes (#1718)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-05 21:13:32 +01:00

213 строки
9.6 KiB
C++

/*
***********************************************************************************************************************
*
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
**********************************************************************************************************************/
/**
***********************************************************************************************************************
* @file palAutoBuffer.h
* @brief PAL utility collection AutoBuffer class definition.
***********************************************************************************************************************
*/
#pragma once
#include "palSpan.h"
#include "palSysMemory.h"
namespace Util
{
/**
***********************************************************************************************************************
* @brief Safe version of C99's variable-length arrays.
*
* The general idea is that this class encapsulates a variable-length array where we expect the size required to not
* exceed the 'defaultCapacity' template parameter most of the time. In those "normal" cases, this buffer will
* reference a static array of size 'defaultCapacity', but if the constructor's parameter exceeds defaultCapacity, then
* a dynamic array will be allocated from the heap to satisfy the space requirements. The destructor will clean-up any
* dynamic allocation made by the constructor.
*
* This class violates several PAL coding conventions, but for good reason:
*
* - We have overloaded the [] (array-element-accessor) operator to make using this class just like using a regular
* array, which it semantically represents.
* - In order to return array elements by-reference instead of by-value, we need to use C++ references in the
* overloaded operators because this is required by C++.
*
* This class __does not__ clear the contents of the static or dynamic arrays, for performance reasons. If a client
* needs the buffer to be cleared, it must do the memset itself. (However, if 'Item' is a class type rather than
* plain-old-data, the default c'tor will be invoked.)
***********************************************************************************************************************
*/
template<typename Item, size_t defaultCapacity, typename Allocator>
class AutoBuffer
{
public:
/// Constructor.
///
/// The object is initialized to use the static array of items if the required capacity is less than or equal to the
/// default capacity. Otherwise, a larger array is allocated on the heap.
///
/// @param [in] requiredCapacity Number of items actually required (unknown until runtime).
/// @param [in] pAllocator The allocator that will allocate memory if required.
AutoBuffer(
size_t requiredCapacity,
Allocator*const pAllocator)
:
m_capacity(requiredCapacity),
m_pBuffer(reinterpret_cast<Item*>(m_localBuffer)),
m_pAllocator(pAllocator)
{
if (requiredCapacity > defaultCapacity)
{
// Create dynamically allocated array, by allocating memory and constructing its objects.
// On failure, to avoid subtle bugs from misuse, AutoBuffer will be in a zombie state with zero capacity.
m_pBuffer = PAL_NEW_ARRAY(Item, requiredCapacity, pAllocator, AllocInternalTemp);
if (m_pBuffer == nullptr)
{
m_capacity = 0;
}
}
else if (!std::is_trivial<Item>::value)
{
// Explicitly construct all objects of non-trivial type in the local buffer.
for (uint32 idx = 0; idx < m_capacity; ++idx)
{
PAL_PLACEMENT_NEW(m_pBuffer + idx) Item();
}
}
}
/// Destructor.
///
/// Cleans up the dynamically allocated buffer if we allocated one.
~AutoBuffer()
{
if (m_pBuffer != reinterpret_cast<Item*>(m_localBuffer))
{
// Destory dynamically allocated array, by destroying its objects and freeing memory.
PAL_SAFE_DELETE_ARRAY(m_pBuffer, m_pAllocator);
}
else if (!std::is_trivial<Item>::value)
{
// Explicitly destroy all objects of non-trivial type from the local buffer.
for (uint32 idx = 0; idx < m_capacity; ++idx)
{
m_pBuffer[idx].~Item();
}
}
}
/// Getter for the capacity of the buffer.
///
/// Clients can use this function to determine if the constuctor's allocation succeeded.
///
/// @returns Size of the array in bytes. Should match the requiredCapacity parameter passed to the constructor
/// unless a dynamic memory allocation failed.
constexpr size_t Capacity() const noexcept { return m_capacity; }
/// Getter for the size of this buffer, in bytes.
constexpr size_t SizeBytes() const noexcept { return (sizeof(Item) * m_capacity); }
/// Accessor for the nth element of this buffer.
const Item& operator[](size_t n) const
{
PAL_ASSERT(n < m_capacity);
return m_pBuffer[n];
}
/// Non-const accessor for the nth element of this buffer.
Item& operator[](size_t n)
{
PAL_ASSERT(n < m_capacity);
return m_pBuffer[n];
}
///@{
/// Implicitly gets the current contents of the buffer as a Span.
///
/// @returns The contents of the buffer as a Span; same as Span<T>(Data(), Size()).
operator Span<Item>() { return Span<Item>(Data(), Capacity()); }
operator Span<const Item>() const { return Span<const Item>(Data(), Capacity()); }
///@}
/// Returns pointer to the underlying buffer serving as data storage.
/// The returned pointer defines always valid range [Data(), Data() + Capacity()).
///
/// @returns Pointer to the underlying data storage for read & write access.
/// The returned pointer contains address of the first element.
constexpr Item* Data() noexcept { return m_pBuffer; }
/// Returns pointer to the underlying buffer serving as data storage.
/// The returned pointer defines always valid range [Data(), Data() + Capacity()),
/// even if the container is empty (Data() is not dereferenceable in that case).
///
/// @returns Pointer to the underlying data storage for read only access.
/// The returned pointer contains address of the first element.
constexpr const Item* Data() const noexcept { return m_pBuffer; }
///@{
/// @internal Satisfies concept `range_expression`, using Item* as `iterator` and 64-bit size and difference types
///
/// @note - These are a convenience intended to be used by c++ language features such as range-based-for-loops.
using value_type = Item;
using reference = Item&;
using const_reference = const Item&;
using iterator = Item*;
using const_iterator = const Item*;
using difference_type = ptrdiff_t;
using size_type = size_t;
constexpr iterator begin() noexcept { return Data(); }
constexpr iterator end() noexcept { return Data() + Capacity(); }
constexpr const_iterator begin() const noexcept { return Data(); }
constexpr const_iterator end() const noexcept { return Data() + Capacity(); }
constexpr const_iterator cbegin() const noexcept { return Data(); }
constexpr const_iterator cend() const noexcept { return Data() + Capacity(); }
[[nodiscard]] constexpr bool empty() const noexcept { return Capacity() == 0; }
constexpr size_type size() const noexcept { return Capacity(); }
///@}
private:
// This is a POD-type that exactly fits one Item value.
using ValueStorage = typename std::aligned_storage<sizeof(Item), alignof(Item)>::type;
// Capacity of this buffer (in Items).
size_t m_capacity;
// Buffer pointer this object uses to access the buffer's elements: if the required capacity exceeds the default
// capacity, this points to a dynamic array of Items. Otherwise, this points to m_localBuffer.
Item* m_pBuffer;
// Static array providing storage for Items which we expect most objects of this type to end up using.
ValueStorage m_localBuffer[defaultCapacity];
// Allocator for this AutoBuffer.
Allocator*const m_pAllocator;
PAL_DISALLOW_DEFAULT_CTOR(AutoBuffer);
PAL_DISALLOW_COPY_AND_ASSIGN(AutoBuffer);
};
} // Util