// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #pragma once #define SC_ASSERT Assert const HRESULT E_NOTFOUND = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); //---------------------------------------------------------------------------- // NonCopyAble - Base class for objects that can't be copied using assignement // operators or copy constructors. Objects that drive from this class // typically provide a separate initialization function that reurn errors // upon failure. Constructors and assignment operators can not perform any // operation that can fail because they have no good way to return errors. //---------------------------------------------------------------------------- class NonCopyAble { public: NonCopyAble(){} ~NonCopyAble(){} private: NonCopyAble(const NonCopyAble& other); NonCopyAble& operator=(const NonCopyAble& other); }; //---------------------------------------------------------------------------- // CDefaultTraits - Default helper for comparing, constructing, and // assigning elements. // // Override if using complex types without operator== or operator= or copy // constructors. //---------------------------------------------------------------------------- template class CDefaultTraits { public: static bool IsEqual(const T& t1, const T& t2) { return t1 == t2; } template static HRESULT Construct( T *pDest, // Pointer to destination element const X &xSrc // Source to assign ) { // Use placement new to call the copy constructor. Note that since we // don't use exceptions, constructors are not allowed to fail. Objects // that require more complex initialization should have a custom traits class // that contruct and then initialize returning errors as appropriate. new(pDest) T(xSrc); return S_OK; } template static HRESULT Assign( T *pDest, // Pointer to destination element const X &xSrc // Source to assign ) { // Call the object's assignment operator. Override this method if // with a custom traits class if assignment can fail. *pDest = xSrc; return S_OK; } }; //---------------------------------------------------------------------------- // CArray - Dynamically sized array based off of ATL's CSimpleArray. //---------------------------------------------------------------------------- template > class CArray : public NonCopyAble { public: static const UINT NotFound; // Construction/destruction CArray(); ~CArray(); // Operations UINT GetCount() const; UINT GetCapacity() const; HRESULT RemoveAt(UINT nIndex); void RemoveAll(); const T& operator[] (UINT nIndex) const; T& operator[] (UINT nIndex); T* GetData() const; HRESULT Resize(UINT nNewSize); T& GetFront() { Assert(GetCount() != 0); return (*this)[0]; } T& GetBack() { Assert(GetCount() != 0); return (*this)[GetCount()-1]; } void RemoveLast() { Assert(GetCount() != 0); // We can safely ignore the return value here because Resize will never // fail when reducing the size of the array (no reallocation is needed). Resize(GetCount()-1); } // // The following three functions are templated to permit the caller to // assign different types to our array. The TElementTraits must support // the types used. For example an array of CString objects might allow // both CString and PWSTR variable types to be added. // // // Removes the specified element from the array. All following elements are // shifted down. // template HRESULT Remove( const X &x // Element to remove ) { HRESULT hr = S_OK; UINT nIndex = Find(x); if (nIndex != NotFound) { hr = RemoveAt(nIndex); } else { hr = E_NOTFOUND; } return hr; } // // Finds an element in the array. Returns the index of the found element, or // NotFound if the element is not found. // template UINT Find ( const X &x // Element to find ) const { for (UINT i = 0; i < _nSize; i++) { if (TElementTraits::IsEqual(_aT[i], x)) { return i; } } // Not found return NotFound; } // // Inserts an item at the specified index shifting following items up one slot. // template HRESULT InsertAt( UINT nIndex, // Index where to insert const X &x // Element to insert ) { HRESULT hr; UINT nOldSize = _nSize; hr = MakeRoomAt(nIndex); if (SUCCEEDED(hr)) { // If index is past the end, call default constructor // for all items between the old end and new index if (nIndex > nOldSize) { for (UINT i = nOldSize; i < nIndex; ++i) { new(_aT + i) T; } } hr = TElementTraits::Construct(_aT + nIndex, x); if (FAILED(hr)) { // Element assignment failed, remove inserted element RemoveAt(nIndex); } } return hr; } // // Appends element to the end of the array. // template HRESULT Add( const X &x // Element to add ) { HRESULT hr = S_OK; if (_nSize == _nAllocSize) { hr = EnsureCapacity(_nSize + 1); } if (SUCCEEDED(hr)) { _nSize++; hr = TElementTraits::Construct(_aT + _nSize - 1, x); if (FAILED(hr)) { // Element assignment failed, remove element RemoveAt(_nSize - 1); } } return hr; } // Set the specified element in the array template HRESULT SetAt( UINT nIndex, // Element index to Set const X &x // Element to assign ) { if (nIndex < 0 || nIndex >= _nSize) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { hr = TElementTraits::Assign(_aT + nIndex, x); } return hr; } // Copy a C array of a type into the array template HRESULT AddArray( const X *pxSource, // Array to to copy UINT cItems // Count of elements to copy ) { HRESULT hr = S_OK; if (cItems < 0) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { hr = EnsureCapacity(_nSize + cItems); for (UINT i = 0; SUCCEEDED(hr) && i < cItems; i++) { hr = Add(pxSource[i]); } } return hr; } template HRESULT AddArray( const CArray &other // Array to add ) { return AddArray(other.GetData(), other.GetCount()); } HRESULT EnsureCapacity(UINT cCapacityDesired); protected: HRESULT MakeRoomAt(UINT nIndex); private: T* _aT; UINT _nSize; UINT _nAllocSize; }; template const UINT CArray::NotFound = UINT_MAX; // // Constructor // template CArray::CArray() : _aT(NULL), _nSize(0), _nAllocSize(0) { } // // Destructor // template CArray::~CArray() { RemoveAll(); } // // Return the number of elements in the array // template UINT CArray::GetCount() const { return _nSize; } // // Return the capacity of the array // template UINT CArray::GetCapacity() const { return _nAllocSize; } // // Allocates sufficient memory to hold the desired number of items. // template HRESULT CArray::EnsureCapacity( UINT cCapacityDesired // Capacity count to check and allocate ) { HRESULT hr = S_OK; if (_nAllocSize < cCapacityDesired) { #pragma prefast(push) #pragma prefast(disable:6287 "Prefast can't determne that sizeof(T) != 1") if (_nSize > (UINT_MAX / 2) || _nSize > (SIZE_MAX / 2 / sizeof(T))) #pragma prefast(pop) { hr = E_FAIL; } else { const UINT nNewAllocSize = max(_nSize * 2, cCapacityDesired); T *aT; // // If we have already allocated the array. Note, _nSize might be zero // at this point if we had resized to 0. // if (_aT) { aT = static_cast(realloc(_aT, nNewAllocSize * sizeof(T))); } else { aT = static_cast(malloc(nNewAllocSize * sizeof(T))); } if (aT == NULL) { hr = E_OUTOFMEMORY; } else { _nAllocSize = nNewAllocSize; _aT = aT; } } } return hr; } // // Adds an empty slot at the specified index. // template HRESULT CArray::MakeRoomAt( UINT nIndex // Index to add an empty element ) { HRESULT hr = S_OK; if (nIndex < 0) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { UINT cElemGrowTo; if (nIndex >= _nSize) { cElemGrowTo = nIndex + 1; } else { cElemGrowTo = _nSize + 1; } if (cElemGrowTo > _nAllocSize) { hr = EnsureCapacity(cElemGrowTo); } if (SUCCEEDED(hr)) { if (nIndex < _nSize) { // Move the remaining elements out so there's room memmove((void*)(_aT + nIndex + 1), (void*)(_aT + nIndex), (_nSize - nIndex) * sizeof(T)); } _nSize = cElemGrowTo; } } return hr; } // // Removes the element at the specified index and shift all following elements // down an index. // template HRESULT CArray::RemoveAt( UINT nIndex // Index of element to remove ) { HRESULT hr = S_OK; SC_ASSERT(nIndex >= 0 && nIndex < _nSize); if (nIndex < 0 || nIndex >= _nSize) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { _aT[nIndex].~T(); if (nIndex != (_nSize - 1)) { memmove((void *)(_aT + nIndex), (void *)(_aT + nIndex + 1), (_nSize - (nIndex + 1)) * sizeof(T)); } _nSize--; } return hr; } // // Removes all elements from the array // template void CArray::RemoveAll() { if (_aT != NULL) { for (UINT i = 0; i < _nSize; i++) { _aT[i].~T(); } free(_aT); _aT = NULL; } _nSize = 0; _nAllocSize = 0; } // // Array operator for accessing elements. This version works for const arrays. // template const T& CArray::operator[] (UINT nIndex) const { SC_ASSERT(nIndex >= 0 && nIndex < _nSize); return _aT[nIndex]; } // // Array operator for accessing elements. // template T& CArray::operator[] (UINT nIndex) { SC_ASSERT(nIndex >= 0 && nIndex < _nSize); return _aT[nIndex]; } // // Returns a pointer to the data stored in the array. // template T* CArray::GetData() const { return _aT; } // // If the array's size is less than the requested size, nNewSize elements // are added to the array until it reaches the requested size. If the // array's size is larger than the requested size, the elements closest to // the end of the array are deleted until the array reaches the requested size // If the present size of the array is the same as the requested size, no // action is taken. // template HRESULT CArray::Resize( UINT nNewSize // Count to resize to ) { HRESULT hr = S_OK; if (nNewSize < 0) { hr = E_INVALIDARG; } if (nNewSize > _nSize) { // Add more elements to the end of the array hr = EnsureCapacity(nNewSize); if (SUCCEEDED(hr)) { // Call the NULL constructor to init each element for (UINT i = _nSize; i < nNewSize; ++i) { new(_aT + i) T; } _nSize = nNewSize; } } else if (nNewSize < _nSize) { // Remove elements from the end of the array for (UINT i = nNewSize; i < _nSize; ++i) { _aT[i].~T(); } _nSize = nNewSize; } return hr; }