From cd8c944989c484efedbbf0049bab43dfacdc1e1b Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 7 Mar 2026 09:36:46 -0600 Subject: [PATCH] vector work to allow it to take an allocator, as well as a fixed count and skip the tracking vars. --- Engine/source/console/engineStructs.h | 2 - Engine/source/console/propertyParsing.h | 1 - Engine/source/core/util/str.h | 7 +- Engine/source/core/util/tVector.cpp | 254 ++- Engine/source/core/util/tVector.h | 1900 +++++++++++++---- .../source/core/util/tVectorSpecializations.h | 2 + Engine/source/lighting/lightingInterfaces.h | 3 +- Engine/source/platform/platform.h | 12 +- Engine/source/platform/platformFont.h | 2 +- Engine/source/scene/sceneContainer.cpp | 2 +- Engine/source/scene/sceneObject.cpp | 26 +- Engine/source/scene/sceneObject.h | 1 + 12 files changed, 1768 insertions(+), 444 deletions(-) diff --git a/Engine/source/console/engineStructs.h b/Engine/source/console/engineStructs.h index 57b9a4050..d6c7f1f7e 100644 --- a/Engine/source/console/engineStructs.h +++ b/Engine/source/console/engineStructs.h @@ -33,8 +33,6 @@ /// @file /// Definitions for the core engine structured types. - -template< typename T > class Vector; namespace Torque { class UUID; } diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h index 69bdc09f6..91eeb07b3 100644 --- a/Engine/source/console/propertyParsing.h +++ b/Engine/source/console/propertyParsing.h @@ -47,7 +47,6 @@ class String; class FileName; class SimObject; class SimObjectType; -template class Vector; //----------------------------------------------------------------------------- // String scan/print methods diff --git a/Engine/source/core/util/str.h b/Engine/source/core/util/str.h index 827ba0c1f..7cb48e9f5 100644 --- a/Engine/source/core/util/str.h +++ b/Engine/source/core/util/str.h @@ -30,9 +30,8 @@ #endif #include - -template< class T > class Vector; - +template struct CustomAllocator; +template class Vector; typedef UTF8 StringChar; @@ -147,7 +146,7 @@ public: String collapseEscapes() const; /// Split the string into its components separated by the given delimiter. - void split( const char* delimiter, Vector< String >& outElements ) const; + void split( const char* delimiter, Vector >& outElements ) const; /// Return true if the string starts with the given text. bool startsWith( const char* text ) const; diff --git a/Engine/source/core/util/tVector.cpp b/Engine/source/core/util/tVector.cpp index 42ef4b21a..0993fb08f 100644 --- a/Engine/source/core/util/tVector.cpp +++ b/Engine/source/core/util/tVector.cpp @@ -24,54 +24,16 @@ #include "core/util/tVector.h" #include "platform/profiler.h" +#include "console/engineAPI.h" #ifdef TORQUE_DEBUG_GUARD -bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize, - const char* fileName, - const U32 lineNum) +bool VectorResize(U32* aSize, U32* aCount, void** arrayPtr, U32 newCount, U32 elemSize, + const char* fileName, + const U32 lineNum) { - PROFILE_SCOPE( VectorResize ); - - if (newCount > 0) - { - U32 blocks = newCount / VectorBlockSize; - if (newCount % VectorBlockSize) - blocks++; - S32 mem_size = blocks * VectorBlockSize * elemSize; - - const char* pUseFileName = fileName != NULL ? fileName : __FILE__; - U32 useLineNum = fileName != NULL ? lineNum : __LINE__; - - if (*arrayPtr != NULL) - *arrayPtr = dRealloc_r(*arrayPtr, mem_size, pUseFileName, useLineNum); - else - *arrayPtr = dMalloc_r(mem_size, pUseFileName, useLineNum); - - AssertFatal( *arrayPtr, "VectorResize - Allocation failed." ); - - *aCount = newCount; - *aSize = blocks * VectorBlockSize; - return true; - } - - if (*arrayPtr) - { - dFree(*arrayPtr); - *arrayPtr = 0; - } - - *aSize = 0; - *aCount = 0; - return true; -} - -#else - -bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize) -{ - PROFILE_SCOPE( VectorResize ); + PROFILE_SCOPE(VectorResize); if (newCount > 0) { @@ -79,7 +41,46 @@ bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 el if (newCount % VectorBlockSize) blocks++; S32 mem_size = blocks * VectorBlockSize * elemSize; - *arrayPtr = *arrayPtr ? dRealloc(*arrayPtr,mem_size) : + + const char* pUseFileName = fileName != NULL ? fileName : __FILE__; + U32 useLineNum = fileName != NULL ? lineNum : __LINE__; + + if (*arrayPtr != NULL) + *arrayPtr = dRealloc_r(*arrayPtr, mem_size, pUseFileName, useLineNum); + else + *arrayPtr = dMalloc_r(mem_size, pUseFileName, useLineNum); + + AssertFatal(*arrayPtr, "VectorResize - Allocation failed."); + + *aCount = newCount; + *aSize = blocks * VectorBlockSize; + return true; + } + + if (*arrayPtr) + { + dFree(*arrayPtr); + *arrayPtr = 0; + } + + *aSize = 0; + *aCount = 0; + return true; +} + +#else + +bool VectorResize(U32* aSize, U32* aCount, void** arrayPtr, U32 newCount, U32 elemSize) +{ + PROFILE_SCOPE(VectorResize); + + if (newCount > 0) + { + U32 blocks = newCount / VectorBlockSize; + if (newCount % VectorBlockSize) + blocks++; + S32 mem_size = blocks * VectorBlockSize * elemSize; + *arrayPtr = *arrayPtr ? dRealloc(*arrayPtr, mem_size) : dMalloc(mem_size); *aCount = newCount; @@ -87,7 +88,7 @@ bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 el return true; } - if (*arrayPtr) + if (*arrayPtr) { dFree(*arrayPtr); *arrayPtr = 0; @@ -99,3 +100,166 @@ bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 el } #endif + +void test_Vector_dynamic() { + Vector v; + AssertFatal(v.size() == 0, "Initial size should be 0"); + AssertFatal(v.empty(), "Vector should be empty"); + + // push_back, push_front, front, back + v.push_back(42); + AssertFatal(v.size() == 1, "Size after push_back should be 1"); + AssertFatal(v[0] == 42, "push_back value incorrect"); + v.push_front(7); + AssertFatal(v.size() == 2, "Size after push_front should be 2"); + AssertFatal(v[0] == 7 && v[1] == 42, "push_front value incorrect"); + AssertFatal(v.front() == 7, "front() incorrect"); + AssertFatal(v.back() == 42, "back() incorrect"); + + // insert, erase, erase_fast + v.insert(1, 99); + AssertFatal(v.size() == 3, "Size after insert should be 3"); + AssertFatal(v[1] == 99, "insert value incorrect"); + v.erase(1); + AssertFatal(v.size() == 2, "Size after erase should be 2"); + v.push_back(55); + v.erase_fast(static_cast < U32>(0)); + AssertFatal(v.size() == 2, "Size after erase_fast should be 2"); + + // find_next, contains, remove + AssertFatal(v.find_next(42) != -1, "find_next failed"); + AssertFatal(v.contains(42), "contains failed"); + AssertFatal(v.remove(42), "remove failed"); + AssertFatal(!v.contains(42), "remove did not remove value"); + + // pop_front, pop_back + v.push_back(88); + v.pop_front(); + AssertFatal(v.size() == 1, "pop_front failed"); + v.pop_back(); + AssertFatal(v.size() == 0, "pop_back failed"); + + // reserve, capacity, setSize, set, clear, compact, fill, copy, merge, reverse + v.reserve(5); + AssertFatal(v.capacity() >= 5, "reserve/capacity failed"); + v.setSize(3); + AssertFatal(v.size() == 3, "setSize failed"); + U8 arr[3] = { 1,2,3 }; + v.set(arr, 3); + for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == arr[i], "set failed"); + v.clear(); + AssertFatal(v.size() == 0, "clear failed"); + v.setSize(3); + v.fill(9); + for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == 9, "fill failed"); + U8 arr2[3] = { 4,5,6 }; + v.copy(arr2); + for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == arr2[i], "copy failed"); + v.reverse(); + AssertFatal(v[0] == 6 && v[2] == 4, "reverse failed"); + Vector v2; + v2.set(arr, 3); + v.merge(v2); + AssertFatal(v.size() == 6, "merge failed"); + + // increment, decrement + v.increment(); + AssertFatal(v.size() == 7, "increment failed"); + v.decrement(); + AssertFatal(v.size() == 6, "decrement failed"); + v.increment(2); + AssertFatal(v.size() == 8, "increment(n) failed"); + v.decrement(2); + AssertFatal(v.size() == 6, "decrement(n) failed"); + + // insert (block), fill(count), fill(count, offset) + U8 block[2] = { 10,11 }; + v.insert(block, 2, 2); + AssertFatal(v[2] == 10 && v[3] == 11, "block insert failed"); + v.fill(7, 2); + for (U32 i = 0; i < 2; ++i) AssertFatal(v[i] == 7, "fill(count) failed"); + v.fill(8, 2, 2); + AssertFatal(v[2] == 8 && v[3] == 8, "fill(count,offset) failed"); + + // assign, moveFrom, swap, compare, forEach, removeIf, countIf, anyOf, allOf + Vector v3; + v3.set(arr, 3); + v.assign(&v3, 3, 0, 0); + for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == arr[i], "assign failed"); + v3.moveFrom(v); + AssertFatal(v3.size() == 3 && v.size() == 0, "moveFrom failed"); + v.set(arr, 3); + v.swap(v3); + for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == arr[i], "swap failed"); + AssertFatal(v.compare(v3), "compare failed"); + v.forEach([](U8& x) { x = 1; }); + for (U32 i = 0; i < v.size(); ++i) AssertFatal(v[i] == 1, "forEach failed"); + v.removeIf([](U8 x) { return x == 1; }); + AssertFatal(v.size() == 0, "removeIf failed"); + v.set(arr, 3); + AssertFatal(v.countIf([](U8 x) { return x > 1; }) == 2, "countIf failed"); + AssertFatal(v.anyOf([](U8 x) { return x == 2; }), "anyOf failed"); + AssertFatal(v.allOf([](U8 x) { return x > 0; }), "allOf failed"); +} + +void test_Vector_fixed() { + Vector v; + AssertFatal(v.size() == 10, "Fixed vector size should be 10"); + + // fill, operator[], front, back, first, last + v.fill(5); + for (U32 i = 0; i < v.size(); ++i) AssertFatal(v[i] == 5, "fill failed"); + v[3] = 99; + AssertFatal(v[3] == 99, "operator[] failed"); + AssertFatal(v.front() == 5, "front failed"); + AssertFatal(v.back() == 5, "back failed"); + AssertFatal(v.first() == 5, "first failed"); + AssertFatal(v.last() == 5, "last failed"); + + // reverse, swap, erase, erase_fast, contains, remove + v.reverse(); + AssertFatal(v[6] == 99, "reverse failed"); + Vector v2; + v2.fill(1); + v.swap(v2); + for (U32 i = 0; i < v.size(); ++i) AssertFatal(v[i] == 1 && v2[i] == 5, "swap failed"); + v.erase(static_cast (0)); + AssertFatal(v.size() == 10, "erase should not change size for fixed vector"); + v.erase_fast(1); + AssertFatal(v.size() == 10, "erase_fast should not change size for fixed vector"); + AssertFatal(v.contains(1), "contains failed"); + v.remove(1); + AssertFatal(v.contains(1), "remove should not change size for fixed vector"); + + // fill(count), fill(count, offset), copy, merge, assign, compare + v.fill(2, 5); + for (U32 i = 0; i < 5; ++i) AssertFatal(v[i] == 2, "fill(count) failed"); + v.fill(3, 2, 5); + AssertFatal(v[5] == 3 && v[6] == 3, "fill(count,offset) failed"); + U8 arr[10] = { 0,1,2,3,4,5,6,7,8,9 }; + v.copy(arr); + for (U32 i = 0; i < 10; ++i) AssertFatal(v[i] == arr[i], "copy failed"); + v2.copy(arr); + v.merge(v2); + AssertFatal(v.size() == 10, "merge should not change size for fixed vector"); + v.assign(&v2, 10, 0, 0); + for (U32 i = 0; i < 10; ++i) AssertFatal(v[i] == arr[i], "assign failed"); + AssertFatal(v.compare(v2), "compare failed"); + + // forEach, removeIf, countIf, anyOf, allOf + v.forEach([](U8& x) { x = 4; }); + for (U32 i = 0; i < v.size(); ++i) AssertFatal(v[i] == 4, "forEach failed"); + v.removeIf([](U8 x) { return x == 4; }); + AssertFatal(v.size() == 10, "removeIf should not change size for fixed vector"); + v.copy(arr); + AssertFatal(v.countIf([](U8 x) { return x > 5; }) == 4, "countIf failed"); + AssertFatal(v.anyOf([](U8 x) { return x == 7; }), "anyOf failed"); + AssertFatal(v.allOf([](U8 x) { return x < 10; }), "allOf failed"); +} + +DefineEngineFunction(test_Vector, void, (), , + "Test the Vector class with both dynamic and fixed-size vectors.") +{ + test_Vector_dynamic(); + test_Vector_fixed(); +} diff --git a/Engine/source/core/util/tVector.h b/Engine/source/core/util/tVector.h index c7c4fc7a4..79ade2fe4 100644 --- a/Engine/source/core/util/tVector.h +++ b/Engine/source/core/util/tVector.h @@ -28,6 +28,8 @@ #ifndef _PLATFORM_H_ #include "platform/platform.h" #endif +#include +#include #include #include "console/engineTypes.h" #include "console/engineTypeInfo.h" @@ -37,12 +39,13 @@ /// Size of memory blocks to allocate at a time for vectors. const static S32 VectorBlockSize = 16; + #ifdef TORQUE_DEBUG_GUARD -extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize, - const char* fileName, - const U32 lineNum); +extern bool VectorResize(U32* aSize, U32* aCount, void** arrayPtr, U32 newCount, U32 elemSize, + const char* fileName, + const U32 lineNum); #else -extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize); +extern bool VectorResize(U32* aSize, U32* aCount, void** arrayPtr, U32 newCount, U32 elemSize); #endif /// Use the following macro to bind a vector to a particular line @@ -53,6 +56,731 @@ extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, #define VECTOR_SET_ASSOCIATION(x) #endif +//----------------------------------------------------------------------------- +// Custom allocator using dMalloc/dFree + +// typetrait defs +template +struct VectorTrackingTokens { + U32 arraySize() const { return COUNT; } + U32 elementCount() const { return COUNT; } + void setArraySize(U32) {} // no-op + void setElementCount(U32) {} // no-op + /* + bool mOwnsMemory = false; + bool ownsMemory() const { return mOwnsMemory; } + void setOwnsMemory(bool owns) { mOwnsMemory = owns; } + */ +}; + +template<> +struct VectorTrackingTokens<0> { + U32 mArraySize = 0; + U32 mElementCount = 0; + U32 arraySize() const { return mArraySize; } + U32 elementCount() const { return mElementCount; } + void setArraySize(U32 sz) { mArraySize = sz; } + void setElementCount(U32 sz) { mElementCount = sz; } + /* + bool mOwnsMemory = false; + bool ownsMemory() const { return mOwnsMemory; } + void setOwnsMemory(bool owns) { mOwnsMemory = owns; } + */ +}; + +template +struct VectorTraits; + +template +struct VectorTraits> +{ + static constexpr U32 COUNT = COUNT; + using allocator = Allocator; + using value_type = T; +}; + +//----------------------------------------------------------------------------- +// Custom allocator using dMalloc/dFree +template +struct CustomAllocator +{ + using State = VectorTrackingTokens; + static T* allocate(U32 count = COUNT) + { + if (COUNT > 0) + return reinterpret_cast(dMalloc(COUNT * sizeof(T))); + else + return reinterpret_cast(dMalloc(count * sizeof(T))); + } + static void deallocate(T* ptr) + { + dFree(ptr); + } +}; + +//----------------------------------------------------------------------------- +// VectorHelpers namespace +// +// Purpose: +// - Provides extensible, utility functions for vector operations. +// - Supports bulk, cross-type, and advanced operations not covered by core vector methods. +// - Intended for use cases such as batch processing, interoperability, and future extensions (e.g., TreeNode, BVH, SVO). +// +// Usage: +// - Use VectorHelpers for operations that go beyond standard vector element access and modification. +// - Suitable for experimental, cross-type, or performance-oriented utilities. +// +// Example operations: +// - Bulk copy, move, fill, destroy across multiple vectors or specializations. +// - Generic algorithms and batch utilities for vector-like types. +// - Integration with advanced data structures or scene systems. +// +namespace VectorHelpers +{ + // Helper for fixed-size capacity checks. + template + inline bool checkFixedCapacity(VecT* vec, U32 required) + { + using Traits = VectorTraits; + constexpr U32 COUNT = Traits::COUNT; + if constexpr (COUNT != 0) + { + if (required > COUNT) + { + AssertFatal(false, "VectorHelpers::checkFixedCapacity - operation would exceed fixed vector capacity."); + return false; + } + } + return true; + } + + // Copies elements from a buffer into a vector, up to n elements, with offset in both buffer and vector. + template + inline void fromBuffer(VecT* vec, const typename VectorTraits::value_type* buffer, U32 n, U32 vecOffset = 0, U32 bufferOffset = 0) + { + if (!vec || !buffer || n == 0) return; + using T = typename VectorTraits::value_type; + T* arr = vec->array(); + U32 len = vec->size(); + if (vecOffset >= len || bufferOffset >= n) return; + if (vecOffset + n > len) n = len - vecOffset; + if constexpr (std::is_trivially_copyable::value) + dMemcpy(arr + vecOffset, buffer + bufferOffset, n * sizeof(T)); + else if constexpr (std::is_copy_assignable::value) + for (U32 i = 0; i < n; ++i) arr[vecOffset + i] = buffer[bufferOffset + i]; + // else: do nothing, not assignable + } + + // Copies elements from a vector into a buffer, up to n elements, with offset in both vector and buffer. + template + inline void toBuffer(const VecT* vec, typename VectorTraits::value_type* buffer, U32 n, U32 vecOffset = 0, U32 bufferOffset = 0) + { + if (!vec || !buffer || n == 0) return; + using T = typename VectorTraits::value_type; + const T* arr = vec->array(); + U32 len = vec->size(); + if (vecOffset >= len || bufferOffset >= n) return; + if (vecOffset + n > len) n = len - vecOffset; + if constexpr (std::is_trivially_copyable::value) + dMemcpy(buffer + bufferOffset, arr + vecOffset, n * sizeof(T)); + else if constexpr (std::is_copy_assignable::value) + for (U32 i = 0; i < n; ++i) buffer[bufferOffset + i] = arr[vecOffset + i]; + // else: do nothing, not assignable + } + + // Moves or copies 'n' elements between a buffer and a vector at offset. + template + inline void bufferTransfer( + VecT& vec, + typename VectorTraits::value_type* buffer, + typename VectorTraits::COUNT n, + typename VectorTraits::COUNT offset, + bool toBuffer, + bool moveElements = true) + { + using T = typename VectorTraits::value_type; + using U32 = typename VectorTraits::COUNT; + U32 vecCapacity = vec.capacity(); + if (!buffer || n == 0 || offset >= vecCapacity) return; + if (offset + n > vecCapacity) n = vecCapacity - offset; + if (!toBuffer && U32(0) == 0 && offset + n > vec.size()) vec.setSize(offset + n); + T* vecArray = vec.array(); + if (!moveElements) { + if (toBuffer) toBuffer(&vec, buffer, n, offset); + else fromBuffer(&vec, buffer, n, offset); + return; + } + if constexpr (std::is_trivially_copyable::value) { + if (toBuffer) { + dMemcpy(buffer, &vecArray[offset], n * sizeof(T)); + dMemset(&vecArray[offset], 0, n * sizeof(T)); + } + else { + dMemcpy(&vecArray[offset], buffer, n * sizeof(T)); + dMemset(buffer, 0, n * sizeof(T)); + } + } + else if constexpr (std::is_move_assignable::value && std::is_default_constructible::value) { + if (toBuffer) { + for (U32 i = 0; i < n; ++i) { + buffer[i] = std::move(vecArray[offset + i]); + vecArray[offset + i] = T(); + } + } + else { + for (U32 i = 0; i < n; ++i) { + vecArray[offset + i] = std::move(buffer[i]); + buffer[i] = T(); + } + } + } + // else: do nothing, not move-assignable or not default-constructible + } + + // Merges elements from a source buffer into the end of a vector, resizing if needed. + template + inline void mergeBuffer(VecT* vec, const typename VectorTraits::value_type* src, U32 count) + { + if (!vec || !src || count == 0) return; + U32 oldSize = vec->elementCount(); + U32 newSize = oldSize + count; + if (!checkFixedCapacity(vec, newSize)) return; + vec->setSize(newSize); + auto* dest = vec->array() + oldSize; + for (U32 i = 0; i < count; ++i) + dest[i] = src[i]; + } + + // Merges elements from a source vector into the end of a target vector, resizing if needed. + template + inline void mergeVector(VecT* vec, const VecT& src) + { + if (!vec || src.size() == 0) return; + U32 oldSize = vec->elementCount(); + U32 count = src.size(); + U32 newSize = oldSize + count; + if (!checkFixedCapacity(vec, newSize)) return; + vec->setSize(newSize); + auto* dest = vec->array() + oldSize; + auto* srcPtr = src.array(); + for (U32 i = 0; i < count; ++i) + dest[i] = srcPtr[i]; + } + + // Bulk insert: shift elements once, then copy in bulk. + template + inline void insertBuffer(VecT* vec, const typename VectorTraits::value_type* src, U32 count, U32 idx) + { + if (!vec || !src || count == 0) return; + if (!checkFixedCapacity(vec, vec->elementCount() + count)) return; + + // Grow the vector to fit the new elements + U32 oldCount = vec->elementCount(); + vec->setSize(oldCount + count); + + // Move existing elements after idx up by 'count' slots + if (oldCount > idx) + move(vec, vec, oldCount - idx, idx + count, idx); + + // Copy new elements into the gap + copy(vec, src, count, idx); + } + + // Permanently redirects the vector's internal T* mArray pointer to an external pool at a given offset and range defined by start/end. + // For dynamic vectors, update mArraySize/mElementCount if needed. + // For fixed-size vectors, only updates mArray; user must ensure pool is at least COUNT + start+end elements. + // Use with caution - this is a low-level operation that can lead to instability if used incorrectly. + // The caller must ensure the pool remains valid for the vector's lifetime and that no other operations invalidate the pool. + template + inline void rebaseArrayAt(VecT* vec, typename VectorTraits::value_type* pool, U32 start, U32 end) + { + if (!vec || !pool || end <= start) return; + using Traits = VectorTraits; + constexpr U32 COUNT = Traits::COUNT; + if constexpr (COUNT == 0) + vec->set(pool + start, end - start); + else { + if ((end - start) < COUNT) return; + vec->set(pool + start, COUNT); + } + } + + // Applies a function to each element in the source vector. + // If a target vector is provided, stores the result in the target at targOffset. + // If no target is provided, applies the function in-place or for side effects. + template + inline void forEach(VecT* src, Func fn, U32 srcOffset = 0, U32 n = 0, U32 targOffset = 0, VecT* targ = NULL) + { + if (!src) return; + U32 srcLen = src->size(); + if (srcOffset >= srcLen) return; + U32 len = (n == 0) ? srcLen - srcOffset : n; + if (len == 0) return; + using T = typename VectorTraits::value_type; + T* srcArr = src->array(); + if (targ) { + U32 targLen = targ->size(); + if (targOffset >= targLen) return; + len = std::min(len, targLen - targOffset); + T* targArr = targ->array(); + for (U32 i = 0; i < len; ++i) + targArr[targOffset + i] = fn(srcArr[srcOffset + i]); + } + else { + for (U32 i = 0; i < len; ++i) + fn(srcArr[srcOffset + i]); + } + } + + // Applies a function to each pair of elements from src and targ vectors. + // Returns bool for comparison operations; otherwise, void. + template + inline auto forEachPair(VecT1* src, VecT2* targ, U32 n = 0, U32 srcOffset = 0, U32 targOffset = 0, Func fn = Func()) + { + if (!src || !targ) { + using RetType = decltype(fn(*static_cast::value_type*>(NULL), + *static_cast::value_type*>(NULL))); + if constexpr (!std::is_void_v) return RetType(); + else return; + } + U32 srcLen = src->size(), targLen = targ->size(); + if (srcOffset >= srcLen || targOffset >= targLen) { + using RetType = decltype(fn(*static_cast::value_type*>(NULL), + *static_cast::value_type*>(NULL))); + if constexpr (!std::is_void_v) return RetType(); + else return; + } + U32 len = (n == 0) ? std::min(srcLen - srcOffset, targLen - targOffset) : n; + if (len == 0) { + using RetType = decltype(fn(*static_cast::value_type*>(NULL), + *static_cast::value_type*>(NULL))); + if constexpr (!std::is_void_v) return RetType(); + else return; + } + using T = typename VectorTraits::value_type; + using RetType = decltype(fn(std::declval(), std::declval())); + T* srcArr = src->array(); + T* targArr = targ->array(); + constexpr bool isCompare = std::is_same_v; + if constexpr (isCompare) { + bool result = true; + for (U32 i = 0; i < len; ++i) + result = result && fn(srcArr[srcOffset + i], targArr[targOffset + i]); + return result; + } + else { + for (U32 i = 0; i < len; ++i) + fn(srcArr[srcOffset + i], targArr[targOffset + i]); + if constexpr (!std::is_void_v) return RetType(); + } + } + + // Constructs elements in a vector. Uses std::fill for PODs, constructInPlace for non-PODs. + template + inline void create(VecT* vec, S32 idx = -1, S32 n = 1) + { + if (!vec) return; + U32 len = vec->size(); + U32 start = (idx >= 0) ? static_cast(idx) : 0; + U32 count = (n < 1) ? (len - start) : static_cast(n); + if (count == 0 || start >= len || start + count > len) return; + using Traits = VectorTraits; + using T = typename Traits::value_type; + constexpr U32 COUNT = Traits::COUNT; + T* arr = vec->array(); + if constexpr (COUNT == 0) { + // Only construct new elements, do not destruct or move existing ones + if constexpr (!std::is_trivially_constructible::value) + for (U32 i = 0; i < count; ++i) + constructInPlace(&arr[start + i]); + else + std::fill(arr + start, arr + start + count, T()); + } + else { + if constexpr (!std::is_trivially_constructible::value) + for (U32 i = 0; i < count; ++i) + constructInPlace(&arr[start + i]); + else + std::fill(arr + start, arr + start + count, T()); + if constexpr (std::is_default_constructible::value) + std::fill(arr + start + count, arr + COUNT, T()); + } + } + + // Compacts the vector by moving elements after [start, start+count) down and updating size/backfill. + // Does NOT destruct any elements. + template + inline void compact(VecT* vec, S32 idx = -1, S32 n = 1) + { + if (!vec) return; + using T = typename VectorTraits::value_type; + using Traits = VectorTraits; + U32 len = vec->size(); + U32 start = (idx >= 0) ? static_cast(idx) : 0; + U32 count = (n < 1) ? (len - start) : static_cast(n); + if (count == 0 || start >= len || start + count > len) return; + T* arr = vec->array(); + + U32 tail = len - (start + count); + if (tail > 0) + { + if constexpr (std::is_trivially_copyable::value) + { + dMemmove(&arr[start], &arr[start + count], tail * sizeof(T)); + // Reset tail region to default value + std::fill(arr + (start + tail), arr + (start + tail + count), T()); + } + else if constexpr (std::is_default_constructible::value) + { + for (U32 i = 0; i < tail; ++i) + arr[start + i] = std::move(arr[start + count + i]); + // Reset tail region to default value + for (U32 i = start + tail; i < start + tail + count; ++i) + constructInPlace(&arr[i]); + } + // else: do nothing, not default-constructible or not assignable + } + + if constexpr (Traits::COUNT == 0) + { + // Dynamic vector: update size + vec->setElementCount(len - count); + } + else + { + // Fixed-size: backfill tail + for (U32 i = len - count; i < len; ++i) + constructInPlace(&arr[i]); + } + } + + // Destroys elements in a vector, then compacts. + // Destructs elements in range, then calls compact to move and update size/backfill. + template + inline void destroy(VecT* vec, S32 idx = -1, S32 n = 1) + { + if (!vec) return; + U32 len = vec->size(); + U32 start = (idx >= 0) ? static_cast(idx) : 0; + U32 count = (n < 1) ? (len - start) : static_cast(n); + if (count == 0 || start >= len || start + count > len) return; + using T = typename VectorTraits::value_type; + T* arr = vec->array(); + + for (U32 i = 0; i < count; ++i) + { + if (arr && (start + i) < len) + { + destructInPlace(&arr[start + i]); + } + vec->setElementCount(len - count); + } + } + + // Removes elements matching a predicate + // Dynamic vectors: compacts and shrinks. + // Fixed-size vectors: destroys and backfills each matching element with type default. + template + inline void removeIf(Vector* vec, Func fn, U32 offset = 0, U32 n = 0) + { + if (!vec) return; + U32 len = vec->size(); + if (offset >= len) return; + U32 end = (n == 0) ? len : getMin(len, offset + n); + if (end <= offset) return; + T* arr = vec->array(); + if constexpr (COUNT == 0) { + U32 write = offset; + for (U32 read = offset; read < end; ++read) + if (!fn(arr[read])) { + if (write != read) arr[write] = std::move(arr[read]); + ++write; + } + VectorHelpers::destroy(vec, write, len - write); + vec->setSize(write); + } + else { + // Compact and backfill using destroy for fixed-size + U32 write = offset; + for (U32 read = offset; read < end; ++read) + if (!fn(arr[read])) { + if (write != read) arr[write] = std::move(arr[read]); + ++write; + } + VectorHelpers::destroy(vec, write, len - write); + } + } + + // Counts elements matching a predicate. + template + inline U32 countIf(const Vector* vec, Func fn) + { + if (!vec) return 0; + U32 len = vec->size(); + if (len == 0) return 0; + U32 n = 0; + const T* arr = vec->array(); + for (U32 i = 0; i < len; ++i) + if (fn(arr[i])) ++n; + return n; + } + + /// Returns true if any element matches the predicate. + template + inline bool anyOf(const Vector* vec, Func fn) + { + if (!vec) return false; + U32 len = vec->size(); + if (len == 0) return false; + const T* arr = vec->array(); + for (U32 i = 0; i < len; ++i) + if (fn(arr[i])) return true; + return false; + } + + /// Returns true if all elements match the predicate. + template + inline bool allOf(const Vector* vec, Func fn) + { + if (!vec) return false; + U32 len = vec->size(); + if (len == 0) return false; + const T* arr = vec->array(); + for (U32 i = 0; i < len; ++i) + if (!fn(arr[i])) return false; + return true; + } + + // Copies n elements from src to dst vector. + // Supports cross-specialization and optimized for PODs. + template + inline void copy(TargVecT* targ, const SrcVecT* src, U32 n = 0, U32 targOffset = 0, U32 srcOffset = 0) + { + using T = typename VectorTraits::value_type; + U32 targLen = targ->size(); + U32 len = n ? n : targLen - targOffset; + if (len == 0) return; + if constexpr (std::is_trivially_copyable::value) + std::memmove(targ->array() + targOffset, src + srcOffset, len * sizeof(T)); + else + for (U32 i = 0; i < len; ++i) + targ->array()[targOffset + i] = src[srcOffset + i]; + } + + // Moves n elements from src to dst vector. + // Supports cross-specialization and optimized for PODs. + template + inline void move(TargVecT* targ, SrcVecT* src, U32 n = 0, U32 targOffset = 0, U32 srcOffset = 0) + { + using T = typename VectorTraits::value_type; + constexpr U32 COUNT = VectorTraits::COUNT; + U32 targLen = targ->size(); + U32 len = n ? n : targLen - targOffset; + if (len == 0) return; + if constexpr (std::is_trivially_copyable::value) + std::memmove(targ->array() + targOffset, src->array() + srcOffset, len * sizeof(T)); + else if constexpr (std::is_move_assignable::value && std::is_move_constructible::value) + for (U32 i = 0; i < len; ++i) { + targ->array()[targOffset + i] = std::move(src->array()[srcOffset + i]); + if constexpr (COUNT != 0) + constructInPlace(&src->array()[srcOffset + i]); + else + src->array()[srcOffset + i] = T(); + } + else if constexpr (std::is_copy_assignable::value) + for (U32 i = 0; i < len; ++i) { + targ->array()[targOffset + i] = src->array()[srcOffset + i]; + if constexpr (COUNT != 0) + constructInPlace(&src->array()[srcOffset + i]); + else + src->array()[srcOffset + i] = T(); + } + else + static_assert(std::is_copy_assignable::value || std::is_move_assignable::value, + "VectorHelpers::move: T must be move- or copy-assignable."); + } + + // Fills n elements starting at offset with value. + template + inline void fill(VecT* vec, const typename VectorTraits::value_type& val, U32 n = 0, U32 offset = 0) + { + using Traits = VectorTraits; + using T = typename Traits::value_type; + U32 len = vec->size(); + if (n == 0) n = len - offset; + if (offset >= len || n == 0) return; + if constexpr (std::is_trivially_copyable::value) { + if constexpr (std::is_integral::value || std::is_floating_point::value || std::is_pointer::value) { + if (val == T()) { + std::memset(vec->array() + offset, 0, n * sizeof(T)); + return; + } + } + for (U32 i = 0; i < n; ++i) + vec->array()[offset + i] = val; + } + else if constexpr (std::is_copy_assignable::value) + for (U32 i = 0; i < n; ++i) + vec->array()[offset + i] = val; + // else: not assignable, do nothing + } + + // Assigns n elements from src to targ vector, handling overlap for non-PODs. + template + inline void assign(TargVecT* targ, const SrcVecT* src, U32 n = 0, U32 targOffset = 0, U32 srcOffset = 0) + { + using Traits = VectorTraits; + using T = typename Traits::value_type; + constexpr bool isPod = std::is_trivially_copyable::value; + U32 targLen = targ->size(), srcLen = src->size(); + if (n == 0 || targOffset + n > targLen || srcOffset + n > srcLen) + n = getMin(targLen - targOffset, srcLen - srcOffset); + if constexpr (Traits::COUNT == 0) + if (targOffset + n > targ->elementCount()) + targ->setSize(targOffset + n); + if (n == 0) return; + if constexpr (isPod) + std::memmove(targ->array() + targOffset, src->array() + srcOffset, n * sizeof(T)); + else if constexpr (std::is_copy_assignable::value) { + if (targ == src && std::abs((int)targOffset - (int)srcOffset) < (int)n) { + std::unique_ptr temp(new T[n]); + toBuffer(src, temp.get(), n, srcOffset); + fromBuffer(targ, temp.get(), n, targOffset); + } + else { + fromBuffer(targ, src->array() + srcOffset, n, targOffset); + } + } + // else: not copy-assignable, do nothing + } + + // Swaps n elements between two ranges in one or two vectors. + template + inline void swap(VecT* src, U32 srcOffset, U32 targOffset, U32 n = 0, OtherVecT* targ = NULL) + { + using T = typename VectorTraits::value_type; + U32 srcLen = src->size(), targLen = targ ? targ->size() : srcLen; + U32 len = (n == 0) ? getMin(srcLen - srcOffset, targLen - targOffset) : n; + T* srcArr = src->array(); + T* targArr = targ ? targ->array() : src->array(); + if constexpr (std::is_move_constructible::value && std::is_move_assignable::value) { + for (U32 i = 0; i < len; ++i) + std::swap(srcArr[srcOffset + i], targArr[targOffset + i]); + } + else if constexpr (std::is_copy_constructible::value && std::is_copy_assignable::value) { + // Fallback: use a temp buffer + std::unique_ptr temp(new T[len]); + for (U32 i = 0; i < len; ++i) + temp[i] = srcArr[srcOffset + i]; + for (U32 i = 0; i < len; ++i) + srcArr[srcOffset + i] = targArr[targOffset + i]; + for (U32 i = 0; i < len; ++i) + targArr[targOffset + i] = temp[i]; + } + // else: do nothing, not swappable + } + + // Reverses the order of elements in the vector or a range. + template + inline void reverse(VecT* vec, U32 n = 0, U32 offset = 0) + { + using T = typename VectorTraits::value_type; + U32 len = vec->size(); + if (offset >= len) return; + if (n == 0) n = len - offset; + if (n < 2) return; + T* arr = vec->array(); + if constexpr (std::is_move_constructible::value && std::is_move_assignable::value) { + U32 i = offset, j = offset + n - 1; + while (i < j) { + std::swap(arr[i], arr[j]); + ++i; --j; + } + } + else if constexpr (std::is_copy_constructible::value && std::is_copy_assignable::value) { + // Fallback: use a temp buffer + std::unique_ptr temp(new T[n]); + for (U32 i = 0; i < n; ++i) + temp[i] = arr[offset + n - 1 - i]; + for (U32 i = 0; i < n; ++i) + arr[offset + i] = temp[i]; + } + // else: do nothing, not reversible + } + + // Compares a range of elements between two vectors. + template + inline bool compare(const VecT* src, const OtherVecT* targ, U32 n = 0, U32 srcOffset = 0, U32 targOffset = 0) + { + U32 srcLen = src->size(), targLen = targ->size(); + if (srcOffset >= srcLen || targOffset >= targLen) return false; + if (n == 0) n = getMin(srcLen - srcOffset, targLen - targOffset); + auto arr1 = src->array(), arr2 = targ->array(); + for (U32 i = 0; i < n; ++i) + if (!(arr1[srcOffset + i] == arr2[targOffset + i])) return false; + return true; + } + + // Finds the index of a value in a vector or range. + template + inline S32 find(const VecT* vec, U32 n, const typename VecT::value_type& value, U32 offset = 0) + { + U32 len = vec->size(); + if (offset >= len) return -1; + if (n <= 0) n = len - offset; //consider n=0 or n=-1 as "search to end" + auto arr = vec->array(); + for (U32 i = 0; i < n; ++i) + if (arr[offset + i] == value) return static_cast(offset + i); + return -1; + } + + // Clears or resets a single element at a given index. + template + inline void clearElement(VecT* vec, U32 idx) + { + using T = typename VecT::value_type; + U32 len = vec->size(); + if (idx >= len) return; + if constexpr (std::is_trivially_copyable::value) + std::fill(vec->array() + idx, vec->array() + idx + 1, T()); + else if constexpr (std::is_default_constructible::value && std::is_copy_assignable::value) + vec->array()[idx] = T(); + // else: do nothing, not default-constructible or not assignable + } + + template + inline void moveWithin(VecT* vec, typename VecT::value_type* src, U32 n, U32 destOffset) + { + using T = typename VectorTraits::value_type; + if (!vec || !src || n == 0) return; + T* arr = vec->array(); + if constexpr (std::is_trivially_copyable::value) { + std::memmove(arr + destOffset, src, n * sizeof(T)); + } + else if (arr + destOffset > src && arr + destOffset < src + n) { + for (S32 i = n - 1; i >= 0; --i) + arr[destOffset + i] = std::move(src[i]); + } + else { + for (U32 i = 0; i < n; ++i) + arr[destOffset + i] = std::move(src[i]); + } + } + + template + inline typename VectorTraits::value_type* data(VecT* vec) + { + return vec ? vec->array() : NULL; + } + + template + inline const typename VectorTraits::value_type* data(const VecT* vec) + { + return vec ? vec->array() : NULL; + } + +} + // ============================================================================= /// A dynamic array template class. /// @@ -62,31 +790,39 @@ extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, /// reserve() method. /// /// @nosubgrouping -template +template> class Vector { friend class VectorFieldEngineExport; - protected: - U32 mElementCount; ///< Number of elements currently in the Vector. - U32 mArraySize; ///< Number of elements allocated for the Vector. - T* mArray; ///< Pointer to the Vector elements. +protected: + mutable typename Allocator::State mState; + T* mArray; ///< Pointer to the Vector elements. #ifdef TORQUE_DEBUG_GUARD const char* mFileAssociation; - U32 mLineAssociation; + U32 mLineAssociation; #endif - bool resize(U32); // resizes, but does no construction/destruction - void destroy(U32 start, U32 end); ///< Destructs elements from start to end-1 + bool resize(U32); // resizes, and does construction/destruction as apropriate + void destroy(U32 start, U32 end); ///< Destructs elements from start to end-1 void construct(U32 start, U32 end); ///< Constructs elements from start to end-1 - void construct(U32 start, U32 end, const T* array); - public: + void construct(U32 start, U32 end, const T* array); + +public: + /// @name Constructors/Destructor + /// @{ + void zeroState(); + /// Constructs a vector with an optional initial size. Vector(const U32 initialSize = 0); Vector(const U32 initialSize, const char* fileName, const U32 lineNum); Vector(const char* fileName, const U32 lineNum); Vector(const Vector&); ~Vector(); + /// allocator state access + typename Allocator::State& state() { return mState; } + const typename Allocator::State& state() const { return mState; } + #ifdef TORQUE_DEBUG_GUARD void setFileAssociation(const char* file, const U32 line); #endif @@ -95,17 +831,15 @@ class Vector /// @{ typedef T value_type; - typedef T& reference; + typedef T& reference; typedef const T& const_reference; - - typedef T* iterator; + typedef T* iterator; typedef const T* const_iterator; - typedef S32 difference_type; - typedef U32 size_type; + typedef S32 difference_type; + typedef U32 size_type; + typedef difference_type(QSORT_CALLBACK* compare_func)(const T* a, const T* b); - typedef difference_type (QSORT_CALLBACK *compare_func)(const T *a, const T *b); - - Vector& operator=(const Vector& p); + Vector& operator=(const Vector& p); iterator begin(); const_iterator begin() const; @@ -119,24 +853,23 @@ class Vector void insert(iterator, const T&); void erase(iterator); - T& front(); + T& front(); const T& front() const; - T& back(); + T& back(); const T& back() const; void push_front(const T&); void push_back(const T&); U32 push_front_unique(const T&); U32 push_back_unique(const T&); - S32 find_next( const T&, U32 start = 0 ) const; + S32 find_next(const T&, U32 start = 0) const; void pop_front(); void pop_back(); T& operator[](U32); const T& operator[](U32) const; - - T& operator[](S32 i) { return operator[](U32(i)); } - const T& operator[](S32 i ) const { return operator[](U32(i)); } + T& operator[](S32 i) { return operator[](U32(i)); } + const T& operator[](S32 i) const { return operator[](U32(i)); } void reserve(U32); U32 capacity() const; @@ -147,7 +880,7 @@ class Vector /// @{ U32 memSize() const; - T* address() const; + T* address() const; U32 setSize(U32); void increment(); void decrement(); @@ -162,528 +895,662 @@ class Vector void clear(); void compact(); void sort(compare_func f); - void fill( const T& value ); + void fill(const T& value); /// Finds the first matching element and erases it. /// @return Returns true if a match is found. - bool remove( const T& ); + bool remove(const T&); T& first(); T& last(); const T& first() const; const T& last() const; - void set(void * addr, U32 sz); + void set(void* addr, U32 sz); /// Appends the content of the vector to this one. - void merge( const Vector &p ); + void merge(const Vector& p); /// Appends the content of the array to the vector. /// /// @param addr A pointer to the first item of the array to merge. /// @param count The number of elements in the array to merge. /// - void merge( const T *addr, U32 count ); + void merge(const T* addr, U32 count); // Reverses the order of elements. void reverse(); /// @} + + // Modern/extra methods (from tVector.h, not in original, but keep at end) + void insert(const T* src, U32 count, U32 idx); + void fill(const T& value, U32 count); + void fill(const T& value, U32 count, U32 offset); + + template + void removeIf(Func pred); + + template + void forEach(Func fn); + + template + U32 countIf(Func fn) const; + + template + bool anyOf(Func fn) const; + + template + bool allOf(Func fn) const; + + void reverse(U32 offset, U32 count); + + template + void swap(Vector& other); + template + void swap(Vector& other, U32 thisOffset, U32 otherOffset, U32 count); + + template + bool compare(const Vector& other) const; + template + bool compare(const Vector& other, U32 thisOffset, U32 otherOffset, U32 count) const; + + void copy(const T* src); + + template + void assign(const SrcVec* src, U32 n = 0, U32 targOffset = 0, U32 srcOffset = 0); + + template + void moveFrom(SrcVec& src); + + // Returns the number of elements currently in the Vector. + inline U32 elementCount() const { return COUNT == 0 ? mState.elementCount() : COUNT; } + // Returns the number of elements allocated for the Vector. + inline U32 arraySize() const { return COUNT == 0 ? mState.arraySize() : COUNT; } + // Returns pointer to the Vector elements. + inline T* array() const { return mArray; } + + inline void setElementCount(U32 count) + { + AssertFatal(count >= 0, "setElementCount: can't set a negative element count!"); + AssertFatal(count <= arraySize(), "setElementCount: count exceeds array size!"); + mState.setElementCount(count); + + } + inline void setArraySize(U32 size) + { + AssertFatal(size >= 0, "setElementCount: can't set a negative array size!"); + mState.setArraySize(size); + AssertFatal(elementCount() <= arraySize(), "elementCount exceeds arraySize after setArraySize!"); + } }; class VectorFieldEngineExport { public: - template - static EngineFieldTable::Field getElementCountField() - { - typedef Vector ThisType; - return _FIELD(mElementCount, elementCount, 1, ""); - }; - template - static EngineFieldTable::Field getArraySizeField() - { - typedef Vector ThisType; - return _FIELD(mArraySize, arraySize, 1, ""); - }; template static EngineFieldTable::Field getArrayField() { typedef Vector ThisType; return _FIELD(mArray, array, 1, ""); }; + + // given EngineFieldTable::Field _sFields[] = { + // always stops at the first found NULL, + // keep strict order of most to least likely to be used fields, + // and put the least likely to be used ones at the end + // see _IMPLEMENT_STRUCT and _END_IMPLEMENT_STRUCT for further details + + template + static EngineFieldTable::Field getElementCountField() + { + typedef Vector ThisType; + if constexpr (VectorTraits::COUNT == 0) + { + return EngineFieldTable::Field{ + "elementCount", "", 1, + TYPE(((ThisType*)16)->mState.mElementCount), + FIELDOFFSET(mState.mElementCount) + }; + } + else + return { NULL }; //trash + }; + template + static EngineFieldTable::Field getArraySizeField() + { + typedef Vector ThisType; + + if constexpr (VectorTraits::COUNT == 0) + { + return EngineFieldTable::Field{ + "arraySize", "", 1, + TYPE(((ThisType*)16)->mState.mArraySize), + FIELDOFFSET(mState.mArraySize) + }; + } + else + return { NULL }; + }; }; -template inline Vector::~Vector() +template +inline Vector::~Vector() { + // Destroys all elements and deallocates memory. clear(); - dFree(mArray); + if constexpr (COUNT == 0) + { + if (mArray) + Allocator::deallocate(mArray); + mArray = NULL; + } + mArray = NULL; } - -template inline Vector::Vector(const U32 initialSize) +template +inline void Vector::zeroState() { #ifdef TORQUE_DEBUG_GUARD mFileAssociation = NULL; mLineAssociation = 0; #endif - - mArray = 0; - mElementCount = 0; - mArraySize = 0; - if(initialSize) - reserve(initialSize); + mArray = NULL; + setElementCount(0); + setArraySize(0); + if constexpr (COUNT != 0) + { + mArray = Allocator::allocate(COUNT); + construct(0, COUNT); + } } -template inline Vector::Vector(const U32 initialSize, - const char* fileName, - const U32 lineNum) +template +inline Vector::Vector(const U32 initialSize) { + zeroState(); + +#ifdef TORQUE_DEBUG_GUARD + mFileAssociation = NULL; + mLineAssociation = 0; +#endif + if (initialSize) + reserve(initialSize); +} +template +inline Vector::Vector(const char* fileName, + const U32 lineNum) +{ + zeroState(); #ifdef TORQUE_DEBUG_GUARD mFileAssociation = fileName; mLineAssociation = lineNum; #else -// TORQUE_UNUSED(fileName); -// TORQUE_UNUSED(lineNum); + // TORQUE_UNUSED(fileName); + // TORQUE_UNUSED(lineNum); #endif - - mArray = 0; - mElementCount = 0; - mArraySize = 0; - if(initialSize) - reserve(initialSize); } -template inline Vector::Vector(const char* fileName, - const U32 lineNum) -{ -#ifdef TORQUE_DEBUG_GUARD - mFileAssociation = fileName; - mLineAssociation = lineNum; -#else -// TORQUE_UNUSED(fileName); -// TORQUE_UNUSED(lineNum); -#endif - - mArray = 0; - mElementCount = 0; - mArraySize = 0; -} - -template inline Vector::Vector(const Vector& p) +template +inline Vector::Vector(const Vector& p) { + if (this == &p) + return; + zeroState(); #ifdef TORQUE_DEBUG_GUARD mFileAssociation = p.mFileAssociation; mLineAssociation = p.mLineAssociation; #endif - - mArray = 0; - resize(p.mElementCount); - construct(0, p.mElementCount, p.mArray); + if constexpr (COUNT == 0) + { + resize(p.elementCount()); + construct(0, p.elementCount(), p.mArray); + } + else + { + construct(0, COUNT, p.mArray); + } } - - #ifdef TORQUE_DEBUG_GUARD -template inline void Vector::setFileAssociation(const char* file, - const U32 line) +template +inline void Vector::setFileAssociation(const char* file, + const U32 line) { mFileAssociation = file; mLineAssociation = line; } #endif -template inline void Vector::destroy(U32 start, U32 end) // destroys from start to end-1 + +template +inline void Vector::destroy(U32 start, U32 end) // destroys from start to end-1 { // This check is a little generous as we can legitimately get (0,0) as // our parameters... so it won't detect every invalid case but it does // remain simple. - AssertFatal(start <= mElementCount && end <= mElementCount, "Vector::destroy - out of bounds start/end."); - + AssertFatal(start <= elementCount() && end <= elementCount(), "Vector::destroy - out of bounds start/end."); // destroys from start to end-1 - while(start < end) + while (start < end) destructInPlace(&mArray[start++]); } -template inline void Vector::construct(U32 start, U32 end) // destroys from start to end-1 +template +inline void Vector::construct(U32 start, U32 end) // allocates from start to end-1 { - AssertFatal(start <= mElementCount && end <= mElementCount, "Vector::construct - out of bounds start/end."); - while(start < end) + AssertFatal(start <= elementCount() && end <= elementCount(), "Vector::construct - out of bounds start/end."); + while (start < end) constructInPlace(&mArray[start++]); } -template inline void Vector::construct(U32 start, U32 end, const T* array) // destroys from start to end-1 +template +inline void Vector::construct(U32 start, U32 end, const T* array) // allocates from start to end-1 { - AssertFatal(start <= mElementCount && end <= mElementCount, "Vector::construct - out of bounds start/end."); - while(start < end) - { - constructInPlace(&mArray[start], &array[start]); - start++; - } + AssertFatal(start <= elementCount() && end <= elementCount(), "Vector::construct - out of bounds start/end."); + while (start < end) + { + constructInPlace(&mArray[start], &array[start]); + start++; + } } -template inline U32 Vector::memSize() const +template +inline U32 Vector::memSize() const // reserved size in bytes { return capacity() * sizeof(T); } -template inline T* Vector::address() const +template +inline T* Vector::address() const { return mArray; } -template inline U32 Vector::setSize(U32 size) +template +inline U32 Vector::setSize(U32 size) { - const U32 oldSize = mElementCount; - - if(size > mElementCount) + // Sets the number of elements in the vector. + // If increasing, constructs new elements. If decreasing, destructs removed elements. + U32 oldSize = elementCount(); + if constexpr (COUNT == 0) { - if (size > mArraySize) - resize(size); - - // Set count first so we are in a valid state for construct. - mElementCount = size; - construct(oldSize, size); + if (size > oldSize) + { + if (size > arraySize()) + resize(size); + setElementCount(size); + construct(oldSize, size); + } + else if (size < oldSize) + { + destroy(size, oldSize); + setElementCount(size); + } + // else, size == oldSize, do nothing + return elementCount(); } - else if(size < mElementCount) - { - destroy(size, oldSize); - mElementCount = size; - } - - return mElementCount; -} - -template inline void Vector::increment() -{ - if(mElementCount == mArraySize) - resize(mElementCount + 1); else - mElementCount++; - constructInPlace(&mArray[mElementCount - 1]); + { + // For fixed-size vectors, just return the count + return COUNT; + } } -template inline void Vector::decrement() +template +inline void Vector::increment() { - AssertFatal(mElementCount != 0, "Vector::decrement - cannot decrement zero-length vector."); - mElementCount--; - destructInPlace(&mArray[mElementCount]); + setSize(elementCount() + 1); } -template inline void Vector::increment(U32 delta) +template +inline void Vector::decrement() { - U32 count = mElementCount; - if ((mElementCount += delta) > mArraySize) - resize(mElementCount); - construct(count, mElementCount); + AssertFatal(elementCount(), "Vector::decrement - cannot decrement zero-length vector."); + setSize(elementCount() - 1); } -template inline void Vector::decrement(U32 delta) +template +inline void Vector::increment(U32 delta) { - AssertFatal(mElementCount != 0, "Vector::decrement - cannot decrement zero-length vector."); - - const U32 count = mElementCount; + setSize(elementCount() + delta); +} +template +inline void Vector::decrement(U32 delta) +{ + AssertFatal(elementCount(), "Vector::decrement - cannot decrement zero-length vector."); // Determine new count after decrement... - U32 newCount = mElementCount; - if (mElementCount > delta) - newCount -= delta; + U32 newCount = (elementCount() > delta) ? elementCount() - delta : 0; + setSize(newCount); +} + +template +inline void Vector::insert(U32 index) +{ + AssertFatal(index <= elementCount(), "Vector::insert - out of bounds index."); + if constexpr (COUNT == 0) + { + setSize(elementCount() + 1); + dMemmove(&mArray[index + 1], &mArray[index], dsize_t(elementCount() - index - 1) * sizeof(value_type)); + constructInPlace(&mArray[index]); + } else - newCount = 0; - - // Destruct removed items... - destroy(newCount, count); - - // Note new element count. - mElementCount = newCount; + { + /// DO RIGHT! + } } -template inline void Vector::insert(U32 index) +template +inline void Vector::insert(U32 index, const T& x) { - AssertFatal(index <= mElementCount, "Vector::insert - out of bounds index."); + if constexpr (COUNT == 0) + { - if(mElementCount == mArraySize) - resize(mElementCount + 1); + insert(index); + mArray[index] = x; + } else - mElementCount++; + { + if (index < COUNT) + { - dMemmove(&mArray[index + 1], - &mArray[index], - dsize_t(mElementCount - index - 1) * sizeof(value_type)); - - constructInPlace(&mArray[index]); + insert(index); + (*this)[index] = x; + } + } } -template inline void Vector::insert(U32 index,const T& x) +template +inline void Vector::erase(U32 index) { - insert(index); - mArray[index] = x; -} - -template inline void Vector::erase(U32 index) -{ - AssertFatal(index < mElementCount, "Vector::erase - out of bounds index!"); + AssertFatal(index < elementCount(), "Vector::erase - out of bounds index!"); destructInPlace(&mArray[index]); - if (index < (mElementCount - 1)) + if (index < (elementCount() - 1)) { dMemmove(&mArray[index], &mArray[index + 1], - dsize_t(mElementCount - index - 1) * sizeof(value_type)); + dsize_t(elementCount() - index - 1) * sizeof(value_type)); } - - mElementCount--; + setElementCount(elementCount() - 1); } -template inline bool Vector::remove( const T& x ) +template +inline bool Vector::remove(const T& x) { iterator i = begin(); while (i != end()) { if (*i == x) { - erase( i ); + erase(i); return true; } i++; } - return false; } -template inline void Vector::erase(U32 index, U32 count) +template +inline void Vector::erase(U32 index, U32 count) { - AssertFatal(index < mElementCount, "Vector::erase - out of bounds index!"); + AssertFatal(index < elementCount(), "Vector::erase - out of bounds index!"); AssertFatal(count > 0, "Vector::erase - count must be greater than zero!"); - AssertFatal(index+count <= mElementCount, "Vector::erase - out of bounds count!"); + AssertFatal(index + count <= elementCount(), "Vector::erase - out of bounds count!"); + destroy(index, index + count); - destroy( index, index+count ); - - dMemmove( &mArray[index], - &mArray[index + count], - dsize_t(mElementCount - index - count) * sizeof(value_type)); - - mElementCount -= count; + dMemmove(&mArray[index], + &mArray[index + count], + dsize_t(elementCount() - index - count) * sizeof(value_type)); + setElementCount(elementCount() - count); } -template inline void Vector::erase_fast(U32 index) +template +inline void Vector::erase_fast(U32 index) { - AssertFatal(index < mElementCount, "Vector::erase_fast - out of bounds index."); + AssertFatal(index < elementCount(), "Vector::erase_fast - out of bounds index."); // CAUTION: this operator does NOT maintain list order // Copy the last element into the deleted 'hole' and decrement the // size of the vector. destructInPlace(&mArray[index]); - if (index < (mElementCount - 1)) - dMemmove(&mArray[index], &mArray[mElementCount - 1], sizeof(value_type)); - mElementCount--; + if (index != elementCount() - 1) + mArray[index] = mArray[elementCount() - 1]; + setElementCount(elementCount() - 1); } -template inline bool Vector::contains(const T& x) const +template +inline bool Vector::contains(const T& x) const { - const_iterator i = begin(); - while (i != end()) - { - if (*i == x) - return true; - - i++; - } - - return false; + const_iterator i = begin(); + while (i != end()) + { + if (*i == x) + return true; + i++; + } + return false; } -template< class T > inline void Vector< T >::fill( const T& value ) +template +inline void Vector::fill(const T& value) { - for( U32 i = 0; i < size(); ++ i ) - mArray[ i ] = value; + for (U32 i = 0; i < size(); ++i) + mArray[i] = value; } -template inline T& Vector::first() +template +inline T& Vector::first() { - AssertFatal(mElementCount != 0, "Vector::first - Error, no first element of a zero sized array!"); + AssertFatal(elementCount(), "Vector::first - Error, no first element of a zero sized array!"); return mArray[0]; } -template inline const T& Vector::first() const +template +inline const T& Vector::first() const { - AssertFatal(mElementCount != 0, "Vector::first - Error, no first element of a zero sized array! (const)"); + AssertFatal(elementCount(), "Vector::first - Error, no first element of a zero sized array! (const)"); return mArray[0]; } -template inline T& Vector::last() +template +inline T& Vector::last() { - AssertFatal(mElementCount != 0, "Vector::last - Error, no last element of a zero sized array!"); - return mArray[mElementCount - 1]; + // Returns reference to last element. Asserts if empty. + AssertFatal(elementCount(), "Vector::last - Error, no last element of a zero sized array!"); + return mArray[elementCount() - 1]; } -template inline const T& Vector::last() const +template +inline const T& Vector::last() const { - AssertFatal(mElementCount != 0, "Vector::last - Error, no last element of a zero sized array! (const)"); - return mArray[mElementCount - 1]; + AssertFatal(elementCount(), "Vector::last - Error, no last element of a zero sized array! (const)"); + return mArray[elementCount() - 1]; } -template inline void Vector::clear() +template +inline void Vector::clear() { - destroy(0, mElementCount); - mElementCount = 0; + destroy(0, elementCount()); + setElementCount(0); } -template inline void Vector::compact() +template +inline void Vector::compact() { - resize(mElementCount); + resize(elementCount()); } -typedef S32 (QSORT_CALLBACK *qsort_compare_func)(const void *, const void *); +typedef S32(QSORT_CALLBACK* qsort_compare_func)(const void*, const void*); -template inline void Vector::sort(compare_func f) +template +inline void Vector::sort(compare_func f) { - qsort(address(), size(), sizeof(T), (qsort_compare_func) f); + qsort(address(), size(), sizeof(T), (qsort_compare_func)f); } //----------------------------------------------------------------------------- -template inline Vector& Vector::operator=(const Vector& p) + +template +inline Vector& Vector::operator=(const Vector& p) { - if(mElementCount > p.mElementCount) + if (this == &p) + return *this; + + if (elementCount() > p.elementCount()) { - destroy(p.mElementCount, mElementCount); + destroy(p.elementCount(), elementCount()); } - - U32 count = getMin( mElementCount, p.mElementCount ); + + U32 count = getMin(elementCount(), p.elementCount()); U32 i; - for( i=0; i < count; i++ ) + for (i = 0; i < count; i++) { mArray[i] = p.mArray[i]; } - - resize( p.mElementCount ); - - if( i < p.mElementCount ) + + resize(p.elementCount()); + + if (i < p.elementCount()) { - construct(i, p.mElementCount, p.mArray); + construct(i, p.elementCount(), p.mArray); } return *this; } -template inline typename Vector::iterator Vector::begin() +template +inline typename Vector::iterator Vector::begin() { return mArray; } -template inline typename Vector::const_iterator Vector::begin() const +template +inline typename Vector::const_iterator Vector::begin() const { return mArray; } -template inline typename Vector::iterator Vector::end() +template +inline typename Vector::iterator Vector::end() { - return mArray + mElementCount; + return mArray + elementCount(); } -template inline typename Vector::const_iterator Vector::end() const +template +inline typename Vector::const_iterator Vector::end() const { - return mArray +mElementCount; + return mArray + elementCount(); } -template inline S32 Vector::size() const +template +inline S32 Vector::size() const { - return (S32)mElementCount; + if constexpr (COUNT == 0) + return (S32)elementCount(); + else + return (S32)COUNT; } -template inline bool Vector::empty() const +template +inline bool Vector::empty() const { - return (mElementCount == 0); + return size() == 0; } -template inline void Vector::insert(iterator p,const T& x) +template +inline void Vector::insert(iterator p, const T& x) { - U32 index = (U32) (p - mArray); + U32 index = (U32)(p - mArray); insert(index); mArray[index] = x; } -template inline void Vector::erase(iterator q) +template +inline void Vector::erase(iterator q) { erase(U32(q - mArray)); } -template inline void Vector::erase_fast(iterator q) +template +inline void Vector::erase_fast(iterator q) { erase_fast(U32(q - mArray)); } -template inline T& Vector::front() +template +inline typename Vector::reference Vector::front() { return *begin(); } -template inline const T& Vector::front() const +template +inline typename Vector::const_reference Vector::front() const { return *begin(); } -template inline T& Vector::back() +template +inline typename Vector::reference Vector::back() { - AssertFatal(mElementCount != 0, "Vector::back - cannot access last element of zero-length vector."); - return *(end()-1); + AssertFatal(elementCount(), "Vector::back - cannot access last element of zero-length vector."); + return *(end() - 1); } -template inline const T& Vector::back() const +template +inline typename Vector::const_reference Vector::back() const { - AssertFatal(mElementCount != 0, "Vector::back - cannot access last element of zero-length vector."); - return *(end()-1); + AssertFatal(elementCount(), "Vector::back - cannot access last element of zero-length vector."); + return *(end() - 1); } -template inline void Vector::push_front(const T& x) +template +inline void Vector::push_front(const T& x) { insert(0); mArray[0] = x; } -template inline void Vector::push_back(const T& x) +template +inline void Vector::push_back(const T& x) { increment(); - mArray[mElementCount - 1] = x; + mArray[elementCount() - 1] = x; } -template inline U32 Vector::push_front_unique(const T& x) +template +inline U32 Vector::push_front_unique(const T& x) { S32 index = find_next(x); - if (index == -1) { index = 0; - insert(index); mArray[index] = x; } - return index; } -template inline U32 Vector::push_back_unique(const T& x) +template +inline U32 Vector::push_back_unique(const T& x) { S32 index = find_next(x); - if (index == -1) { increment(); - - index = mElementCount - 1; + index = elementCount() - 1; mArray[index] = x; } - return index; } -template inline S32 Vector::find_next( const T& x, U32 start ) const +template +inline S32 Vector::find_next(const T& x, U32 start) const { S32 index = -1; - - if (start < mElementCount) + if (start < elementCount()) { - for (U32 i = start; i < mElementCount; i++) + for (U32 i = start; i < elementCount(); i++) { if (mArray[i] == x) { @@ -692,119 +1559,396 @@ template inline S32 Vector::find_next( const T& x, U32 start ) const } } } - return index; } -template inline void Vector::pop_front() +template +inline void Vector::pop_front() { - AssertFatal(mElementCount != 0, "Vector::pop_front - cannot pop the front of a zero-length vector."); + AssertFatal(elementCount(), "Vector::pop_front - cannot pop the front of a zero-length vector."); erase(U32(0)); } -template inline void Vector::pop_back() +template +inline void Vector::pop_back() { - AssertFatal(mElementCount != 0, "Vector::pop_back - cannot pop the back of a zero-length vector."); + AssertFatal(elementCount(), "Vector::pop_back - cannot pop the back of a zero-length vector."); decrement(); } -template inline T& Vector::operator[](U32 index) +template +inline T& Vector::operator[](U32 index) { - AssertFatal(index < mElementCount, avar("Vector::operator[%i/%i] - out of bounds array access!", index, mElementCount)); + AssertFatal(mArray != NULL, "Vector::operator[] - mArray is NULL"); + AssertFatal(index < elementCount(), avar("Vector::operator[%i/%i] - out of bounds array access!", index,elementCount())); return mArray[index]; } -template inline const T& Vector::operator[](U32 index) const +template +inline const T& Vector::operator[](U32 index) const { - AssertFatal(index < mElementCount, avar("Vector::operator[%i/%i] - out of bounds array access!", index, mElementCount)); + AssertFatal(index < elementCount(), avar("Vector::operator[%i/%i] - out of bounds array access!", index,elementCount())); return mArray[index]; } -template inline void Vector::reserve(U32 size) +template +inline void Vector::reserve(U32 size) { - if (size <= mArraySize) - return; + if constexpr (COUNT == 0) + { + if (size <= arraySize()) + return; - const U32 ec = mElementCount; - if (resize(size)) - mElementCount = ec; + const U32 ec = elementCount(); + if (resize(size)) + setElementCount(ec); + } } -template inline U32 Vector::capacity() const +template +inline U32 Vector::capacity() const { - return mArraySize; + if constexpr (COUNT == 0) + return arraySize(); + else + return COUNT; } -template inline void Vector::set(void * addr, U32 sz) +template +inline void Vector::set(void* addr, U32 sz) { - if ( !addr ) + if (!addr) sz = 0; - setSize( sz ); + setSize(sz); - if ( addr && sz > 0 ) - dMemcpy(address(),addr,sz*sizeof(T)); + if (addr && sz > 0) + dMemcpy(address(), addr, sz * sizeof(T)); } //----------------------------------------------------------------------------- -template inline bool Vector::resize(U32 ecount) +template +inline bool Vector::resize(U32 ecount) { + bool ret = false; + U32 tArraySize = arraySize(); + U32 tElementCount = elementCount(); #ifdef TORQUE_DEBUG_GUARD - return VectorResize(&mArraySize, &mElementCount, (void**) &mArray, ecount, sizeof(T), - mFileAssociation, mLineAssociation); + ret = VectorResize(&tArraySize, &tElementCount, (void**)&mArray, ecount, sizeof(T), + mFileAssociation, mLineAssociation); #else - return VectorResize(&mArraySize, &mElementCount, (void**) &mArray, ecount, sizeof(T)); + ret = VectorResize(&tArraySize, &tElementCount, (void**)&mArray, ecount, sizeof(T)); #endif + setArraySize(tArraySize); + setElementCount(tElementCount); + // No construct/destroy here for COUNT == 0 + // For fixed-size vectors, we may still want to backfill + if constexpr (COUNT > 0) { + AssertFatal(tElementCount <= tArraySize, "Vector::resize - elementCount exceeds arraySize after resize"); + AssertFatal(mArray != NULL || ecount == 0, "Vector::resize - mArray should be valid or NULL if ecount == 0"); + for (U32 i = ecount; i < arraySize(); ++i) + constructInPlace(&mArray[i]); + } + else + { + if (ecount == 0) + mArray = NULL; + } + return ret; } -template inline void Vector::merge( const Vector &p ) +template +inline void Vector::merge(const Vector& p) { - if ( !p.size() ) + if (!p.size()) return; - - const U32 oldSize = mElementCount; + const U32 oldSize = elementCount(); const U32 newSize = oldSize + p.size(); - if ( newSize > mArraySize ) - resize( newSize ); - - T *dest = mArray + oldSize; - const T *src = p.mArray; - while ( dest < mArray + newSize ) - constructInPlace( dest++, src++ ); - - mElementCount = newSize; + if (newSize > arraySize()) + resize(newSize); + T* dest = mArray + oldSize; + const T* src = p.mArray; + while (dest < mArray + newSize) + constructInPlace(dest++, src++); + setElementCount(newSize); } -template inline void Vector::merge( const T *addr, U32 count ) +template +inline void Vector::merge(const T* addr, U32 count) { - const U32 oldSize = mElementCount; + const U32 oldSize = elementCount(); const U32 newSize = oldSize + count; - if ( newSize > mArraySize ) - resize( newSize ); - - T *dest = mArray + oldSize; - while ( dest < mArray + newSize ) - constructInPlace( dest++, addr++ ); - - mElementCount = newSize; + if (newSize > arraySize()) + resize(newSize); + T* dest = mArray + oldSize; + while (dest < mArray + newSize) + constructInPlace(dest++, addr++); + setElementCount(newSize); } -template inline void Vector::reverse() +template +inline void Vector::reverse() { - for (U32 i = 0, j = size(); (i != j) && (i != --j); ++i) - std::swap( mArray[ i ], mArray[ j ] ); + // Reverses the order of elements in the vector. + for (U32 i = 0, j = size(); (i != j) && (i != --j); ++i) + std::swap(mArray[i], mArray[j]); +} + +template +inline void Vector::insert(const T* src, U32 count, U32 idx) +{ + // Inserts a block of elements at index. + if (!src || count == 0) + return; + if constexpr (COUNT == 0) + { + auto* self = static_cast*>(this); + AssertFatal(idx <= self->elementCount(), "Vector::insert - out of bounds index."); + self->setSize(self->elementCount() + count); + U32 moveCount = self->elementCount() - idx - count; + if (moveCount > 0) + dMemmove(&self->mArray[idx + count], &self->mArray[idx], moveCount * sizeof(T)); + for (U32 i = 0; i < count; ++i) + self->mArray[idx + i] = src[i]; + } + else + { + AssertFatal(idx < COUNT, "Vector::insert - out of bounds index."); + U32 moveCount = COUNT - idx - count; + if (moveCount > 0) + dMemmove(&mArray[idx + count], &mArray[idx], moveCount * sizeof(T)); + for (U32 i = 0; i < count; ++i) + mArray[idx + i] = src[i]; + } +} + +template +inline void Vector::fill(const T& value, U32 count) +{ + // Fills first 'count' elements with value. + U32 n = (count > size()) ? size() : count; + for (U32 i = 0; i < n; ++i) + mArray[i] = value; +} + +template +inline void Vector::fill(const T& value, U32 count, U32 offset) +{ + // Fills 'count' elements starting at 'offset' with value. + U32 start = offset; + U32 n = (count == (U32)-1) ? (this->size() - start) : count; + U32 limit = this->size(); + if (start >= limit) + return; + if (start + n > limit) + n = limit - start; + for (U32 i = 0; i < n; ++i) + mArray[start + i] = value; +} + + +template +inline void Vector::copy(const T* src) +{ + // Copies data from an external array to the vector. + if (!src || size() == 0) + return; + for (U32 i = 0; i < size(); ++i) + mArray[i] = src[i]; +} + +template +template +inline void Vector::moveFrom(SrcVec& src) +{ + // Moves data from another vector, leaving it in a valid but unspecified state. + U32 n = mMin(COUNT, src.size()); + for (U32 i = 0; i < n; ++i) + mArray[i] = std::move(src.mArray[i]); + src.clear(); +} + + +//// review these! + +template +template +inline U32 Vector::countIf(Func fn) const +{ + // Counts elements matching a predicate. + U32 n = 0; + for (U32 i = 0; i < size(); ++i) + if (fn(mArray[i])) + ++n; + return n; +} + +template +template +inline bool Vector::anyOf(Func fn) const +{ + // Returns true if any element matches the predicate. + for (U32 i = 0; i < size(); ++i) + if (fn(mArray[i])) + return true; +} + +template +template +inline bool Vector::allOf(Func fn) const +{ + // Returns true if all elements match the predicate. + for (U32 i = 0; i < size(); ++i) + if (!fn(mArray[i])) + return false; + return true; +} + +// brief Assigns elements from another vector to this vector. +// // Copies up to n elements from the source vector (starting at srcOffset) to this vector (starting at targOffset). +// Handles overlapping ranges safely and resizes this vector if needed (for dynamic vectors). +// For fixed-size vectors, only copies up to the available capacity. +template +template +inline void Vector::assign(const SrcVec* src, U32 n, U32 targOffset, U32 srcOffset) +{ + U32 targLen = this->size(); + U32 srcLen = src->size(); + if (n == 0 || targOffset + n > targLen || srcOffset + n > srcLen) + n = mMin(targLen - targOffset, srcLen - srcOffset); + if constexpr (COUNT == 0) + if (targOffset + n > this->elementCount()) + this->setSize(targOffset + n); + if (n == 0) + return; + for (U32 i = 0; i < n; ++i) + this->mArray[targOffset + i] = src->mArray[srcOffset + i]; +} + + + + +template +inline void Vector::reverse(U32 offset, U32 count) +{ + // Reverses the order of a range of elements. + U32 limit = this->size(); + if (offset >= limit || count < 2) + return; + if (offset + count > limit) + count = limit - offset; + VectorHelpers::reverse(this, count, offset); +} + +template +template +void Vector::swap(Vector& other) +{ + // Swaps elements with another vector. + U32 n = mMin(this->size(), other.size()); + for (U32 i = 0; i < n; ++i) + std::swap(this->mArray[i], other.mArray[i]); +} + +template +template +void Vector::swap(Vector& other, U32 thisOffset, U32 otherOffset, U32 count) +{ + // Swaps a range of elements with another vector. + U32 thisLimit = this->size(); + U32 otherLimit = other.size(); + U32 n = count; + if (thisOffset >= thisLimit || otherOffset >= otherLimit) + return; + if (thisOffset + n > thisLimit) + n = thisLimit - thisOffset; + if (otherOffset + n > otherLimit) + n = otherLimit - otherOffset; + for (U32 i = 0; i < n; ++i) + std::swap(this->mArray[thisOffset + i], other.mArray[otherOffset + i]); +} + +template +template +inline void Vector::forEach(Func fn) +{ + // Applies a function to each element in the vector. + for (U32 i = 0; i < size(); ++i) + fn(mArray[i]); +} + +template +template +inline void Vector::removeIf(Func fn) +{ + // Removes elements matching a predicate. + U32 write = 0; + for (U32 read = 0; read < elementCount(); ++read) + { + if (!fn(mArray[read])) + { + if (write != read) + mArray[write] = std::move(mArray[read]); + ++write; + } + else + destructInPlace(&mArray[read]); + } + setElementCount(write); +} + +template +template +inline bool Vector::compare(const Vector& other) const +{ + U32 n = mMin(this->size(), other.size()); + return VectorHelpers::compare(this, &other, n, 0, 0); +} + +template +template +inline bool Vector::compare(const Vector& other, U32 thisOffset, U32 otherOffset, U32 count) const +{ + U32 thisLimit = this->size(); + U32 otherLimit = other.size(); + U32 n = count; + if (thisOffset >= thisLimit || otherOffset >= otherLimit) + return false; + if (thisOffset + n > thisLimit) + n = thisLimit - thisOffset; + if (otherOffset + n > otherLimit) + n = otherLimit - otherOffset; + return VectorHelpers::compare(this, &other, n, thisOffset, otherOffset); +} + +template +inline Vector::Vector(const U32 initialSize, const char* fileName, const U32 lineNum) +{ + zeroState(); +#ifdef TORQUE_DEBUG_GUARD + mFileAssociation = fileName; + mLineAssociation = lineNum; +#else + // TORQUE_UNUSED(fileName); + // TORQUE_UNUSED(lineNum); +#endif + if constexpr (COUNT == 0) + { + if (initialSize) + reserve(initialSize); + } } //----------------------------------------------------------------------------- /// Template for vectors of pointers. template -class VectorPtr : public Vector +class VectorPtr : public Vector { /// @deprecated Disallowed. VectorPtr(const VectorPtr&); // Disallowed - public: +public: VectorPtr(); VectorPtr(const char* fileName, const U32 lineNum); @@ -812,10 +1956,9 @@ class VectorPtr : public Vector /// @{ typedef T value_type; - typedef T& reference; + typedef T& reference; typedef const T& const_reference; - - typedef T* iterator; + typedef T* iterator; typedef const T* const_iterator; typedef U32 difference_type; typedef U32 size_type; @@ -825,18 +1968,18 @@ class VectorPtr : public Vector iterator end(); const_iterator end() const; - void insert(iterator,const T&); + void insert(iterator, const T&); void insert(S32 idx) { Parent::insert(idx); } void erase(iterator); - T& front(); + T& front(); const T& front() const; - T& back(); + T& back(); const T& back() const; void push_front(const T&); void push_back(const T&); - T& operator[](U32); + T& operator[](U32); const T& operator[](U32) const; /// @} @@ -844,9 +1987,9 @@ class VectorPtr : public Vector /// @name Extended interface /// @{ - typedef Vector Parent; - T& first(); - T& last(); + typedef Vector Parent; + T& first(); + T& last(); const T& first() const; const T& last() const; void erase_fast(U32); @@ -857,144 +2000,145 @@ class VectorPtr : public Vector //----------------------------------------------------------------------------- -template inline VectorPtr::VectorPtr() +template inline VectorPtr::VectorPtr() { // } -template inline VectorPtr::VectorPtr(const char* fileName, - const U32 lineNum) +template inline VectorPtr::VectorPtr(const char* fileName, + const U32 lineNum) : Vector(fileName, lineNum) { // } -template inline T& VectorPtr::first() +template inline T& VectorPtr::first() { return (T&)Parent::first(); } -template inline const T& VectorPtr::first() const +template inline const T& VectorPtr::first() const { return (const T)Parent::first(); } -template inline T& VectorPtr::last() +template inline T& VectorPtr::last() { return (T&)Parent::last(); } -template inline const T& VectorPtr::last() const +template inline const T& VectorPtr::last() const { return (const T&)Parent::last(); } -template inline typename VectorPtr::iterator VectorPtr::begin() +template inline typename VectorPtr::iterator VectorPtr::begin() { return (iterator)Parent::begin(); } -template inline typename VectorPtr::const_iterator VectorPtr::begin() const +template inline typename VectorPtr::const_iterator VectorPtr::begin() const { return (const_iterator)Parent::begin(); } -template inline typename VectorPtr::iterator VectorPtr::end() +template inline typename VectorPtr::iterator VectorPtr::end() { return (iterator)Parent::end(); } -template inline typename VectorPtr::const_iterator VectorPtr::end() const +template inline typename VectorPtr::const_iterator VectorPtr::end() const { return (const_iterator)Parent::end(); } -template inline void VectorPtr::insert(iterator i,const T& x) +template inline void VectorPtr::insert(iterator i, const T& x) { - Parent::insert( (Parent::iterator)i, (Parent::reference)x ); + Parent::insert((Parent::iterator)i, (Parent::reference)x); } -template inline void VectorPtr::erase(iterator i) +template inline void VectorPtr::erase(iterator i) { - Parent::erase( (Parent::iterator)i ); + Parent::erase((Parent::iterator)i); } -template inline void VectorPtr::erase_fast(U32 index) +template inline void VectorPtr::erase_fast(U32 index) { - AssertFatal(index < mElementCount, "VectorPtr::erase_fast - out of bounds index." ); + AssertFatal(index < elementCount(), "VectorPtr::erase_fast - out of bounds index."); // CAUTION: this operator does not maintain list order // Copy the last element into the deleted 'hole' and decrement the // size of the vector. - // Assert: index >= 0 && index < mElementCount - if (index < (mElementCount - 1)) - mArray[index] = mArray[mElementCount - 1]; + // Assert: index >= 0 && index < elementCount() + if (index < (elementCount() - 1)) + mArray[index] = mArray[elementCount() - 1]; decrement(); } -template inline void VectorPtr::erase_fast(iterator i) +template inline void VectorPtr::erase_fast(iterator i) { erase_fast(U32(i - iterator(mArray))); } -template inline T& VectorPtr::front() +template inline T& VectorPtr::front() { return *begin(); } -template inline const T& VectorPtr::front() const +template inline const T& VectorPtr::front() const { return *begin(); } -template inline T& VectorPtr::back() +template inline T& VectorPtr::back() { - AssertFatal(mElementCount != 0, "Vector::back - cannot access last element of zero-length vector."); - return *(end()-1); + AssertFatal(elementCount(), "Vector::back - cannot access last element of zero-length vector."); + return *(end() - 1); } -template inline const T& VectorPtr::back() const +template inline const T& VectorPtr::back() const { - AssertFatal(mElementCount != 0, "Vector::back - cannot access last element of zero-length vector."); - return *(end()-1); + AssertFatal(elementCount(), "Vector::back - cannot access last element of zero-length vector."); + return *(end() - 1); } -template inline void VectorPtr::push_front(const T& x) +template inline void VectorPtr::push_front(const T& x) { Parent::push_front((Parent::const_reference)x); } -template inline void VectorPtr::push_back(const T& x) +template inline void VectorPtr::push_back(const T& x) { Parent::push_back((Parent::const_reference)x); } -template inline T& VectorPtr::operator[](U32 index) +template inline T& VectorPtr::operator[](U32 index) { return (T&)Parent::operator[](index); } -template inline const T& VectorPtr::operator[](U32 index) const +template inline const T& VectorPtr::operator[](U32 index) const { return (const T&)Parent::operator[](index); } - //------------------------------------------------------------------------------ - +template struct CustomAllocator; +template class Vector; template class VectorSet : public Vector { public: void insert(T dat) { - if(find(this->begin(), this->end(), dat) == this->end()) + if (VectorHelpers::find(this, -1, dat) == -1) push_back(dat); } }; + + // Include vector specializations #ifndef _TVECTORSPEC_H_ #include "core/util/tVectorSpecializations.h" #endif #endif //_TVECTOR_H_ - diff --git a/Engine/source/core/util/tVectorSpecializations.h b/Engine/source/core/util/tVectorSpecializations.h index 8f9970fc6..f1bb179c3 100644 --- a/Engine/source/core/util/tVectorSpecializations.h +++ b/Engine/source/core/util/tVectorSpecializations.h @@ -27,6 +27,8 @@ #include "core/util/tVector.h" #endif +template struct CustomAllocator; +template class Vector; // Not exactly a specialization, just a vector to use when speed is a concern template class FastVector : public Vector diff --git a/Engine/source/lighting/lightingInterfaces.h b/Engine/source/lighting/lightingInterfaces.h index dc4db77a4..fead951eb 100644 --- a/Engine/source/lighting/lightingInterfaces.h +++ b/Engine/source/lighting/lightingInterfaces.h @@ -34,7 +34,8 @@ class ObjectProxy; class ObjectProxyList; class SceneLightingInterface; -template class Vector; +template struct CustomAllocator; +template class Vector; typedef Vector SceneLightingInterfaces; // List of available "systems" that the lighting kit can use diff --git a/Engine/source/platform/platform.h b/Engine/source/platform/platform.h index acb929a9f..f7c9c04fd 100644 --- a/Engine/source/platform/platform.h +++ b/Engine/source/platform/platform.h @@ -110,7 +110,7 @@ enum DriveType // Some forward declares for later. class Point2I; -template class Vector; + template class Signal; struct InputEventInfo; @@ -243,10 +243,10 @@ namespace Platform bool isFullPath(const char *path); StringTableEntry makeRelativePathName(const char *path, const char *to); - String stripExtension( String fileName, Vector< String >& validExtensions ); + String stripExtension( String fileName, Vector >& validExtensions ); - bool dumpPath(const char *in_pBasePath, Vector& out_rFileVector, S32 recurseDepth = -1); - bool dumpDirectories( const char *path, Vector &directoryVector, S32 depth = 0, bool noBasePath = false ); + bool dumpPath(const char *in_pBasePath, Vector >& out_rFileVector, S32 recurseDepth = -1); + bool dumpDirectories( const char *path, Vector >&directoryVector, S32 depth = 0, bool noBasePath = false ); bool hasSubDirectory( const char *pPath ); bool getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime); bool isFile(const char *pFilePath); @@ -283,8 +283,8 @@ namespace Platform extern struct VolumeInformation *PVolumeInformation; // Volume functions. - void getVolumeNamesList( Vector& out_rNameVector, bool bOnlyFixedDrives = false ); - void getVolumeInformationList( Vector& out_rVolumeInfoVector, bool bOnlyFixedDrives = false ); + void getVolumeNamesList(Vector >& out_rNameVector, bool bOnlyFixedDrives = false ); + void getVolumeInformationList(Vector >& out_rVolumeInfoVector, bool bOnlyFixedDrives = false ); struct SystemInfo_struct { diff --git a/Engine/source/platform/platformFont.h b/Engine/source/platform/platformFont.h index 63a47d95a..ebc7a229a 100644 --- a/Engine/source/platform/platformFont.h +++ b/Engine/source/platform/platformFont.h @@ -89,7 +89,7 @@ public: /// /// @todo Rethink this so we don't have a private public. virtual bool create( const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET ) = 0; - static void enumeratePlatformFonts( Vector& fonts, UTF16* fontFamily = NULL ); + static void enumeratePlatformFonts( Vector>& fonts, UTF16* fontFamily = NULL ); }; extern PlatformFont *createPlatformFont(const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET); diff --git a/Engine/source/scene/sceneContainer.cpp b/Engine/source/scene/sceneContainer.cpp index 81a437392..1503befac 100644 --- a/Engine/source/scene/sceneContainer.cpp +++ b/Engine/source/scene/sceneContainer.cpp @@ -443,7 +443,7 @@ bool SceneContainer::removeObject(SceneObject* obj) if ( obj->getTypeMask() & ( WaterObjectType | PhysicalZoneObjectType ) ) { iter = std::find( mWaterAndZones.begin(), mWaterAndZones.end(), obj ); - if( iter != mTerrains.end() ) + if( iter != mWaterAndZones.end() ) mWaterAndZones.erase_fast(iter); } diff --git a/Engine/source/scene/sceneObject.cpp b/Engine/source/scene/sceneObject.cpp index b73326ae5..3ce8449e3 100644 --- a/Engine/source/scene/sceneObject.cpp +++ b/Engine/source/scene/sceneObject.cpp @@ -336,13 +336,14 @@ void SceneObject::onRemove() { smSceneObjectRemove.trigger(this); + if (getParent() != NULL) attachToParent(NULL); unmount(); plUnlink(); + if (mContainer) + mContainer->removeObject(this); + Parent::onRemove(); -// PATHSHAPE - if ( getParent() != NULL) attachToParent( NULL); -// PATHSHAPE END } //----------------------------------------------------------------------------- @@ -395,7 +396,7 @@ void SceneObject::inspectPostApply() //----------------------------------------------------------------------------- void SceneObject::setGlobalBounds() -{ +{ mGlobalBounds = true; mObjBox.minExtents.set( -1e10, -1e10, -1e10 ); mObjBox.maxExtents.set( 1e10, 1e10, 1e10 ); @@ -434,6 +435,7 @@ void SceneObject::setTransform( const MatrixF& mat ) mSceneManager->notifyObjectDirty( this ); setRenderTransform( mat ); + setMountTransforms(); } //----------------------------------------------------------------------------- @@ -1351,6 +1353,19 @@ void SceneObject::getRenderMountTransform( F32 delta, S32 index, const MatrixF & outMat->mul( mRenderObjToWorld, mountTransform ); } +void SceneObject::setMountTransforms() +{ + // Update all objects mounted to us + for (SceneObject* mounted = mMount.list; mounted; mounted = mounted->mMount.link) + { + // Compute mounted object's new world transform + MatrixF worldXfm; + getMountTransform(mounted->mMount.node, mounted->mMount.xfm, &worldXfm); + mounted->setTransform(worldXfm); + } +} + + //============================================================================= // Console API. //============================================================================= @@ -1666,7 +1681,8 @@ void SceneObject::moveRender(const Point3F &delta) void SceneObject::PerformUpdatesForChildren(MatrixF mat){ for (U32 i=0; i < getNumChildren(); i++) { SceneObject *o = getChild(i); - o->updateChildTransform(); //update the position of the child object + if (!o->isMounted()) + o->updateChildTransform(); //update the position of the child object } } diff --git a/Engine/source/scene/sceneObject.h b/Engine/source/scene/sceneObject.h index e12f1a0f8..9223c674c 100644 --- a/Engine/source/scene/sceneObject.h +++ b/Engine/source/scene/sceneObject.h @@ -674,6 +674,7 @@ class SceneObject : public NetObject, public ProcessObject void resolveMountPID(); + void setMountTransforms(); /// @} /// @name Sound