#pragma once #include #include #include "ContainerPolicies.h" template class CSimpleArrayStandardCompareHelper { public: int Compare(const T& t1, const T& t2) const { return t2 == t1 ? 0 : t2 < t1 ? 1 : -1; } }; class CSimpleArrayCaseInsensitiveOrdinalStringCompareHelper { public: int Compare(const WCHAR* psz1, const WCHAR* psz2) const { return CompareStringOrdinal(psz1, -1, psz2, -1, TRUE) - CSTR_EQUAL; } }; class CSimpleArrayUserDefaultLocaleCaseInsensitiveCompareHelper { public: int Compare(const WCHAR* psz1, const WCHAR* psz2) const { return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, psz1, -1, psz2, -1) - CSTR_EQUAL; } }; template class CSimpleArrayStandardMergeHelper { }; template < typename T, typename CompareHelper = CSimpleArrayStandardCompareHelper > class CTSimpleFixedArray { public: T* _parray; size_t _celem; CTSimpleFixedArray() : _parray(nullptr) , _celem(0) { } CTSimpleFixedArray(T* const parray, size_t celem) : _parray(parray) , _celem(celem) { } void SetData(T* const parray, size_t celem) { _parray = parray; _celem = celem; } size_t GetSize() const { return _celem; } T& operator[](size_t iElem) { return _parray[iElem]; } const T& operator[](size_t iElem) const { return _parray[iElem]; } HRESULT GetAt(size_t iElem, T& tOut) const { HRESULT hr = TYPE_E_OUTOFBOUNDS; if (iElem < _celem) { tOut = _parray[iElem]; hr = S_OK; } return hr; } T* GetData() const { return _parray; } T* begin() { return _parray; } T* begin() const { return _parray; } T* end() { return _parray + _celem; } T* end() const { return _parray + _celem; } HRESULT Find(const T& t, size_t* piElem, size_t iStartAt = 0) const { return FindEx(CompareHelper(), t, piElem, iStartAt); } template HRESULT FindEx(const Comparer& tcompare, const T& t, size_t* piElem, size_t iStartAt = 0) const { *piElem = 0; for (size_t i = iStartAt; i < _celem; ++i) { if (tcompare.Compare(_parray[i], t) == 0) { *piElem = i; return S_OK; } } return TYPE_E_ELEMENTNOTFOUND; } HRESULT BinarySearch(const T& t, size_t* piElem) const { return BinarySearchEx(CompareHelper(), t, piElem); } template HRESULT BinarySearchEx(const Comparer& tcompare, const T& t, size_t* piElem) const { *piElem = 0; HRESULT hr = TYPE_E_ELEMENTNOTFOUND; if (_celem != 0) { hr = S_OK; size_t iLow = 0; size_t iHigh = _celem - 1; while (true) { size_t iMid = (iLow + iHigh) / 2; int compare = tcompare.Compare(_parray[iMid], t); if (compare > 0) { if (iMid != 0) { iHigh = iMid - 1; } else { hr = TYPE_E_ELEMENTNOTFOUND; } } else if (compare < 0) { iLow = iMid + 1; } else { for (; iMid != 0; --iMid) { if (tcompare.Compare(_parray[iMid - 1], t) != 0) break; } *piElem = iMid; break; } if (iHigh < iLow) hr = TYPE_E_ELEMENTNOTFOUND; if (FAILED(hr)) { *piElem = compare < 0 ? iLow : iMid; break; } } } return hr; } template void ForEach(const TCallback& callback) const // @MOD Pass callback by reference { for (size_t iElement = 0; iElement < _celem; ++iElement) { callback(iElement, _parray[iElement]); } } }; template < typename T, size_t MaxSize, typename Allocator, typename CompareHelper = CSimpleArrayStandardCompareHelper, typename MergeHelper = CSimpleArrayStandardMergeHelper > class CTSimpleArray : public CTSimpleFixedArray { public: T* _parrayT; size_t _celemCapacity; CTSimpleArray() : CTSimpleFixedArray() , _parrayT(nullptr) , _celemCapacity(0) { } ~CTSimpleArray() { RemoveAll(); } HRESULT Add(const T& t, size_t* piElemInsertedAt = nullptr) { return _Add(t, piElemInsertedAt); } HRESULT Add(T&& t, size_t* piElemInsertedAt = nullptr) { return _Add(std::move(t), piElemInsertedAt); } HRESULT AddSorted(const T& t, size_t* piElemInsertedAt = nullptr) { return _AddSorted(t, piElemInsertedAt); } HRESULT AddSorted(T&& t, size_t* piElemInsertedAt = nullptr) { return _AddSorted(std::move(t), piElemInsertedAt); } template HRESULT AddSortedEx(const Comparer& tcompare, const T& t, size_t* piElemInsertedAt = nullptr) { return _AddSortedEx(tcompare, t, piElemInsertedAt); } template HRESULT AddSortedEx(const Comparer& tcompare, T&& t, size_t* piElemInsertedAt = nullptr) { return _AddSortedEx(tcompare, std::move(t), piElemInsertedAt); } HRESULT InsertAt(const T& t, size_t iElem) { return _InsertAt(t, iElem); } HRESULT InsertAt(T&& t, size_t iElem) { return _InsertAt(std::move(t), iElem); } HRESULT SetAtIndex(size_t iElem, const T& t) { return _SetAtIndex(iElem, t); } HRESULT SetAtIndex(size_t iElem, T&& t) { return _SetAtIndex(iElem, std::move(t)); } HRESULT Remove(const T& t, size_t* piElemRemovedAt = nullptr) { if (piElemRemovedAt) *piElemRemovedAt = 0; size_t iElem; HRESULT hr = this->Find(t, &iElem); if (SUCCEEDED(hr)) { hr = RemoveAt(iElem); if (SUCCEEDED(hr) && piElemRemovedAt) { *piElemRemovedAt = iElem; } } return hr; } HRESULT RemoveAt(size_t iElem) { if (iElem >= this->_celem) return TYPE_E_OUTOFBOUNDS; if constexpr (!std::is_trivially_destructible_v) this->_parray[iElem].~T(); if (iElem != this->_celem - 1) memmove(std::addressof(this->_parray[iElem]), std::addressof(this->_parray[iElem + 1]), sizeof(T) * (this->_celem - iElem - 1)); --this->_celem; return S_OK; } void RemoveAll() { if (this->_parray) { if constexpr (!std::is_trivially_destructible_v) { for (size_t i = 0; i < this->_celem; ++i) this->_parray[i].~T(); } Allocator::Destroy(this->_parray); this->_parray = nullptr; } this->_celem = 0; _celemCapacity = 0; } void TransferData(CTSimpleArray* other) { RemoveAll(); this->_parray = other->_parray; this->_celem = other->_celem; this->_parrayT = other->_parrayT; this->_celemCapacity = other->_celemCapacity; other->_parray = nullptr; other->_celem = 0; other->_parrayT = nullptr; other->_celemCapacity = 0; } void Attach(T* parray, size_t celem) { RemoveAll(); this->_parray = parray; this->_celem = celem; _celemCapacity = celem; } size_t GetCapacity() const { return _celemCapacity; } HRESULT Sort() { return SortEx(CompareHelper()); } template HRESULT SortEx(const Comparer& tcompare) { HRESULT hr = S_OK; if (this->_celem > 1) { _parrayT = nullptr; hr = Allocator::ReallocArray(nullptr, this->_celem / 2, &_parrayT); if (SUCCEEDED(hr)) { _MergeSort(tcompare, 0, this->_celem); Allocator::Destroy(_parrayT); _parrayT = nullptr; } } return hr; } HRESULT _EnsureCapacity(size_t celemCapacityDesired, size_t celemMaxCapacity = 4096) { HRESULT hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); if (celemCapacityDesired > MaxSize) return hr; // If we have enough capacity, we're done hr = S_OK; size_t celemCapacityCur = _celemCapacity; if (celemCapacityDesired <= celemCapacityCur) return hr; // Double the capacity size_t celemCapacityT; hr = SizeTMult(celemCapacityCur, 2, &celemCapacityT); if (FAILED(hr)) return hr; // Make sure we don't grow too much celemCapacityT = celemCapacityT - celemCapacityCur > celemMaxCapacity ? celemCapacityCur + celemMaxCapacity : celemCapacityT; // Cap at desired capacity and max capacity celemCapacityT = celemCapacityDesired > celemCapacityT || celemCapacityT <= MaxSize ? max(celemCapacityDesired, celemCapacityT) : MaxSize; // Realloc T* pvArrayT; hr = Allocator::ReallocArray(this->_parray, celemCapacityT, &pvArrayT); if (FAILED(hr)) return hr; _celemCapacity = celemCapacityT; this->_parray = pvArrayT; return hr; } HRESULT _MakeRoomAt(size_t iElem) { HRESULT hr = S_OK; size_t cElemGrowTo = max(this->_celem, iElem) + 1; if (cElemGrowTo > _celemCapacity) { hr = _EnsureCapacity(cElemGrowTo); } if (SUCCEEDED(hr)) { if (iElem < this->_celem) memmove(std::addressof(this->_parray[iElem + 1]), std::addressof(this->_parray[iElem]), sizeof(T) * (this->_celem - iElem)); this->_celem = cElemGrowTo; } return hr; } template void _InternalSetAtIndex(size_t iElem, ArgType&& t) { T* newPos = std::addressof(this->_parray[iElem]); if (newPos) new(newPos) T(std::forward(t)); } template HRESULT _Add(ArgType&& t, size_t* piElemInsertedAt) { if (piElemInsertedAt) *piElemInsertedAt = 0; HRESULT hr = S_OK; if (this->_celem == _celemCapacity) { hr = _EnsureCapacity(_celemCapacity + 1); } if (SUCCEEDED(hr)) { _InternalSetAtIndex(this->_celem++, std::forward(t)); if (piElemInsertedAt) *piElemInsertedAt = this->_celem - 1; } return hr; } template HRESULT _AddSorted(ArgType&& t, size_t* piElemInsertedAt) { if (piElemInsertedAt) *piElemInsertedAt = 0; size_t iElemInsertAt; this->BinarySearch(t, &iElemInsertAt); return _InsertAt(std::forward(t), iElemInsertAt); } template HRESULT _AddSortedEx(const Comparer& tcompare, ArgType&& t, size_t* piElemInsertedAt) { if (piElemInsertedAt) *piElemInsertedAt = 0; size_t iElemInsertAt; this->BinarySearchEx(tcompare, t, &iElemInsertAt); return _InsertAt(std::forward(t), iElemInsertAt); } template HRESULT _InsertAt(ArgType&& t, size_t iElem) { HRESULT hr = _MakeRoomAt(iElem); if (SUCCEEDED(hr)) { _InternalSetAtIndex(iElem, std::forward(t)); } return hr; } template HRESULT _SetAtIndex(size_t iElem, ArgType&& t) { HRESULT hr = TYPE_E_OUTOFBOUNDS; if (iElem < this->_celem) { _InternalSetAtIndex(iElem, std::forward(t)); hr = S_OK; } return hr; } template void _MergeThem(const Comparer& tcompare, size_t iFirst, size_t cElems) { size_t cHalf = cElems / 2; T* parraySrc = &this->_parray[iFirst]; memcpy(_parrayT, parraySrc, sizeof(T) * cHalf); size_t iIn1 = 0; size_t iIn2 = cHalf; size_t iOut = 0; bool fDone = false; while (!fDone) { if (tcompare.Compare(_parrayT[iIn1], parraySrc[iIn2]) > 0) { memmove(&parraySrc[iOut], &parraySrc[iIn2], sizeof(T)); ++iOut; if (++iIn2 == cElems) { memcpy(&parraySrc[iOut], &_parrayT[iIn1], sizeof(T) * (cElems - iOut)); fDone = true; } } else { memmove(&parraySrc[iOut], &_parrayT[iIn1], sizeof(T)); ++iOut; if (++iIn1 == cHalf) { fDone = true; } } } } template void _MergeSort(const Comparer& tcompare, size_t iFirst, size_t cElems) { if (cElems == 1) return; if (cElems == 2) { if (tcompare.Compare(this->_parray[iFirst], this->_parray[iFirst + 1]) > 0) { memmove(_parrayT, &this->_parray[iFirst], sizeof(T)); memmove(&this->_parray[iFirst], &this->_parray[iFirst + 1], sizeof(T)); memmove(&this->_parray[iFirst + 1], _parrayT, sizeof(T)); } } else { size_t cHalf = cElems >> 1; _MergeSort(tcompare, iFirst, cHalf); _MergeSort(tcompare, iFirst + cHalf, cElems - cHalf); _MergeThem(tcompare, iFirst, cElems); } } }; template < typename T, size_t MaxSize = UINT_MAX - 1, typename CompareHelper = CSimpleArrayStandardCompareHelper > class CCoSimpleArray : public CTSimpleArray, CompareHelper> { public: CCoSimpleArray() { } CCoSimpleArray(CCoSimpleArray&& other) noexcept { this->TransferData(&other); } CCoSimpleArray& operator=(CCoSimpleArray&& other) noexcept { if (this != &other) { this->TransferData(&other); } return *this; } }; template < typename T, size_t MaxSize = UINT_MAX - 1, typename CompareHelper = CSimpleArrayStandardCompareHelper > class CLocalSimpleArray : public CTSimpleArray, CompareHelper> { }; template < typename T, typename ElementAllocator, typename CompareHelper = CSimpleArrayStandardCompareHelper > class CSimplePointerArray : public CCoSimpleArray { public: ~CSimplePointerArray() { RemoveAndReleaseAll(); } HRESULT RemoveAndReleaseAt(size_t iElem) { T* pT; HRESULT hr = this->GetAt(iElem, pT); if (SUCCEEDED(hr)) { hr = this->RemoveAt(iElem); if (SUCCEEDED(hr)) { ElementAllocator::Destroy(pT); } } return hr; } void RemoveAndReleaseAll() { for (size_t i = 0; i < this->_celem; ++i) { ElementAllocator::Destroy(this->_parray[i]); } this->RemoveAll(); } }; template < typename T, typename CompareHelper = CSimpleArrayStandardCompareHelper > class CSimplePointerArrayNewMem : public CSimplePointerArray { }; template < typename T, typename CompareHelper = CSimpleArrayStandardCompareHelper > class CSimplePointerArrayCoTaskMem : public CSimplePointerArray, CompareHelper> { }; template < typename T, typename CompareHelper = CSimpleArrayStandardCompareHelper > class CSimplePointerArrayLocalMem : public CSimplePointerArray, CompareHelper> { }; template class CSimplePointerArrayRelease : public CSimplePointerArray> { }; template class CSimpleStringArrayBase : public CSimplePointerArrayCoTaskMem { public: HRESULT Find(const WCHAR* pszItem, size_t* piElem, size_t iStartAt = 0) const { return CSimplePointerArrayCoTaskMem::Find(const_cast(pszItem), piElem, iStartAt); } HRESULT BinarySearch(const WCHAR* pszItem, size_t* piElem) const { return CSimplePointerArrayCoTaskMem::BinarySearch(const_cast(pszItem), piElem); } HRESULT Remove(const WCHAR* pszItem, size_t* piElemRemovedAt = nullptr) { return CSimplePointerArrayCoTaskMem::Remove(const_cast(pszItem), piElemRemovedAt); } }; class CSimpleCaseInsensitiveOrdinalStringArray : public CSimpleStringArrayBase { }; class CSimpleUserDefaultLocaleCaseInsensitiveStringArray : public CSimpleStringArrayBase { };