PxArray.h#

Fully qualified name: include/foundation/PxArray.h

File members: include/foundation/PxArray.h

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of NVIDIA CORPORATION nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2026 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.

#ifndef PX_ARRAY_H
#define PX_ARRAY_H

#include "foundation/PxAssert.h"
#include "foundation/PxMathIntrinsics.h"
#include "foundation/PxAllocator.h"
#include "foundation/PxBasicTemplates.h"
#include "foundation/PxMemory.h"
#include "foundation/PxIO.h"

#if !PX_DOXYGEN
namespace physx
{
#endif

#if !PX_DOXYGEN
template <class T, class Alloc = typename PxAllocatorTraits<T>::Type>
class PxArray : protected Alloc
#else
template <class T, class Alloc> class PxArray
#endif
{
  public:
    typedef T* Iterator;
    typedef const T* ConstIterator;

    explicit PxArray(const PxEMPTY v) : Alloc(v)
    {
        if(mData)
            mCapacity |= PX_SIGN_BITMASK;
    }

    PX_INLINE explicit PxArray(const Alloc& alloc = Alloc()) : Alloc(alloc), mData(0), mSize(0), mCapacity(0)
    {
    }

    PX_INLINE explicit PxArray(uint32_t size, const T& a = T(), const Alloc& alloc = Alloc())
    : Alloc(alloc), mData(0), mSize(0), mCapacity(0)
    {
        resize(size, a);
    }

    template <class A>
    PX_INLINE explicit PxArray(const PxArray<T, A>& other, const Alloc& alloc = Alloc())
    : Alloc(alloc)
    {
        copy(other);
    }

    // This is necessary else the basic default copy constructor is used in the case of both arrays being of the same
    // template instance
    // The C++ standard clearly states that a template constructor is never a copy constructor [2]. In other words,
    // the presence of a template constructor does not suppress the implicit declaration of the copy constructor.
    // Also never make a copy constructor explicit, or copy-initialization* will no longer work. This is because
    // 'binding an rvalue to a const reference requires an accessible copy constructor' (http://gcc.gnu.org/bugs/)
    // *http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-initialization-and-assignment-initializ
    PX_INLINE PxArray(const PxArray& other, const Alloc& alloc = Alloc()) : Alloc(alloc)
    {
        copy(other);
    }

    PX_INLINE explicit PxArray(const T* first, const T* last, const Alloc& alloc = Alloc())
    : Alloc(alloc), mSize(last < first ? 0 : uint32_t(last - first)), mCapacity(mSize)
    {
        mData = allocate(mSize);
        if(mData)
            copy(mData, mData + mSize, first);
    }

    PX_INLINE ~PxArray()
    {
        destroy(mData, mData + mSize);

        if(capacity() && !isInUserMemory())
            deallocate(mData);
    }

    template <class A>
    PX_INLINE PxArray& operator=(const PxArray<T, A>& rhs)
    {
        if(&rhs == this)
            return *this;

        clear();
        reserve(rhs.mSize);
        copy(mData, mData + rhs.mSize, rhs.mData);

        mSize = rhs.mSize;
        return *this;
    }

    PX_INLINE PxArray& operator=(const PxArray& t) // Needs to be declared, see comment at copy-constructor
    {
        return operator=<Alloc>(t);
    }

    PX_FORCE_INLINE const T& operator[](uint32_t i) const
    {
        PX_ASSERT(i < mSize);
        return mData[i];
    }

    PX_FORCE_INLINE T& operator[](uint32_t i)
    {
        PX_ASSERT(i < mSize);
        return mData[i];
    }

    PX_FORCE_INLINE ConstIterator begin() const
    {
        return mData;
    }

    PX_FORCE_INLINE Iterator begin()
    {
        return mData;
    }

    PX_FORCE_INLINE ConstIterator end() const
    {
        return mData + mSize;
    }

    PX_FORCE_INLINE Iterator end()
    {
        return mData + mSize;
    }

    PX_FORCE_INLINE const T& front() const
    {
        PX_ASSERT(mSize);
        return mData[0];
    }

    PX_FORCE_INLINE T& front()
    {
        PX_ASSERT(mSize);
        return mData[0];
    }

    PX_FORCE_INLINE const T& back() const
    {
        PX_ASSERT(mSize);
        return mData[mSize - 1];
    }

    PX_FORCE_INLINE T& back()
    {
        PX_ASSERT(mSize);
        return mData[mSize - 1];
    }

    PX_FORCE_INLINE uint32_t size() const
    {
        return mSize;
    }

    PX_INLINE void clear()
    {
        destroy(mData, mData + mSize);
        mSize = 0;
    }

    PX_FORCE_INLINE bool empty() const
    {
        return mSize == 0;
    }

    PX_INLINE Iterator find(const T& a)
    {
        uint32_t index;
        for(index = 0; index < mSize && mData[index] != a; index++)
            ;
        return mData + index;
    }

    PX_INLINE ConstIterator find(const T& a) const
    {
        uint32_t index;
        for(index = 0; index < mSize && mData[index] != a; index++)
            ;
        return mData + index;
    }

    PX_FORCE_INLINE T* pushBack(const T& a)
    {
        if(capacity() <= mSize)
            return growAndPushBack(a);

        PX_PLACEMENT_NEW(reinterpret_cast<void*>(mData + mSize), T)(a);

        return &mData[mSize++];
    }

    PX_INLINE T popBack()
    {
        PX_ASSERT(mSize);
        T t = mData[mSize - 1];

        mData[--mSize].~T();

        return t;
    }

    PX_INLINE T* insert()
    {
        if(capacity() <= mSize)
        {
            if (!grow(capacityIncrement()))
            {
                return NULL;
            }
        }

        T* ptr = mData + mSize++;
        PX_PLACEMENT_NEW(ptr, T); // not 'T()' because PODs should not get default-initialized.
        return ptr;
    }

    PX_INLINE void replaceWithLast(uint32_t i)
    {
        PX_ASSERT(i < mSize);
        mData[i] = mData[--mSize];

        mData[mSize].~T();
    }

    PX_INLINE void replaceWithLast(Iterator i)
    {
        replaceWithLast(static_cast<uint32_t>(i - mData));
    }

    PX_INLINE bool findAndReplaceWithLast(const T& a)
    {
        uint32_t index = 0;
        while(index < mSize && mData[index] != a)
            ++index;
        if(index == mSize)
            return false;
        replaceWithLast(index);
        return true;
    }

    PX_INLINE void remove(uint32_t i)
    {
        PX_ASSERT(i < mSize);

        T* it = mData + i;
        it->~T();
        while (++i < mSize)
        {
            PX_PLACEMENT_NEW(it, T(mData[i]));
            ++it;
            it->~T();
        }
        --mSize;
    }

    PX_INLINE void removeRange(uint32_t begin, uint32_t count)
    {
        PX_ASSERT(begin < mSize);
        PX_ASSERT((begin + count) <= mSize);

        for(uint32_t i = 0; i < count; i++)
            mData[begin + i].~T(); // call the destructor on the ones being removed first.

        T* dest = &mData[begin];                       // location we are copying the tail end objects to
        T* src = &mData[begin + count];                // start of tail objects
        uint32_t move_count = mSize - (begin + count); // compute remainder that needs to be copied down

        for(uint32_t i = 0; i < move_count; i++)
        {
            PX_PLACEMENT_NEW(dest, T(*src));    // copy the old one to the new location
            src->~T();                          // call the destructor on the old location
            dest++;
            src++;
        }
        mSize -= count;
    }

    PX_NOINLINE bool resize(const uint32_t size, const T& a = T());

    PX_NOINLINE bool resizeUninitialized(const uint32_t size);

    PX_INLINE bool shrink()
    {
        return recreate(mSize);
    }

    PX_INLINE void reset()
    {
        resize(0);
        shrink();
    }

    PX_INLINE void resetOrClear()
    {
        const PxU32 c = capacity();
        const PxU32 s = size();
        if(s>=c/2)
            clear();
        else
            reset();
    }

    PX_INLINE bool reserve(const uint32_t capacity)
    {
        if(capacity > this->capacity())
            return grow(capacity);
        return true;
    }

    PX_FORCE_INLINE uint32_t capacity() const
    {
        return mCapacity & ~PX_SIGN_BITMASK;
    }

    PX_FORCE_INLINE void forceSize_Unsafe(uint32_t size)
    {
        PX_ASSERT(size <= capacity());
        mSize = size;
    }

    PX_INLINE void swap(PxArray<T, Alloc>& other)
    {
        PxSwap(mData, other.mData);
        PxSwap(mSize, other.mSize);
        PxSwap(mCapacity, other.mCapacity);
    }

    PX_INLINE bool assign(const T* first, const T* last)
    {
        if(!resizeUninitialized(uint32_t(last - first)))
            return false;

        copy(begin(), end(), first);
        return true;
    }

    // We need one bit to mark arrays that have been deserialized from a user-provided memory block.
    // For alignment & memory saving purpose we store that bit in the rarely used capacity member.
    PX_FORCE_INLINE uint32_t isInUserMemory() const
    {
        return mCapacity & PX_SIGN_BITMASK;
    }

    PX_INLINE Alloc& getAllocator()
    {
        return *this;
    }

  protected:
    // constructor for where we don't own the memory
    PxArray(T* memory, uint32_t size, uint32_t capacity, const Alloc& alloc = Alloc())
    : Alloc(alloc), mData(memory), mSize(size), mCapacity(capacity | PX_SIGN_BITMASK)
    {
    }

    template <class A>
    PX_NOINLINE void copy(const PxArray<T, A>& other);

    PX_INLINE T* allocate(uint32_t size, uint32_t* cookie=NULL)
    {
        // always call Alloc::allocate to respect cookie logic even if size is 0
        T* p = reinterpret_cast<T*>(Alloc::allocate(sizeof(T) * size, PX_FL, cookie));
        if(size > 0)
        {
            if(p)
            {
                PxMarkSerializedMemory(p, sizeof(T) * size);
            }
            else
            {
                PxGetFoundation().error(PxErrorCode::eOUT_OF_MEMORY, PX_FL, "PxArray::allocate: allocator returned null pointer.");
            }
        }
        return p;
    }

    PX_INLINE void deallocate(void* mem, uint32_t* cookie=NULL)
    {
        Alloc::deallocate(mem, cookie);
    }

    static PX_INLINE void create(T* first, T* last, const T& a)
    {
        for(; first < last; ++first)
            ::PX_PLACEMENT_NEW(first, T(a));
    }

    static PX_INLINE void copy(T* first, T* last, const T* src)
    {
        if(last <= first)
            return;

        for(; first < last; ++first, ++src)
            ::PX_PLACEMENT_NEW(first, T(*src));
    }

    static PX_INLINE void destroy(T* first, T* last)
    {
        for(; first < last; ++first)
            first->~T();
    }

    PX_NOINLINE T* growAndPushBack(const T& a);

    PX_INLINE bool grow(uint32_t capacity)
    {
        PX_ASSERT(this->capacity() < capacity);
        return recreate(capacity);
    }

    PX_NOINLINE bool recreate(uint32_t capacity);

    // The idea here is to prevent accidental bugs with pushBack or insert. Unfortunately
    // it interacts badly with InlineArrays with smaller inline allocations.
    // TODO(dsequeira): policy template arg, this is exactly what they're for.
    PX_INLINE uint32_t capacityIncrement() const
    {
        const uint32_t capacity = this->capacity();
        return capacity == 0 ? 1 : capacity * 2;
    }

    T* mData;
    uint32_t mSize;
    uint32_t mCapacity;
};

template <class T, class Alloc>
PX_NOINLINE bool PxArray<T, Alloc>::resize(const uint32_t size, const T& a)
{
    if(!reserve(size))
        return false;
    create(mData + mSize, mData + size, a);
    destroy(mData + size, mData + mSize);
    mSize = size;
    return true;
}

template <class T, class Alloc>
template <class A>
PX_NOINLINE void PxArray<T, Alloc>::copy(const PxArray<T, A>& other)
{
    if(!other.empty())
    {
        mData = allocate(mSize = mCapacity = other.size());
        if(mData)
            copy(mData, mData + mSize, other.begin());
    }
    else
    {
        mData = NULL;
        mSize = 0;
        mCapacity = 0;
    }

    // mData = allocate(other.mSize);
    // mSize = other.mSize;
    // mCapacity = other.mSize;
    // copy(mData, mData + mSize, other.mData);
}

template <class T, class Alloc>
PX_NOINLINE bool PxArray<T, Alloc>::resizeUninitialized(const uint32_t size)
{
    if(!reserve(size))
        return false;
    mSize = size;
    return true;
}

template <class T, class Alloc>
PX_NOINLINE T* PxArray<T, Alloc>::growAndPushBack(const T& a)
{
    const uint32_t capacity = capacityIncrement();

    uint32_t cookie = 0;
    T* newData = allocate(capacity, &cookie);
    if(!newData)
        return NULL;
    PX_ASSERT((!capacity) || (newData && (newData != mData)));

    copy(newData, newData + mSize, mData);

    // inserting element before destroying old array
    // avoids referencing destroyed object when duplicating array element.
    PX_PLACEMENT_NEW(reinterpret_cast<void*>(newData + mSize), T)(a);

    destroy(mData, mData + mSize);
    if(!isInUserMemory())
        deallocate(mData, &cookie);

    mData = newData;
    mCapacity = capacity;
    return &mData[mSize++];
}

template <class T, class Alloc>
PX_NOINLINE bool PxArray<T, Alloc>::recreate(uint32_t capacity)
{
    uint32_t cookie = 0;
    T* newData = allocate(capacity, &cookie);
    if(capacity && !newData)
        return false;
    PX_ASSERT((!capacity) || (newData && (newData != mData)));

    copy(newData, newData + mSize, mData);
    destroy(mData, mData + mSize);
    if(!isInUserMemory())
        deallocate(mData, &cookie);

    mData = newData;
    mCapacity = capacity;
    return true;
}

template <class T, class Alloc>
PX_INLINE void swap(PxArray<T, Alloc>& x, PxArray<T, Alloc>& y)
{
    x.swap(y);
}

#if !PX_DOXYGEN
} // namespace physx
#endif

#endif