/* * This is a part of the BugTrap package. * Copyright (c) 2005-2009 IntelleSoft. * All rights reserved. * * Description: Doubly-linked list. * Author: Maksim Pyatkovskiy. * * This source code is only intended as a supplement to the * BugTrap package reference and related electronic documentation * provided with the product. See these sources for detailed * information regarding the BugTrap package. */ #pragma once #include "ColHelper.h" /// Doubly-linked list template < typename DATA_TYPE, class INSTANCE_TRAITS = CObjectTraits, class COMPARE_TRAITS = CCompareTraits > class CList { private: /// List item. class CListItem { /// Doubly-linked list. friend class CList; private: /// Contains data value. DATA_TYPE m_Data; /// Pointer to the previous item. CListItem* m_pPrev; /// Pointer to the next item. CListItem* m_pNext; }; /// Pointer to the first list item. CListItem* m_pFirst; /// Pointer to the last list item. CListItem* m_pLast; /// List of deleted items. These items can be reused in future allocations. CListItem* m_pGarbage; /// Number of items in collection. size_t m_nCount; /// Initialize list variables. void InitList(void); /// Throw an exception if pointer to the item is not valid. static void ValidateItem(const CListItem* pItem); /// Allocate new list item. CListItem* AllocListItem(void); public: /// Synonym of list iterator type. typedef CListItem* POSITION; /// Initialize a list object. CList(void); /// Makes a copy of a list. CList(const CList& rList); /// Makes a copy of a list. const CList& operator=(const CList& rList); /// Destroy a list object and free allocated memory. ~CList(void); /// Tests for the empty list condition (no elements). bool IsEmpty(void) const; /// Returns the number of elements in this list. size_t GetCount(void) const; /// Adds an element to the head of the list (makes a new head). POSITION AddToHead(const DATA_TYPE& rData); /// Adds an element to the tail of the list (makes a new tail). POSITION AddToTail(const DATA_TYPE& rData); /// Returns the head element of the list (cannot be empty). DATA_TYPE& GetHead(void); /// Returns the head element of the list (cannot be empty). const DATA_TYPE& GetHead(void) const; /// Returns the tail element of the list (cannot be empty). DATA_TYPE& GetTail(void); /// Returns the tail element of the list (cannot be empty). const DATA_TYPE& GetTail(void) const; /// Returns the position of the head element of the list. POSITION GetHeadPosition(void) const; /// Returns the position of the tail element of the list. POSITION GetTailPosition(void) const; /// Gets the next element for iterating. static POSITION GetNextPosition(POSITION pos); /// Gets the previous element for iterating. static POSITION GetPrevPosition(POSITION pos); /// Gets an element at a given position. static DATA_TYPE& GetAt(POSITION pos); /// Sets an element at a given position. static void SetAt(POSITION pos, const DATA_TYPE& rData); /// Removes an element from this list, specified by position. void DeleteAt(POSITION pos); /// Inserts a new element before a given position. POSITION InsertBefore(POSITION pos, const DATA_TYPE& rData); /// Inserts a new element after a given position. POSITION InsertAfter(POSITION pos, const DATA_TYPE& rData); /// Removes the element from the head of the list. void DeleteHead(void); /// Removes the element from the tail of the list. void DeleteTail(void); /// Removes all the elements from this list. void DeleteAll(bool bFree = false); /// Gets the position of an element specified by pointer value. POSITION Find(const DATA_TYPE& rSearchValue, POSITION startAfter = NULL) const; /// Gets the position of an element specified by a zero-based index. POSITION GetByIndex(size_t nIndex) const; }; template void CList::InitList(void) { m_pFirst = NULL; m_pLast = NULL; m_pGarbage = NULL; m_nCount = 0; } /** * @param pItem - validated item. */ template inline void CList::ValidateItem(const CListItem* pItem) { _ASSERTE(pItem); if (! pItem) RaiseException(STATUS_ARRAY_BOUNDS_EXCEEDED, 0, 0, NULL); } template inline CList::CList(void) { InitList(); } /** * @param rList - object to be copied. */ template CList::CList(const CList& rList) { InitList(); CListItem* pItem = rList.m_pFirst; while (m_nCount < rList.m_nCount) { AddToTail(pItem->m_Data); pItem = pItem->m_pNext; } } /** * @param rList - object to be copied. * @return reference to itself. */ template const CList& CList::operator=(const CList& rList) { if (this != &rList) { CListItem* pSrcItem = rList.m_pFirst; CListItem* pDstItem = m_pFirst; if (m_nCount < rList.m_nCount) { if (m_pFirst == NULL) { while (m_nCount < rList.m_nCount) { AddToTail(pSrcItem->m_Data); pSrcItem = pSrcItem->m_pNext; } } else { while (m_nCount < rList.m_nCount) { InsertBefore(pDstItem, pSrcItem->m_Data); pSrcItem = pSrcItem->m_pNext; } } } else if (m_nCount > rList.m_nCount) { while (m_nCount > rList.m_nCount) DeleteTail(); } while (pSrcItem != NULL) { INSTANCE_TRAITS::Assignment(pDstItem->m_Data, pSrcItem->m_Data); pSrcItem = pSrcItem->m_pNext; pDstItem = pDstItem->m_pNext; } } return *this; } template inline CList::~CList(void) { DeleteAll(true); } /** * @return newly allocated list item. */ template typename CList::CListItem* CList::AllocListItem(void) { CListItem* pItem; if (m_pGarbage) { pItem = m_pGarbage; m_pGarbage = m_pGarbage->m_pNext; } else { pItem = (CListItem*)new BYTE[sizeof(CListItem)]; if (! pItem) RaiseException(STATUS_NO_MEMORY, 0, 0, NULL); } return pItem; } /** * @param rData - the new element. * @return the position of newly inserted value. */ template typename CList::POSITION CList::AddToHead(const DATA_TYPE& rData) { CListItem* pFirst = AllocListItem(); pFirst->m_pPrev = NULL; pFirst->m_pNext = m_pFirst; if (m_pFirst) m_pFirst->m_pPrev = pFirst; else m_pLast = pFirst; m_pFirst = pFirst; INSTANCE_TRAITS::Constructor(pFirst->m_Data); INSTANCE_TRAITS::Assignment(pFirst->m_Data, rData); ++m_nCount; return pFirst; } /** * @param rData - the new element. * @return the position of newly inserted value. */ template typename CList::POSITION CList::AddToTail(const DATA_TYPE& rData) { CListItem* pLast = AllocListItem(); pLast->m_pPrev = m_pLast; pLast->m_pNext = NULL; if (m_pLast) m_pLast->m_pNext = pLast; else m_pFirst = pLast; m_pLast = pLast; INSTANCE_TRAITS::Constructor(pLast->m_Data); INSTANCE_TRAITS::Assignment(pLast->m_Data, rData); ++m_nCount; return pLast; } /** * @return a reference to an element of the list. */ template inline DATA_TYPE& CList::GetHead(void) { ValidateItem(m_pFirst); return m_pFirst->m_Data; } /** * @return a reference to an element of the list. */ template inline const DATA_TYPE& CList::GetHead(void) const { ValidateItem(m_pFirst); return m_pFirst->m_Data; } /** * @return a reference to an element of the list. */ template inline DATA_TYPE& CList::GetTail(void) { ValidateItem(m_pLast); return m_pLast->m_Data; } /** * @return a reference to an element of the list. */ template inline const DATA_TYPE& CList::GetTail(void) const { ValidateItem(m_pLast); return m_pLast->m_Data; } /** * @return a @a POSITION value that can be used for iteration or object pointer retrieval; NULL if the list is empty. */ template inline typename CList::POSITION CList::GetHeadPosition(void) const { return m_pFirst; } /** * @return a @a POSITION value that can be used for iteration or object pointer retrieval; NULL if the list is empty. */ template inline typename CList::POSITION CList::GetTailPosition(void) const { return m_pLast; } /** * @param pos - a @a POSITION value returned by a previous GetPrevPosition(), GetTailPosition(), or other member function call. * @return the position of next element is the list or NULL. */ template inline typename CList::POSITION CList::GetNextPosition(typename CList::POSITION pos) { ValidateItem(pos); return pos->m_pNext; } /** * @param pos - a @a POSITION value returned by a previous GetPrevPosition(), GetTailPosition(), or other member function call. * @return the position of next element is the list or NULL. */ template inline typename CList::POSITION CList::GetPrevPosition(typename CList::POSITION pos) { ValidateItem(pos); return pos->m_pPrev; } /** * @return true if this list is empty; otherwise false. */ template inline bool CList::IsEmpty(void) const { return (m_pFirst == NULL); } /** * @return an integer value containing the element count. */ template inline size_t CList::GetCount(void) const { return m_nCount; } /** * @param pos - a @a POSITION value returned by a previous GetPrevPosition(), GetTailPosition(), or other member function call. * @return a reference to an element of the list. */ template inline DATA_TYPE& CList::GetAt(typename CList::POSITION pos) { ValidateItem(pos); return pos->m_Data; } /** * @param pos - the @a POSITION of the element to be set. * @param rData - the element to be added to the list. */ template inline void CList::SetAt(typename CList::POSITION pos, const DATA_TYPE& rData) { ValidateItem(pos); INSTANCE_TRAITS::Assignment(pos->m_Data, rData); } /** * @param pos - the position of the element to be removed from the list. */ template void CList::DeleteAt(typename CList::POSITION pos) { ValidateItem(pos); CListItem* pPrev = pos->m_pPrev; CListItem* pNext = pos->m_pNext; if (pPrev) pPrev->m_pNext = pNext; else m_pFirst = pNext; if (pNext) pNext->m_pPrev = pPrev; else m_pLast = pPrev; INSTANCE_TRAITS::Destructor(pos->m_Data); pos->m_pNext = m_pGarbage; m_pGarbage = pos; --m_nCount; } /** * @param pos - a @a POSITION value returned by a previous GetPrevPosition(), GetTailPosition(), or other member function call. * @param rData - the element to be added to this list. * @return the position of newly inserted value. */ template typename CList::POSITION CList::InsertBefore(typename CList::POSITION pos, const DATA_TYPE& rData) { ValidateItem(pos); CListItem* pItem = AllocListItem(); CListItem* pPrev = pos->m_pPrev; pItem->m_pPrev = pPrev; pItem->m_pNext = pos; if (pPrev) pPrev->m_pNext = pItem; else m_pFirst = pItem; pos->m_pPrev = pItem; INSTANCE_TRAITS::Constructor(pItem->m_Data); INSTANCE_TRAITS::Assignment(pItem->m_Data, rData); ++m_nCount; return pItem; } /** * @param pos - a @a POSITION value returned by a previous GetPrevPosition(), GetTailPosition(), or other member function call. * @param rData - the element to be added to this list. * @return the position of newly inserted value. */ template typename CList::POSITION CList::InsertAfter(typename CList::POSITION pos, const DATA_TYPE& rData) { ValidateItem(pos); CListItem* pItem = AllocListItem(); CListItem* pNext = pos->m_pNext; pItem->m_pPrev = pos; pItem->m_pNext = pNext; if (pNext) pNext->m_pPrev = pItem; else m_pLast = pItem; pos->m_pNext = pItem; INSTANCE_TRAITS::Constructor(pItem->m_Data); INSTANCE_TRAITS::Assignment(pItem->m_Data, rData); ++m_nCount; return pItem; } template void CList::DeleteHead(void) { ValidateItem(m_pFirst); INSTANCE_TRAITS::Destructor(m_pFirst->m_Data); CListItem* pNext = m_pFirst->m_pNext; m_pFirst->m_pNext = m_pGarbage; m_pGarbage = m_pFirst; m_pFirst = pNext; if (pNext) pNext->m_pPrev = NULL; else m_pLast = NULL; --m_nCount; } template void CList::DeleteTail(void) { ValidateItem(m_pLast); INSTANCE_TRAITS::Destructor(m_pLast->m_Data); CListItem* pPrev = m_pLast->m_pPrev; m_pLast->m_pNext = m_pGarbage; m_pGarbage = m_pLast; m_pLast = pPrev; if (pPrev) pPrev->m_pNext = NULL; else m_pFirst = NULL; --m_nCount; } /** * @param bFree - pass true of release array memory. */ template void CList::DeleteAll(bool bFree) { if (bFree) { CListItem* pNext; while (m_pGarbage) { pNext = m_pGarbage->m_pNext; delete[] (PBYTE)m_pGarbage; m_pGarbage = pNext; } while (m_pFirst) { pNext = m_pFirst->m_pNext; INSTANCE_TRAITS::Destructor(m_pFirst->m_Data); delete[] (PBYTE)m_pFirst; m_pFirst = pNext; } } else { while (m_pFirst) { CListItem* pNext = m_pFirst->m_pNext; INSTANCE_TRAITS::Destructor(m_pFirst->m_Data); m_pFirst->m_pNext = m_pGarbage; m_pGarbage = m_pFirst; m_pFirst = pNext; } } m_pLast = NULL; m_nCount = 0; } /** * @param rSearchValue - the value to be found in the list. * @param startAfter - the start position for the search. * @return a @a POSITION value that can be used for iteration or object pointer retrieval; NULL if the object is not found. */ template typename CList::POSITION CList::Find(const DATA_TYPE& rSearchValue, typename CList::POSITION startAfter) const { if (startAfter == NULL) startAfter = m_pFirst; while (startAfter != NULL && ! COMPARE_TRAITS::Compare(startAfter->m_Data, rSearchValue)) startAfter = startAfter->m_pNext; return startAfter; } /** * @param nIndex - the zero-based index of the list element to be found. * @return a @a POSITION value that can be used for iteration or object pointer retrieval; NULL if nIndex is negative or too large. */ template typename CList::POSITION CList::GetByIndex(size_t nIndex) const { CListItem* pItem = m_pFirst; while (nIndex-- && pItem) pItem = pItem->m_pNext; return pItem; }