#pragma once #include #include "basis/seadNew.h" #include "basis/seadRawPrint.h" #include "container/seadFreeList.h" #include "container/seadPtrArray.h" namespace sead { /// An ObjArray is a container that allocates elements using a FreeList and also keeps an array of /// pointers for fast access to each element. template class ObjArray : public PtrArrayImpl { public: ObjArray() = default; ObjArray(s32 max_num, void* buf) { setBuffer(max_num, buf); } void allocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*)) { SEAD_ASSERT(mPtrs == nullptr); if (capacity < 1) { SEAD_ASSERT_MSG(false, "capacity[%d] must be larger than zero", capacity); return; } setBuffer(capacity, new (heap, alignment, std::nothrow) u8[calculateWorkBufferSize(capacity)]); } bool tryAllocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*)) { SEAD_ASSERT(mPtrs == nullptr); if (capacity < 1) { SEAD_ASSERT_MSG(false, "capacity[%d] must be larger than zero", capacity); return false; } auto* buf = new (heap, alignment, std::nothrow) u8[calculateWorkBufferSize(capacity)]; if (!buf) return false; setBuffer(capacity, buf); return true; } void setBuffer(s32 max_num, void* buf) { if (!buf) { SEAD_ASSERT_MSG(false, "buf is null"); return; } mFreeList.setWork(buf, ElementSize, max_num); PtrArrayImpl::setBuffer(max_num, reinterpret_cast(buf) + ElementSize * max_num); } void freeBuffer() { if (!isBufferReady()) return; clear(); if (mFreeList.work()) delete[] static_cast(mFreeList.work()); mFreeList.reset(); mPtrs = nullptr; mPtrNumMax = 0; } T* at(s32 pos) const { return static_cast(PtrArrayImpl::at(pos)); } T* unsafeAt(s32 pos) const { return static_cast(PtrArrayImpl::unsafeAt(pos)); } T* operator()(s32 pos) const { return unsafeAt(pos); } T* operator[](s32 pos) const { return at(pos); } // XXX: Does this use at()? T* front() const { return at(0); } T* back() const { return at(mPtrNum - 1); } void pushBack(const T& item) { if (isFull()) SEAD_ASSERT_MSG(false, "buffer full."); else PtrArrayImpl::pushBack(alloc(item)); } template T* emplaceBack(Args&&... args) { if (isFull()) { SEAD_ASSERT_MSG(false, "buffer full."); return nullptr; } T* item = new (mFreeList.alloc()) T(std::forward(args)...); PtrArrayImpl::pushBack(item); return item; } void insert(s32 pos, const T& item) { PtrArrayImpl::insert(pos, alloc(item)); } void erase(int index) { erase(index, 1); } void erase(int index, int count) { if (index + count <= size()) { for (int i = index; i < index + count; ++i) { auto* ptr = unsafeAt(i); ptr->~T(); mFreeList.free(ptr); } } PtrArrayImpl::erase(index, count); } void clear() { for (s32 i = 0; i < mPtrNum; ++i) { auto* ptr = unsafeAt(i); ptr->~T(); mFreeList.free(ptr); } mPtrNum = 0; } using CompareCallback = s32 (*)(const T*, const T*); void sort() { sort(compareT); } void sort(CompareCallback cmp) { PtrArrayImpl::sort_(cmp); } void heapSort() { heapSort(compareT); } void heapSort(CompareCallback cmp) { PtrArrayImpl::heapSort_(cmp); } bool equal(const ObjArray& other, CompareCallback cmp) const { return PtrArrayImpl::equal(other, cmp); } s32 compare(const ObjArray& other, CompareCallback cmp) const { return PtrArrayImpl::compare(other, cmp); } s32 binarySearch(const T* ptr) const { return PtrArrayImpl::binarySearch(ptr, compareT); } s32 binarySearch(const T* ptr, CompareCallback cmp) const { return PtrArrayImpl::binarySearch(ptr, cmp); } bool operator==(const ObjArray& other) const { return equal(other, compareT); } bool operator!=(const ObjArray& other) const { return !(*this == other); } bool operator<(const ObjArray& other) const { return compare(other) < 0; } bool operator<=(const ObjArray& other) const { return compare(other) <= 0; } bool operator>(const ObjArray& other) const { return compare(other) > 0; } bool operator>=(const ObjArray& other) const { return compare(other) >= 0; } void uniq() { PtrArrayImpl::uniq(compareT); } void uniq(CompareCallback cmp) { PtrArrayImpl::uniq(cmp); } class iterator { public: iterator(T* const* pptr) : mPPtr{pptr} {} bool operator==(const iterator& other) const { return mPPtr == other.mPPtr; } bool operator!=(const iterator& other) const { return !(*this == other); } iterator& operator++() { ++mPPtr; return *this; } T& operator*() const { return **mPPtr; } T* operator->() const { return *mPPtr; } private: T* const* mPPtr; }; iterator begin() const { return iterator(data()); } iterator end() const { return iterator(data() + mPtrNum); } class constIterator { public: constIterator(const T* const* pptr) : mPPtr{pptr} {} bool operator==(const constIterator& other) const { return mPPtr == other.mPPtr; } bool operator!=(const constIterator& other) const { return !(*this == other); } constIterator& operator++() { ++mPPtr; return *this; } const T& operator*() const { return **mPPtr; } const T* operator->() const { return *mPPtr; } private: const T* const* mPPtr; }; constIterator constBegin() const { return constIterator(data()); } constIterator constEnd() const { return constIterator(data() + mPtrNum); } T** data() const { return reinterpret_cast(mPtrs); } private: union Node { void* next_node; T elem; }; public: static constexpr size_t ElementSize = sizeof(Node); static constexpr size_t calculateWorkBufferSize(size_t n) { return n * (ElementSize + sizeof(T*)); } protected: T* alloc(const T& item) { void* storage = mFreeList.alloc(); if (!storage) return nullptr; return new (storage) T(item); } static int compareT(const void* a_, const void* b_) { const T* a = static_cast(a_); const T* b = static_cast(b_); if (*a < *b) return -1; if (*b < *a) return 1; return 0; } sead::FreeList mFreeList; }; template class FixedObjArray : public ObjArray { public: FixedObjArray() : ObjArray(N, &mWork) {} // These do not make sense for a *fixed* array. void setBuffer(s32 ptrNumMax, void* buf) = delete; void allocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)) = delete; bool tryAllocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)) = delete; void freeBuffer() = delete; private: std::aligned_storage_t::calculateWorkBufferSize(N), std::max(alignof(T), alignof(T*))> mWork; }; } // namespace sead