#ifndef _INC_DSKQUOTA_CARRAY_H #define _INC_DSKQUOTA_CARRAY_H /* File: carray.h Description: Template class CArray. Implements a dynamic array class. Much of the functionality is based on the feature set of MFC's CArray class. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 09/16/97 Initial creation. BrianAu 12/13/97 Changed SetAtGrow to return true/false. True means BrianAu had to grow array. */ #ifndef _INC_DSKQUOTA_DEBUG_H # include "debug.h" #endif #ifndef _INC_DSKQUOTA_THDSYNC_H # include "thdsync.h" #endif #ifndef _INC_DSKQUOTA_EXCEPT_H # include "except.h" #endif template class CArray { public: CArray(VOID); explicit CArray(INT cItems); CArray(const CArray& rhs); CArray& operator = (const CArray& rhs); virtual ~CArray(VOID); VOID SetAt(const T& item, INT i); bool SetAtGrow(const T& item, INT i); T GetAt(INT i) const; VOID Insert(const T& item, INT i = -1); VOID Append(const T& item, INT i = -1); INT Find(const T& key); VOID Delete(INT i); T operator [] (INT i) const; T& operator [] (INT i); VOID Clear(VOID); BOOL IsEmpty(VOID) const { return 0 == m_cItems; } INT Count(VOID) const { return m_cItems; } INT UpperBound(VOID) const { return m_cItems - 1; } INT Size(VOID) const { return m_cAlloc; } VOID SetGrow(INT cGrow) { m_cGrow = cGrow; } VOID Copy(const CArray& rhs); VOID Append(const CArray& rhs); VOID SetSize(INT cEntries, INT iShift = -1); VOID Lock(VOID) { m_cs.Enter(); } VOID ReleaseLock(VOID) { m_cs.Leave(); } protected: static INT DEFGROW; // Default growth value. private: INT m_cAlloc; // Number of entry allocations. INT m_cItems; // Number of used entries. INT m_cGrow; T *m_rgItems; // Array of entries. mutable CCriticalSection m_cs; // For multi-threaded access. template const U& MIN(const U& a, const U& b) const { return a < b ? a : b; } template const U& MAX(const U& a, const U& b) const { return a > b ? a : b; } }; template INT CArray::DEFGROW = 8; template CArray::CArray( void ) : m_cAlloc(0), m_cItems(0), m_cGrow(DEFGROW), m_rgItems(NULL) { } template CArray::CArray( INT cItems ) : m_cAlloc(0), m_cItems(0), m_cGrow(DEFGROW), m_rgItems(NULL) { SetSize(cItems); m_cItems = cItems; } template CArray::CArray( const CArray& rhs ) : m_cAlloc(0), m_cItems(0), m_cGrow(DEFGROW), m_rgItems(NULL) { *this = rhs; } template VOID CArray::Copy( const CArray& rhs ) { AutoLockCs lock1(rhs.m_cs); AutoLockCs lock2(m_cs); // Place *this in an empty state in case Grow() throws an exception. // It should still be a valid CArray object. delete[] m_rgItems; m_rgItems = NULL; m_cAlloc = 0; m_cItems = 0; // Size the object to hold the source array. SetSize(rhs.m_cAlloc); // Copy the contents. DBGASSERT((m_cAlloc >= rhs.m_cItems)); for (m_cItems = 0; m_cItems < rhs.m_cItems; m_cItems++) { // This assignment could throw an exception so only update // our item count after each successful copy. DBGASSERT((m_cItems < m_cAlloc)); m_rgItems[m_cItems] = rhs.m_rgItems[m_cItems]; } } template VOID CArray::Append( const CArray& rhs ) { AutoLockCs lock1(rhs.m_cs); AutoLockCs lock2(m_cs); // Size the object to hold both arrays. SetSize(m_cAlloc + rhs.m_cItems); // Append the contents. DBGASSERT((m_cAlloc >= (m_cItems + rhs.m_cItems))); for (int i = 0; i < rhs.m_cItems; i++) { DBGASSERT((m_cItems < m_cAlloc)); m_rgItems[m_cItems++] = rhs.m_rgItems[i]; } } template CArray& CArray::operator = ( const CArray& rhs ) { if (this != &rhs) { Copy(rhs); } return *this; } template CArray::~CArray( VOID ) { Clear(); } template T CArray::operator [] ( INT i ) const { return GetAt(i); } template T& CArray::operator [] ( INT i ) { AutoLockCs lock(m_cs); if (i < 0 || i >= m_cItems) throw CMemoryException(CMemoryException::index); return *(m_rgItems + i); } template VOID CArray::Clear( VOID ) { AutoLockCs lock(m_cs); delete[] m_rgItems; m_rgItems = NULL; m_cAlloc = 0; m_cItems = 0; } template VOID CArray::Insert( const T& item, INT i ) { AutoLockCs lock(m_cs); if (-1 == i) { // Insert at head of array. i = 0; } // Can only insert an item before an existing item. // i cannot be negative. // If array is empty, i can only be 0. // If array is not empty, i must be index of a valid item. if ((0 == m_cItems && 0 != i) || (0 != m_cItems && (i < 0 || i >= m_cItems))) { throw CMemoryException(CMemoryException::index); } DBGASSERT((m_cItems <= m_cAlloc)); if (m_cItems >= m_cAlloc) { // Grow the array if necessary. // This will also shift the elements, beginning with element 'i', // one element to the right. SetSize(m_cAlloc + m_cGrow, i); } else { // Growth not necessary. // Shift the contents of the array following the insertion point // one element to the right. for (int j = m_cItems; j > i; j--) { m_rgItems[j] = m_rgItems[j-1]; } } // We've now inserted an item. m_cItems++; // Set the value at the inserted location. // This assignment could throw an exception. SetAt(item, i); } template VOID CArray::Append( const T& item, INT i ) { AutoLockCs lock(m_cs); if (-1 == i) { // Append at end of array. i = m_cItems - 1; } // Can only append an item after an existing item. // When array is empty, i can only be -1. // When array is not empty, i must be index of a valid item. // Note: i will be -1 when m_cItems is 0. if ((0 == m_cItems && -1 != i) || (0 != m_cItems && (i < 0 || i >= m_cItems))) { throw CMemoryException(CMemoryException::index); } DBGASSERT((m_cItems <= m_cAlloc)); if (m_cItems >= m_cAlloc) { // Grow the array if necessary. // This will also shift the elements, beginning with element 'i + 1', // one element to the right. SetSize(m_cAlloc + m_cGrow, i+1); } else { // Shift the contents of the array following the insertion // point, one entry to the right. for (int j = m_cItems; j > (i+1); j--) { m_rgItems[j] = m_rgItems[j-1]; } } // We've now appended an item. m_cItems++; // Set the value at the appended location. // This assignment could throw an exception. SetAt(item, i+1); } template VOID CArray::Delete( INT i ) { AutoLockCs lock(m_cs); // Can only delete a valid item. if (i < 0 || i >= m_cItems) throw CMemoryException(CMemoryException::index); // Shift memory to remove the item. for (int j = i; j < (m_cItems - 1); j++) { m_rgItems[j] = m_rgItems[j+1]; } // Now we have one less item. m_cItems--; // Shrink the array if it's required size is less than 2X the // array's "growth" amount. if ((m_cAlloc - m_cItems) > (2 * m_cGrow)) { SetSize(m_cItems); } } template INT CArray::Find( const T& key ) { AutoLockCs lock(m_cs); for (INT i = 0; i < m_cItems; i++) { if (m_rgItems[i] == key) { return i; } } return -1; } template T CArray::GetAt( INT i ) const { AutoLockCs lock(m_cs); if (i < 0 || i >= m_cItems) throw CMemoryException(CMemoryException::index); return m_rgItems[i]; } template VOID CArray::SetAt( const T& item, INT i ) { AutoLockCs lock(m_cs); if (i < 0 || i >= m_cAlloc) throw CMemoryException(CMemoryException::index); m_rgItems[i] = item; } // Returns: true = array was extended, false = no extension required. template bool CArray::SetAtGrow( const T& item, INT i ) { bool bGrow = false; AutoLockCs lock(m_cs); if (i >= m_cAlloc) { // Need to grow the array to accomodate the new item. SetSize(i + m_cGrow); bGrow = true; } // Set the new item value. SetAt(item, i); // Extend the count of "valid" items. m_cItems = i + 1; return bGrow; } template VOID CArray::SetSize( INT cEntries, INT iShift // Pass -1 for "no shift". ) { AutoLockCs lock(m_cs); // Don't allow an array of less than 1 element. cEntries = MAX(1, cEntries); T *pNew = new T[cEntries]; if (NULL == pNew) throw CAllocException(); if (NULL != m_rgItems) { INT cCopy = MIN(cEntries, m_cItems); INT j = 0; for (INT i = 0; i < cCopy; i++, j++) { // Shift items [i..(n-1)] to [(i+1)..n] if (iShift == j) j++; *(pNew + j) = m_rgItems[i]; } } delete[] m_rgItems; m_rgItems = pNew; m_cAlloc = cEntries; } template class CQueueAsArray : public CArray { public: CQueueAsArray(VOID) { } ~CQueueAsArray(VOID) { } VOID Add(T& item); BOOL Remove(T& item); private: CQueueAsArray(const CQueueAsArray& rhs); CQueueAsArray& operator = (const CQueueAsArray& rhs); }; template VOID CQueueAsArray::Add( T& item ) { Append(item); } template BOOL CQueueAsArray::Remove( T& item ) { BOOL bResult = FALSE; if (!IsEmpty()) { INT i = UpperBound(); item = GetAt(i); Delete(i); bResult = TRUE; } return bResult; } #endif // _INC_DSKQUOTA_CARRAY_H