/* *********************************************************************************************************************** * * 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 palDeque.h * @brief PAL utility collection Deque and DequeIterator class declarations. *********************************************************************************************************************** */ #pragma once #include "palAssert.h" #include "palSysMemory.h" namespace Util { // Forward declarations. template class Deque; /// @internal Private structure used by Deque and its iterators to store chunks of data elements. struct DequeBlockHeader { DequeBlockHeader* pPrev; ///< Pointer to the previous block. DequeBlockHeader* pNext; ///< Pointer to the next block. void* pStart; ///< Pointer to the first element in this block. void* pEnd; ///< Pointer to the last element in this block. }; /** *********************************************************************************************************************** * @brief Iterator for traversal of elements in a Deque collection. * * Allows traversal of all elements in a Deque going either forwards or backwards. If you traverse off either end of * the deque, then you must create a new iterator by calling either the Deque's Begin() or End() method. *********************************************************************************************************************** */ template class DequeIterator { public: /// Trivial destructor. ~DequeIterator() { } /// Returns a pointer to the current element. Will return null if we've gone past the end. T* Get() const { return m_pCurrent; } /// Advances the iterator to the next position (move forward). void Next(); /// Advances the iterator to the previous position (move backward). void Prev(); /// Check if the element the iterator references is valid. bool IsValid() const { return m_pCurrent != nullptr; } private: DequeIterator(const Deque* pDeque, DequeBlockHeader* pHeader, T* pCurrent); const Deque*const m_pDeque; // The Deque we're iterating over. const DequeBlockHeader* m_pCurrentHeader; // The block we're iterating over. T* m_pCurrent; // Pointer to the current element. Null if we've gone past the // end. PAL_DISALLOW_DEFAULT_CTOR(DequeIterator); // Although this is a transgression of coding standards, it means that Deque does not need to have a public // interface specifically to implement this class. The added encapsulation this provides is worthwhile. friend class Deque; }; /** *********************************************************************************************************************** * @brief Simple templated deque container - a double-ended queue. * * This is meant for storing elements of an arbitrary (but uniform) type. Operations which this class supports are: * * - Insertion from the front and back. * - Deletion from the front and back. * - Forwards and reverse iteration * * @warning This class is not thread-safe for push, pop, or iteration! * * @note This class is only designed to work with native types and POD-style structures. If it is needed to have a Deque * of complex objects with nontrivial destructors, copy constructors or assign operators, then a specialized * implementation of CleanupElement() will need to be explicitly defined. *********************************************************************************************************************** */ template class Deque { public: /// Constructor. /// /// @param [in] pAllocator The allocator that will allocate memory if required. Deque(Allocator*const pAllocator, size_t numElementsPerBlock = 256); ~Deque(); /// Returns the number of elements in the deque. size_t NumElements() const { return m_numElements; } /// Returns an iterator pointing to the first element in the deque. /// /// @returns An iterator pointing at the front end of the deque. DequeIterator Begin() const { return DequeIterator(this, m_pFrontHeader, m_pFront); } /// Returns an iterator pointing to the last element in the deque. /// /// This is somewhat different from std::deque.End() which returns a pointer to the theoretical object _past_ the /// end of the deque. /// /// @returns An iterator pointing at the back end of the deque. DequeIterator End() const { return DequeIterator(this, m_pBackHeader, m_pBack); } ///@{ /// 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); const T& At(uint32 index) const; T& operator[](uint32 index); const T& operator[](uint32 index) const; ///@} /// Returns the object at the front of the deque. /// /// @warning This will cause an access violation if called on an empty deque! /// /// @returns Reference to the item stored at the front end of the deque. T& Front() const { PAL_ASSERT(m_numElements != 0); return *m_pFront; } /// Returns the object at the tail of the deque. /// /// @warning This will cause an access violation if called on an empty deque! /// /// @returns Reference to the item stored at the back end of the deque. T& Back() const { PAL_ASSERT(m_numElements != 0); return *m_pBack; } /// Pushes a copy of the specified item onto the front of the deque. /// /// @param [in] data Item to be added to the front of the deque. /// /// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation /// failed because of an internal failure to allocate system memory. Result PushFront(const T& data); /// Emplaces a newly constructed item onto the front of the deque. /// /// @param [in] args arguments used to construct the new item. /// /// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation /// failed because of an internal failure to allocate system memory. template Result EmplaceFront(Args&&... args); /// Pushes a copy of the specified item onto the back of the deque. /// /// @param [in] data Item to be added to the back of the deque. /// /// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation /// failed because of an internal failure to allocate system memory. Result PushBack(const T& data); /// Emplaces a newly constructed item onto the back of the deque. /// /// @param [in] args arguments used to construct the new item. /// /// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation /// failed because of an internal failure to allocate system memory. template Result EmplaceBack(Args&&... args); /// Pops the first item off the front of the deque, returning the popped value. /// /// @param [out] pOut Item popped off the front of the deque. /// /// @returns @ref Success if the item was successfully popped from the deque or @ref ErrorUnavailable if the deque /// is empty. Result PopFront(T* pOut); /// Pops the first item off the back of the deque, returning the popped value. /// /// @param [out] pOut Item popped off the back of the deque. /// /// @returns @ref Success if the item was successfully popped from the deque or @ref ErrorUnavailable if the deque /// is empty. Result PopBack(T* pOut); private: Result AllocateFront(T**); Result AllocateBack(T**); DequeBlockHeader* AllocateNewBlock(); void FreeUnusedBlock(DequeBlockHeader* pHeader); // A helper function to avoid duplication in const and non-const versions of At(). T& InternalAt(uint32 index) const; size_t m_numElements; // Number of elements const size_t m_numElementsPerBlock; // Block granularity when we need to alloc a new one DequeBlockHeader* m_pFrontHeader; // First block of data elements, null for empty deques. DequeBlockHeader* m_pBackHeader; // Last block of data elements, null for empty deques/ T* m_pFront; // First data element, null for empty deques. T* m_pBack; // Last data element, null for empty deques. DequeBlockHeader* m_pLazyFreeHeader; // Cached pointer to the most-recently freed block. Allocator*const m_pAllocator; // Pointer to the allocator for this deque. PAL_DISALLOW_COPY_AND_ASSIGN(Deque); // Although this is a transgression of coding standards, it prevents DequeIterator requiring a public constructor; // constructing a 'bare' DequeIterator (i.e. without calling Deque::GetIterator) can never be a legal operation, so // this means that these two classes are much safer to use. friend class DequeIterator; }; // ===================================================================================================================== template Deque::Deque( Allocator*const pAllocator, size_t numElementsPerBlock) : m_numElements(0), m_numElementsPerBlock(numElementsPerBlock), m_pFrontHeader(nullptr), m_pBackHeader(nullptr), m_pFront(nullptr), m_pBack(nullptr), m_pLazyFreeHeader(nullptr), m_pAllocator(pAllocator) { } // ===================================================================================================================== // Frees all of the blocks this object allocated over its lifetime. template Deque::~Deque() { if (!std::is_trivial::value) { while (m_pFrontHeader != nullptr) { // Explicitly destroy the removed value since it's non-trivial and advance. // We must destroy all of them in the current block before freeing it. m_pFront->~T(); ++m_pFront; --m_numElements; if ((m_pFront == m_pFrontHeader->pEnd) || (m_numElements == 0)) { // Okay, the front block is now empty. Free it and advance to the next block. DequeBlockHeader* pBlockToFree = m_pFrontHeader; m_pFrontHeader = m_pFrontHeader->pNext; PAL_SAFE_FREE(pBlockToFree, m_pAllocator); if (m_pFrontHeader != nullptr) { // Fixup to the new block. m_pFront = static_cast(m_pFrontHeader->pStart); } } } } else { // Elements are trivial so skip iterating through elements and free each block. while (m_pFrontHeader != nullptr) { DequeBlockHeader* pBlockToFree = m_pFrontHeader; m_pFrontHeader = m_pFrontHeader->pNext; PAL_SAFE_FREE(pBlockToFree, m_pAllocator); } } if (m_pLazyFreeHeader != nullptr) { PAL_SAFE_FREE(m_pLazyFreeHeader, m_pAllocator); } } } // Util