Revert "Revert "Update amdgpu-windows-interop with latest changes 20251105 (#…" (#1886)
Reverts ROCm/rocm-systems#1866 (re-landing https://github.com/ROCm/rocm-systems/pull/1728) This broke Windows builds at https://github.com/ROCm/rocm-systems/actions/workflows/therock-ci.yml?query=branch%3Adevelop+event%3Apush, I think intentionally? We need a plan for rolling out such changes without build breaks. Sample logs: https://github.com/ROCm/rocm-systems/actions/runs/19371422209/job/55428130376#step:14:6597 ``` [ocl-clr] [134/153] Building CXX object rocclr\CMakeFiles\rocclr.dir\device\pal\palubercapturemgr.cpp.obj [ocl-clr] FAILED: rocclr/CMakeFiles/rocclr.dir/device/pal/palubercapturemgr.cpp.obj [ocl-clr] ccache "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\cl.exe" /nologo /TP -DATI_OS_WIN -DCL_TARGET_OPENCL_VERSION=220 -DCL_USE_DEPRECATED_OPENCL_1_0_APIS -DCL_USE_DEPRECATED_OPENCL_1_1_APIS -DCL_USE_DEPRECATED_OPENCL_1_2_APIS -DCL_USE_DEPRECATED_OPENCL_2_0_APIS -DCOMGR_DYN_DLL -DGPUOPEN_CLIENT_INTERFACE_MAJOR_VERSION=42 -DHAVE_CL2_HPP -DLITTLEENDIAN_CPU -DOPENCL_C_MAJOR=2 -DOPENCL_C_MINOR=0 -DOPENCL_MAJOR=2 -DOPENCL_MINOR=1 -DPAL_BUILD_RDF=1 -DPAL_CLIENT_INTERFACE_MAJOR_VERSION=932 -DPAL_DEVELOPER_BUILD=0 -DPAL_GPUOPEN_OCL -DPAL_KMT_BUILD=1 -DROCCLR_VERSION_GITHASH=\"38294ab\" -DWITH_PAL_DEVICE -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\.. -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\compiler\lib -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\compiler\lib\include -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\compiler\lib\backends\common -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\device -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\elf -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\include -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\opencl\khronos\headers\opencl2.2\CL -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\opencl\khronos\headers\opencl2.2\CL\.. -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\opencl\khronos\headers\opencl2.2\CL\..\.. -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\opencl\khronos\headers\opencl2.2\CL\..\..\.. -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\opencl\khronos\headers\opencl2.2\CL\..\..\..\.. -IC:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\opencl\khronos\headers\opencl2.2\CL\..\..\..\..\amdocl -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\inc -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\inc\core -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\inc\gpuUtil -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\inc\util -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\shared\inc -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\shared\devdriver\shared\legacy\inc -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\shared\devdriver\third_party\dd_crc32\inc -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\shared\metrohash\src -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\sc\HSAIL\ext\loader -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\hsail-compiler\lib\loaders\elf\utils\libelf\..\..\..\..\..\lib\loaders\elf\utils\common -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\hsail-compiler\lib\loaders\elf\utils\libelf\..\..\..\..\..\lib\loaders\elf\utils\common\win32 -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\hsail-compiler\lib\loaders\elf\utils\libelf\..\..\..\..\..\lib\loaders\elf\utils\libelf -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\sc\HSAIL\ext\libamdhsacode -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\sc\HSAIL\ext\libamdhsacode\..\..\include -IC:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\sc\HSAIL\ext\libamdhsacode\..\..\hsail-tools\libHSAIL -external:IB:\build\compiler\amd-comgr\dist\include -external:W0 /DWIN32 /D_WINDOWS /EHsc /DWIN32 /D_WINDOWS /EHsc /O2 /Ob2 /DNDEBUG -std:c++20 -MD /wd4267 /wd4244 /wd4996 /MT /showIncludes /Forocclr\CMakeFiles\rocclr.dir\device\pal\palubercapturemgr.cpp.obj /Fdrocclr\CMakeFiles\rocclr.dir\rocclr.pdb /FS -c C:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\device\pal\palubercapturemgr.cpp [ocl-clr] cl : Command line warning D9025 : overriding '/MD' with '/MT' [ocl-clr] C:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\device\pal\palubercapturemgr.cpp(152): error C2039: 'RegisterTraceStateChangeCallback': is not a member of 'GpuUtil::TraceSession' [ocl-clr] C:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\inc\gpuUtil\palTraceSession.h(372): note: see declaration of 'GpuUtil::TraceSession' [ocl-clr] C:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\device\pal\palubercapturemgr.cpp(195): error C2039: 'UnregisterTraceStateChangeCallback': is not a member of 'GpuUtil::TraceSession' [ocl-clr] C:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\inc\gpuUtil\palTraceSession.h(372): note: see declaration of 'GpuUtil::TraceSession' [ocl-clr] [135/153] Building CXX object rocclr\CMakeFiles\rocclr.dir\device\pal\paldevicegl.cpp.obj ```
Cette révision appartient à :
@@ -1,455 +1,455 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* 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
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* 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
|
||||
|
||||
Référencer dans un nouveau ticket
Bloquer un utilisateur