You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
583 lines
15 KiB
583 lines
15 KiB
#pragma once |
|
|
|
#include <Windows.h> |
|
#include <type_traits> |
|
|
|
#include "ContainerPolicies.h" |
|
|
|
template <typename T> |
|
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; |
|
} |
|
}; |
|
|
|
template <typename T> |
|
class CSimpleArrayStandardMergeHelper |
|
{ |
|
}; |
|
|
|
template < |
|
typename T, |
|
typename CompareHelper |
|
> |
|
class CTSimpleFixedArray |
|
{ |
|
public: |
|
T* _parray; |
|
size_t _celem; |
|
|
|
CTSimpleFixedArray() |
|
: _parray(nullptr) |
|
, _celem(0) |
|
{ |
|
} |
|
|
|
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 <typename Comparer> |
|
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 <typename Comparer> |
|
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 <typename TCallback> |
|
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, |
|
typename MergeHelper = CSimpleArrayStandardMergeHelper<T> |
|
> |
|
class CTSimpleArray : public CTSimpleFixedArray<T, CompareHelper> |
|
{ |
|
public: |
|
T* _parrayT; |
|
size_t _celemCapacity; |
|
|
|
CTSimpleArray() |
|
: CTSimpleFixedArray<T, CompareHelper>() |
|
, _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 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<T>) |
|
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<T>) |
|
{ |
|
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; |
|
} |
|
|
|
size_t GetCapacity() const |
|
{ |
|
return _celemCapacity; |
|
} |
|
|
|
HRESULT Sort() |
|
{ |
|
return SortEx(CompareHelper()); |
|
} |
|
|
|
template <typename Comparer> |
|
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 <typename ArgType> |
|
void _InternalSetAtIndex(size_t iElem, ArgType&& t) |
|
{ |
|
T* newPos = std::addressof(this->_parray[iElem]); |
|
if (newPos) |
|
new(newPos) T(std::forward<ArgType>(t)); |
|
} |
|
|
|
template <typename ArgType> |
|
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<ArgType>(t)); |
|
if (piElemInsertedAt) |
|
*piElemInsertedAt = this->_celem - 1; |
|
} |
|
|
|
return hr; |
|
} |
|
|
|
template <typename ArgType> |
|
HRESULT _InsertAt(ArgType&& t, size_t iElem) |
|
{ |
|
HRESULT hr = _MakeRoomAt(iElem); |
|
|
|
if (SUCCEEDED(hr)) |
|
{ |
|
_InternalSetAtIndex(iElem, std::forward<ArgType>(t)); |
|
} |
|
|
|
return hr; |
|
} |
|
|
|
template <typename ArgType> |
|
HRESULT _SetAtIndex(size_t iElem, ArgType&& t) |
|
{ |
|
HRESULT hr = TYPE_E_OUTOFBOUNDS; |
|
|
|
if (iElem < this->_celem) |
|
{ |
|
_InternalSetAtIndex(iElem, std::forward<ArgType>(t)); |
|
hr = S_OK; |
|
} |
|
|
|
return hr; |
|
} |
|
|
|
template <typename Comparer> |
|
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 <typename Comparer> |
|
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<T> |
|
> |
|
class CCoSimpleArray : public CTSimpleArray<T, MaxSize, CTPolicyCoTaskMem<T>, 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<T> |
|
> |
|
class CLocalSimpleArray : public CTSimpleArray<T, MaxSize, CTPolicyLocalMem<T>, CompareHelper> |
|
{ |
|
}; |
|
|
|
template < |
|
typename T, |
|
typename ElementAllocator, |
|
typename CompareHelper = CSimpleArrayStandardCompareHelper<T> |
|
> |
|
class CSimplePointerArray : public CCoSimpleArray<T*, UINT_MAX - 1, CompareHelper> |
|
{ |
|
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<T*> |
|
> |
|
class CSimplePointerArrayNewMem : public CSimplePointerArray<T, CTContainer_PolicyNewMem, CompareHelper> |
|
{ |
|
}; |
|
|
|
template < |
|
typename T, |
|
typename CompareHelper = CSimpleArrayStandardCompareHelper<T*> |
|
> |
|
class CSimplePointerArrayCoTaskMem : public CSimplePointerArray<T, CTPolicyCoTaskMem<T>, CompareHelper> |
|
{ |
|
}; |
|
|
|
template < |
|
typename T, |
|
typename CompareHelper = CSimpleArrayStandardCompareHelper<T*> |
|
> |
|
class CSimplePointerArrayLocalMem : public CSimplePointerArray<T, CTPolicyLocalMem<T>, CompareHelper> |
|
{ |
|
}; |
|
|
|
template <typename T> |
|
class CSimplePointerArrayRelease : public CSimplePointerArray<T, CTContainer_PolicyRelease<T>> |
|
{ |
|
};
|
|
|