/* * Dynamic array */ #ifndef DUI_BASE_DYNAMICARRAY_H_INCLUDED #define DUI_BASE_DYNAMICARRAY_H_INCLUDED #pragma once namespace DirectUI { //------------------------------------------------------------------------- // // DyanamicArray // // Stores values in an array the double size when it reaches capacity // // Compile DEBUG for bounds checking and other DUIAsserts, see public class // declarations for API // // Values are stored natively. Type is chosen at compile time. For example // (Value is type BYTE, initial capacity is 10): // // DynamicArray* pda; // DynamicArray::Create(10, &pda); // // pda->Add(4); // pda->Insert(0, 7); // // DUITrace("0: %d\n", pda->GetItem(0)); // a[0] = 7 // DUITrace("1: %d\n", pda->GetItem(1)); // a[1] = 4 // // pda->Remove(0); // // DUITrace("0: %d\n", pda->GetItem(0)); // a[0] = 4 // // The Value type must support the following operation: // Assignment (=) // //------------------------------------------------------------------------- template class DynamicArray { public: // API static HRESULT Create(UINT uCapacity, bool fZeroData, OUT DynamicArray** ppArray); virtual ~DynamicArray(); void Destroy() { HDelete< DynamicArray >(this); } // 'Ptr' methods return addesses, note that Add and Insert may cause the array // to be reallocated and moved in memory. Be sure not to use pointers after // an Add or Insert operation typedef int (__cdecl *QSORTCMP )(const void*, const void*); HRESULT Add(T tItem); // Insert Item at end of list (double capacity if needed) HRESULT AddPtr(OUT T** ppNewItem); // Allocate room for Item at end of list and return pointer (double capacity if needed) HRESULT Insert(UINT uIndex, T tItem); // Insert Item at location (double capacity if needed) HRESULT InsertPtr(UINT uIndex, OUT T** pNewItem); // Allocate room at insertion point and return pointer (double capacity if needed) void SetItem(UINT uIndex, T tItem); // Overwrite Item at location T GetItem(UINT uIndex); // Get Item at location T* GetItemPtr(UINT uIndex); // Get pointer to Item at location UINT GetSize(); // Return size of array (not current capacity) UINT GetIndexPtr(T* pItem); // Return index of Item based on its pointer int GetIndexOf(T tItem); // Return index of Item void Remove(UINT uIndex); // Remove Item at location void Reset(); // Reset array to be reused (make zero size) bool WasMoved(); // May be called right after Add or Insert to determine if da was moved in memory HRESULT Clone(OUT DynamicArray** ppClone); // Make exact copy of array bool IsEqual(DynamicArray* pda); // Return true if contents are equal to da void Sort(QSORTCMP fpCmp); // Sort the array void MakeImmutable(); // Write-once, make so can only do read-only operations void MakeWritable(); // Make read/write bool fLock : 1; DynamicArray() { } HRESULT Initialize(UINT uCapacity, bool fZeroData); private: UINT _uSize; UINT _uCapacity; T* _pData; bool _fZeroData : 1; // Zero memory for data if InsertPtr is used bool _fWasMoved : 1; // On a reallocation (via Add or Insert), true if fLock was moved bool _fImmutable : 1; // If true, can only do read-only operations }; template HRESULT DynamicArray::Create(UINT uCapacity, bool fZeroData, OUT DynamicArray** ppArray) { *ppArray = NULL; // Instantiate DynamicArray* pda = HNew< DynamicArray >(); if (!pda) return E_OUTOFMEMORY; HRESULT hr = pda->Initialize(uCapacity, fZeroData); if (FAILED(hr)) { pda->Destroy(); return hr; } *ppArray = pda; return S_OK; } template HRESULT DynamicArray::Initialize(UINT uCapacity, bool fZeroData) { _uCapacity = uCapacity; _uSize = 0; if (_uCapacity) // Allocate only if have an initial capacity { _pData = (T*)HAlloc(sizeof(T) * _uCapacity); if (!_pData) return E_OUTOFMEMORY; } else _pData = NULL; _fZeroData = fZeroData; _fWasMoved = false; _fImmutable = false; fLock = false; return S_OK; } template DynamicArray::~DynamicArray() { if (_pData) HFree(_pData); } // Copy data into array template HRESULT DynamicArray::Add(T tItem) { return Insert(_uSize, tItem); } template HRESULT DynamicArray::AddPtr(OUT T** ppNewItem) { return InsertPtr(_uSize, ppNewItem); } template HRESULT DynamicArray::Insert(UINT uIndex, T tItem) { DUIAssert(!_fImmutable, "Only read operations allowed on immutable DynamicArray"); _fWasMoved = false; DUIAssert(uIndex <= _uSize, "DynamicArray index out of bounds"); // Resize if needed if (_uSize == _uCapacity) { // Double capacity of list UINT uNewCapacity = _uCapacity; if (uNewCapacity == 0) { uNewCapacity = 1; } else { uNewCapacity *= 2; } // Reallocate current fLock UINT_PTR pOld = (UINT_PTR)_pData; if (_pData) { T* pNewData = (T*)HReAlloc(_pData, sizeof(T) * uNewCapacity); if (!pNewData) return E_OUTOFMEMORY; _pData = pNewData; } else { _pData = (T*)HAlloc(sizeof(T) * uNewCapacity); if (!_pData) return E_OUTOFMEMORY; } // Update capacity field _uCapacity = uNewCapacity; if (pOld != (UINT_PTR)_pData) _fWasMoved = true; } // Shift data at index down one slot MoveMemory(_pData + (uIndex + 1), _pData + uIndex, (_uSize - uIndex) * sizeof(T)); // Copy new data at insertion point _pData[uIndex] = tItem; _uSize++; return S_OK; } template HRESULT DynamicArray::InsertPtr(UINT uIndex, T** pNewItem) { DUIAssert(!_fImmutable, "Only read operations allowed on immutable DynamicArray"); _fWasMoved = false; DUIAssert(uIndex <= _uSize, "DynamicArray index out of bounds"); // Resize if needed if (_uSize == _uCapacity) { // Double capacity of list UINT uNewCapacity = _uCapacity; if (uNewCapacity == 0) { uNewCapacity = 1; } else { uNewCapacity *= 2; } // Reallocate current fLock UINT_PTR pOld = (UINT_PTR)_pData; if (_pData) { T* pNewData = (T*)HReAlloc(_pData, sizeof(T) * uNewCapacity); if (!pNewData) return E_OUTOFMEMORY; _pData = pNewData; } else { _pData = (T*)HAlloc(sizeof(T) * uNewCapacity); if (!_pData) return E_OUTOFMEMORY; } // Update capacity field _uCapacity = uNewCapacity; if (pOld != (UINT_PTR)_pData) _fWasMoved = true; } // Shift data at index down one slot MoveMemory(_pData + (uIndex + 1), _pData + uIndex, (_uSize - uIndex) * sizeof(T)); _uSize++; if (_fZeroData) ZeroMemory(_pData + uIndex, sizeof(T)); *pNewItem = _pData + uIndex; return S_OK; } template void DynamicArray::SetItem(UINT uIndex, T tItem) { DUIAssert(!_fImmutable, "Only read operations allowed on immutable DynamicArray"); DUIAssert(uIndex < _uSize, "DynamicArray index out of bounds"); // Copy new data at insertion point _pData[uIndex] = tItem; } template T DynamicArray::GetItem(UINT uIndex) { DUIAssert(uIndex < _uSize, "DynamicArray index out of bounds"); return _pData[uIndex]; } template T* DynamicArray::GetItemPtr(UINT uIndex) { DUIAssert(!_fImmutable, "Only read operations allowed on immutable DynamicArray"); DUIAssert(uIndex < _uSize, "DynamicArray index out of bounds"); return _pData + uIndex; } template UINT DynamicArray::GetIndexPtr(T* pItem) { DUIAssert((((UINT_PTR)pItem - (UINT_PTR)_pData) / sizeof(T)) >= 0 && (((UINT_PTR)pItem - (UINT_PTR)_pData) / sizeof(T)) < _uSize, "GetIndexPtr out of bounds"); return (UINT)(((UINT_PTR)pItem - (UINT_PTR)_pData) / sizeof(T)); } template int DynamicArray::GetIndexOf(T tItem) { for (UINT i = 0; i < _uSize; i++) { if (_pData[i] == tItem) return i; } return -1; } template UINT DynamicArray::GetSize() { return _uSize; } template void DynamicArray::Remove(UINT uIndex) { DUIAssert(!_fImmutable, "Only read operations allowed on immutable DynamicArray"); DUIAssert(uIndex < _uSize, "DynamicArray index out of bounds"); // Shift memory MoveMemory(_pData + uIndex, _pData + (uIndex + 1), (_uSize - uIndex - 1) * sizeof(T)); _uSize--; } template void DynamicArray::Reset() { DUIAssert(!_fImmutable, "Only read operations allowed on immutable DynamicArray"); _uSize = 0; _fWasMoved = false; fLock = false; } template bool DynamicArray::WasMoved() { return _fWasMoved; } template HRESULT DynamicArray::Clone(OUT DynamicArray** ppClone) // New instance returned { // Validate parameters DUIAssert(ppClone, "Invalid parameter: ppClone == NULL"); *ppClone = NULL; DynamicArray* pda = HNew< DynamicArray >(); if (!pda) return E_OUTOFMEMORY; pda->_uSize = _uSize; pda->_uCapacity = _uCapacity; pda->_fZeroData = _fZeroData; pda->_fWasMoved = _fWasMoved; pda->_fImmutable = false; pda->_pData = NULL; if (_pData) { pda->_pData = (T*)HAlloc(sizeof(T) * _uCapacity); if (!pda->_pData) { pda->Destroy(); return E_OUTOFMEMORY; } CopyMemory(pda->_pData, _pData, sizeof(T) * _uSize); } *ppClone = pda; return S_OK; } template bool DynamicArray::IsEqual(DynamicArray* pda) { if (!pda) return false; if (_uSize != pda->_uSize) return false; DUIAssert(!((_pData && !pda->_pData) || (!_pData && pda->_pData)), "Invalid comparison"); if (_pData && memcmp(_pData, pda->_pData, sizeof(T) * _uSize) != 0) return false; return true; } template void DynamicArray::Sort(QSORTCMP fpCmp) { if (_uSize) { qsort(_pData, _uSize, sizeof(T), fpCmp); } } template void DynamicArray::MakeImmutable() { #if DBG _fImmutable = true; #endif } template void DynamicArray::MakeWritable() { #if DBG _fImmutable = false; #endif } } // namespace DirectUI #endif // DUI_BASE_DYNAMICARRAY_H_INCLUDED