Dosyalar
rocm-systems/shared/amdgpu-windows-interop/pal/inc/util/palVector.h
T

.git-blame-ignore-revs dosyasındaki sürümler yok sayılıyor. Bunun yerine normal sorumlu görüntüsü için buraya tıklayın.

456 satır
19 KiB
C++
Ham Normal Görünüm Geçmiş

/*
***********************************************************************************************************************
*
* Copyright (c) 2015-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 palVector.h
* @brief PAL utility collection Vector and VectorIterator class declarations.
***********************************************************************************************************************
*/
#pragma once
#include "palUtil.h"
#include "palAssert.h"
#include "palSpan.h"
#include "palSysMemory.h"
#include <type_traits>
namespace Util
{
// Forward declarations.
template<typename T, uint32 defaultCapacity, typename Allocator> class Vector;
/**
***********************************************************************************************************************
* @brief Iterator for traversal of elements in Vector.
*
* Supports forward traversal.
***********************************************************************************************************************
*/
template<typename T, uint32 defaultCapacity, typename Allocator>
class VectorIterator
{
public:
/// Checks if the current index is within bounds of the number of elements in the vector.
///
/// @returns True if the current element this iterator is pointing to is within the permitted range.
bool IsValid() const { return (m_curIndex < m_srcVector.m_numElements); }
/// Returns the element the iterator is currently pointing to as a reference.
///
/// @warning This may cause an access violation if the iterator is not valid.
///
/// @returns The element the iterator is currently pointing to.
T& Get() const
{
PAL_ASSERT(IsValid());
return (*(m_srcVector.m_pData + m_curIndex));
}
/// Advances the iterator to point to the next element.
///
/// @warning Does not do bounds checking.
void Next() { ++m_curIndex; }
/// Retrieves the current vector position of this iterator.
///
/// @returns The location in the vector of the element the iterator is currently pointing to.
uint32 Position() const { return m_curIndex; }
private:
VectorIterator(uint32 index, const Vector<T, defaultCapacity, Allocator>& srcVec);
uint32 m_curIndex; // The current index of the vector iterator.
const Vector<T, defaultCapacity, Allocator>& m_srcVector; // The vector container this iterator is used for.
PAL_DISALLOW_DEFAULT_CTOR(VectorIterator);
// Although this is a transgression of coding standards, it means that Vector does not need to have a public
// interface specifically to implement this class. The added encapsulation this provides is worthwhile.
friend class Vector<T, defaultCapacity, Allocator>;
};
/**
***********************************************************************************************************************
* @brief Vector container.
*
* Vector is a templated array based storage that starts with a default-size allocation in the stack. If more space is
* needed it then resorts to dynamic allocation by doubling the size every time the capacity is exceeded.
* Operations which this class supports are:
*
* - Insertion at the end of the array.
* - Forward iteration.
* - Random access.
*
* @warning This class is not thread-safe.
***********************************************************************************************************************
*/
template<typename T, uint32 defaultCapacity, typename Allocator>
class Vector
{
public:
/// A convenient shorthand for VectorIterator.
typedef VectorIterator<T, defaultCapacity, Allocator> Iter;
/// When this allocates, it doubles the old size of memory
static constexpr uint32 GrowthFactor = 2;
/// Constructor.
///
/// @param [in] pAllocator The allocator that will allocate memory if required.
Vector(Allocator*const pAllocator);
/// Destructor.
~Vector();
/// Move constructor.
///
/// @param [in] vector Reference to a dying vector, from which resources will be stolen.
Vector(Vector&& vector);
/// Increases maximal capacity to value greater or equal to the newCapacity.
/// If newCapacity is greater than the maximal capacity, new storage is allocated,
/// otherwise the method does nothing.
///
/// @note All existing iterators will not get invalidated, even in case new storage is allocated,
/// because iterators are referencing vector, rather than elements of that vector.
///
/// @warning All pointers and references to elements of a vector will be invalidated,
/// in case new storage is allocated.
///
/// @param [in] newCapacity The new capacity of a vector, which is lower limit of the maximal capacity.
///
/// @returns Result ErrorOutOfMemory if the operation failed.
Result Reserve(uint32 newCapacity);
/// Increases maximum capacity to the number of elements in the vector, plus the specified increment amount.
/// Equivalent to this->Reserve(this->NumElements() + amount);
///
/// @param [in] amount Number of items beyond the current element count to increas the capacity to.
///
/// @returns Result ErrorOutOfMemory if the operation failed.
Result Grow(uint32 amount) { return Reserve(NumElements() + amount); }
/// Set size to newSize.
/// If size is decreased, elements at the end of the vector will be removed.
/// If size is increased, new elements will be set to newVal.
/// If newSize requires a higher capacity, a new allocation is made. See notes on Reserve.
///
/// @note If size is decreased, any iterators referencing removed elements will become invalid. All other
/// iterators will remain valid. Otherwise, all iterators will still be valid.
///
/// @warning All pointers and references to elements of a vector will be invalidated,
/// in case new storage is allocated.
///
/// @param [in] newSize The new size of a vector.
///
/// @returns Result ErrorOutOfMemory if the operation failed.
Result Resize(uint32 newSize, const T& newVal = T());
/// Copy/Move an element to end of the vector. If not enough space is available, new space will be allocated and
/// the old data will be copied to the new space.
///
/// @param [in] data The element to be pushed to the vector. The element will become the last element.
///
/// @returns Result ErrorOutOfMemory if the operation failed.
Result PushBack(const T& data);
Result PushBack(T&& data);
/// Constructs an object in-place at the end of the vector. If not enough space is available, new space will be
/// allocated and the old data will be copied to the new space.
///
/// @param [in] args... The arguments passed to the constructor
///
/// @returns Result ErrorOutOfMemory if the operation failed.
template <typename... Args>
Result EmplaceBack(Args&&... args);
/// Returns the element at the end of the vector and destroys it.
///
/// @param [out] pData The element at the end of the vector.
/// It is expected that pData is uninitialized as it will be overwritten and not destructed.
void PopBack(T* pData);
/// Destroys all elements stored in the vector. All dynamically allocated memory will be saved for reuse.
void Clear();
///@{
/// Returns the element at the location specified.
///
/// @warning Calling this function with an out-of-bounds index will cause an access violation!
///
/// @param [in] index Integer location of the element needed.
///
/// @returns The element at location specified by index by reference
T& At(uint32 index)
{
PAL_ASSERT(index < m_numElements);
return *(m_pData + index);
}
const T& At(uint32 index) const
{
PAL_ASSERT(index < m_numElements);
return *(m_pData + index);
}
T& operator[](uint32 index) noexcept { return At(index); }
const T& operator[](uint32 index) const noexcept { return At(index); }
///@}
/// Returns the data at the front of the vector.
///
/// @warning Calling this function on an empty vector will cause an access violation!
///
/// @returns The data at the front of the vector.
T& Front() const
{
PAL_ASSERT(IsEmpty() == false);
return *m_pData;
}
/// Returns the data at the back of the vector.
///
/// @warning Calling this function on an empty vector will cause an access violation!
///
/// @returns The data at the back of the vector.
T& Back() const
{
PAL_ASSERT(IsEmpty() == false);
return *(m_pData + (m_numElements - 1));
}
/// Returns an iterator to the first element of the vector.
///
/// @warning Accessing an element using an iterator of an empty vector will cause an access violation!
///
/// @returns An iterator to first element of the vector.
Iter Begin() const { return Iter(0, *this); }
/// Returns an iterator to the last element of the vector.
///
/// @warning Accessing an element using an iterator of an empty vector will cause an access violation!
///
/// @returns VectorIterator An iterator to last element of the vector.
Iter End() const { return Iter((m_numElements - 1), *this); }
///@{
/// Implicitly gets the current contents of the vector as a Span.
///
/// @returns The contents of the vector as a Span; same as Span<T>(Data(), NumElements()).
operator Span<T>() { return Span<T>(Data(), NumElements()); }
operator Span<const T>() const { return Span<const T>(Data(), NumElements()); }
///@}
/// Returns pointer to the underlying buffer serving as data storage.
/// The returned pointer defines always valid range [Data(), Data() + NumElements()),
/// even if the container is empty (Data() is not dereferenceable in that case).
///
/// @warning Dereferencing pointer returned by Data() from an empty vector will cause an access violation!
///
/// @returns Pointer to the underlying data storage for read & write access.
/// For a non-empty vector, the returned pointer contains address of the first element.
/// For an empty vector, the returned pointer may or may not be a null pointer.
T* Data() { return m_pData; }
/// Returns pointer to the underlying buffer serving as data storage.
/// The returned pointer defines always valid range [Data(), Data() + NumElements()),
/// even if the container is empty (Data() is not dereferenceable in that case).
///
/// @warning Dereferencing pointer returned by Data() from an empty vector will cause an access violation!
///
/// @returns Pointer to the underlying data storage for read only access.
/// For a non-empty vector, the returned pointer contains address of the first element.
/// For an empty vector, the returned pointer may or may not be a null pointer.
const T* Data() const { return m_pData; }
/// Returns the size of the vector.
///
/// @returns An unsigned integer equal to the number of elements currently present in the vector.
uint32 NumElements() const { return m_numElements; }
/// Returns true if the number of elements present in the vector is equal to zero.
///
/// @returns True if the vector is empty.
bool IsEmpty() const { return (m_numElements == 0); }
/// Returns a pointer to the allocator used for this container's memory management.
///
/// @returns Allocator pointer.
Allocator* GetAllocator() const { return m_pAllocator; }
///@{
/// @internal Satisfies concept `range_expression`, using T* as `iterator` and 32-bit size and difference types
///
/// @note - These are a convenience intended to be used by c++ language features such as `range for`.
/// These should not be called directly as they do not adhere to PAL coding standards.
using value_type = T;
using reference = T&;
using const_reference = const T&;
using iterator = T*;
using const_iterator = const T*;
using difference_type = int32;
using size_type = uint32;
iterator begin() noexcept { return m_pData; }
iterator end() noexcept { return (m_pData + m_numElements); }
const_iterator begin() const noexcept { return m_pData; }
const_iterator end() const noexcept { return (m_pData + m_numElements); }
const_iterator cbegin() const noexcept { return m_pData; }
const_iterator cend() const noexcept { return (m_pData + m_numElements); }
[[nodiscard]] bool empty() const noexcept { return IsEmpty(); }
size_type size() const noexcept { return m_numElements; }
///@}
/// Erases the element at the specified iterator.
void Erase(Iter it);
/// Erases the element at the specified iterator.
void Erase(iterator it);
/// Erases the element at the specified index.
void Erase(uint32 index);
/// Erase the element at the specified iterator, and swap last element to that position.
/// If the element to erase is the last element, erase directly and no swap operation.
void EraseAndSwapLast(Iter it);
/// Erase the element at the specified iterator, and swap last element to that position.
/// If the element to erase is the last element, erase directly and no swap operation.
void EraseAndSwapLast(iterator it);
/// Erases the element at the specified index, and swap last element to that position.
/// If the element to erase is the last element, erase directly and no swap operation.
void EraseAndSwapLast(uint32 index);
private:
// This is a POD-type that exactly fits one T value.
typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type ValueStorage;
ValueStorage m_data[defaultCapacity]; // The initial data buffer stored within the vector object.
T* m_pData; // Pointer to the current data buffer.
uint32 m_numElements; // Number of elements present.
uint32 m_maxCapacity; // Maximum size it can hold.
Allocator*const m_pAllocator; // Allocator for this Vector.
PAL_DISALLOW_COPY_AND_ASSIGN(Vector);
// Although this is a transgression of coding standards, it prevents VectorIterator requiring a public constructor;
// constructing a 'bare' VectorIterator (i.e. without calling Vector::GetIterator) can never be a legal operation,
// so this means that these two classes are much safer to use.
friend class VectorIterator<T, defaultCapacity, Allocator>;
};
// =====================================================================================================================
template<typename T, uint32 defaultCapacity, typename Allocator>
VectorIterator<T, defaultCapacity, Allocator>::VectorIterator(
uint32 index,
const Vector<T, defaultCapacity, Allocator>& srcVec)
:
m_curIndex(index),
m_srcVector(srcVec)
{
}
// =====================================================================================================================
template<typename T, uint32 defaultCapacity, typename Allocator>
Vector<T, defaultCapacity, Allocator>::Vector(
Allocator*const pAllocator)
:
m_pData(reinterpret_cast<T*>(m_data)),
m_numElements(0),
m_maxCapacity(defaultCapacity),
m_pAllocator(pAllocator)
{
}
// =====================================================================================================================
template<typename T, uint32 defaultCapacity, typename Allocator>
Vector<T, defaultCapacity, Allocator>::~Vector()
{
// Explicitly destroy all non-trivial types.
if (!std::is_trivial<T>::value)
{
for (uint32 idx = 0; idx < m_numElements; ++idx)
{
m_pData[idx].~T();
}
}
// Check if we have dynamically allocated memory.
if (m_pData != reinterpret_cast<T*>(m_data))
{
// Free the memory that was allocated dynamically.
PAL_FREE(m_pData, m_pAllocator);
}
}
// =====================================================================================================================
// Steals allocation from a dying vector, if data buffer uses storage from heap allocation.
// Moves objects between local buffers of new and dying vectors (for non-trivial types) or
// copies local buffer from a dying vector to a new vector (for trivial types),
// if data buffer uses storage from local buffer.
template<typename T, uint32 defaultCapacity, typename Allocator>
Vector<T, defaultCapacity, Allocator>::Vector(
Vector&& vector)
:
m_numElements(vector.m_numElements),
m_maxCapacity(vector.m_maxCapacity),
m_pAllocator(vector.m_pAllocator)
{
if (vector.m_pData == reinterpret_cast<T*>(vector.m_data)) // Local buffer
{
// Data buffer will be using storage from local buffer.
m_pData = reinterpret_cast<T*>(m_data);
if (std::is_trivial<T>::value)
{
// Optimize trivial types by copying local buffer.
std::memcpy(m_pData, vector.m_pData, sizeof(T) * m_numElements);
}
else
{
// Move objects from local buffer of a dying vector to local buffer of a new vector.
for (uint32 idx = 0; idx < m_numElements; ++idx)
{
PAL_PLACEMENT_NEW(m_pData + idx) T(Move(vector.m_pData[idx]));
}
}
}
else // Heap allocation
{
// Steal heap allocation from dying vector.
m_pData = vector.m_pData;
// After the allocation has been stolen, dying vector is just an empty shell.
vector.m_pData = nullptr;
vector.m_numElements = 0;
vector.m_maxCapacity = 0;
}
}
} // Util