/*++ TFDLIST.H This header file defines templates for manipulating doubly linked lists. These are intrusive lists - the client must provide a DLIST_ENTRY item within each data member for us to maintain the list. --*/ #ifndef _TFDLIST_H_ #define _TFDLIST_H_ class DLIST_ENTRY { /*++ Class Description : This class is intended to be incorporated into classes which are held within doubly linked lists. This class will define two pointers used to chain the items within the lists. IMPORTANT : m_pNext and m_pPrev point to DLIST_ENTRY's and not to the top of the containing item - the template classes provided following here are to be used to manipulate these items. --*/ private : // // These are private - they don't make sense for clients ! // DLIST_ENTRY( DLIST_ENTRY& ) ; DLIST_ENTRY& operator=(DLIST_ENTRY&) ; protected : // // The items which allows maintain the doubly linked list ! // class DLIST_ENTRY* m_pNext ; class DLIST_ENTRY* m_pPrev ; // // The base class for all iterators ! // friend class DLISTIterator ; void InsertAfter( DLIST_ENTRY* p ) { /*++ Routine Description : Insert an item into the list after THIS ! Arguments : p - the item to be inserted ! Return Value None. --*/ _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; _ASSERT( p->m_pNext == p ) ; _ASSERT( p->m_pPrev == p ) ; DLIST_ENTRY* pNext = m_pNext ; p->m_pNext = pNext ; p->m_pPrev = this ; pNext->m_pPrev = p ; m_pNext = p ; } void InsertBefore( DLIST_ENTRY* p ) { /*++ Routine Description : Insert an item into the list before THIS ! Arguments : p - the item to be inserted ! Return Value None. --*/ _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; _ASSERT( p->m_pNext == p ) ; _ASSERT( p->m_pPrev == p ) ; DLIST_ENTRY* pPrev = m_pPrev ; p->m_pNext = this ; p->m_pPrev = pPrev ; pPrev->m_pNext = p ; m_pPrev = p ; } public : // // Initialize a list ! // DLIST_ENTRY() { m_pNext = this ; m_pPrev = this ; } // // It would be nice to comment out this Destructor in Retail builds, // however - VC5 has a compiler bug where if you allocate an array of // DLIST_ENTRY objects it adds a DWORD to hold the number of allocated // objects. Unless you have a Destructor (even a do nothing like this // one will be in retail), the delete[] operator won't do the math // to account for the DWORD counter - and you get Assert's etc... // in your memory allocators. // //#ifdef DEBUG // // Destroy an item in a list - should be empty when destroyed ! // ~DLIST_ENTRY() { _ASSERT( m_pNext == this ) ; _ASSERT( m_pPrev == this ) ; _ASSERT( m_pNext == m_pPrev ) ; } //#endif BOOL IsEmpty() { /*++ Routine Description : This function returns TRUE if there is nothing else in the list but us. Arguments : None. Return Value : TRUE if Empty, FALSE otherwise ! --*/ _ASSERT( m_pPrev != 0 && m_pNext != 0 ) ; return m_pPrev == this ; } void RemoveEntry( ) { /*++ Routine Description : Remote this item from the list ! Arguments : None. Return Value : None. --*/ _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; DLIST_ENTRY* pPrev = m_pPrev ; DLIST_ENTRY* pNext = m_pNext ; pPrev->m_pNext = pNext ; pNext->m_pPrev = pPrev ; m_pPrev = this ; m_pNext = this ; } void Join( DLIST_ENTRY& head ) { /*++ Routine Description : Take one list and join it with another. The referenced head of the list is not to become an element in the list, and is left with an empty head ! Arguments ; head - the head of the list that is to become empty, and whose elements are to be joined into this list ! Return Value : None. --*/ _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; if( !head.IsEmpty() ) { // // First - save the guy that is at the head of our list ! // DLIST_ENTRY* pNext = m_pNext ; head.m_pPrev->m_pNext = pNext ; pNext->m_pPrev = head.m_pPrev ; head.m_pNext->m_pPrev = this ; m_pNext = head.m_pNext ; head.m_pNext = &head ; head.m_pPrev = &head ; } _ASSERT( head.IsEmpty() ) ; } } ; class DLISTIterator { /*++ Class Description : Implement an iterator which can go both directions over doubly linked lists built on the DLIST_ENTRY class ! This is the base class for a set of templates that will provide iteration over generic items which contain DLIST_ENTRY objects for their list manipulation ! --*/ protected : // // The current position in the list ! // DLIST_ENTRY *m_pCur ; // // the DLIST_ENTRY which is both head & tail of the list // (since it is circular !) // DLIST_ENTRY *m_pHead ; public : // // TRUE if we're using the m_pNext pointers to go forward ! // This member should not be manipulated by clients - its exposed // for read only purposes only. // BOOL m_fForward ; DLISTIterator( DLIST_ENTRY* pHead, BOOL fForward = TRUE ) : m_pHead( pHead ), m_fForward( fForward ), m_pCur( fForward ? pHead->m_pNext : pHead->m_pPrev ) { _ASSERT( m_pHead != 0 ) ; } void ReBind( DLIST_ENTRY* pHead, BOOL fForward ) { m_pHead = pHead ; m_fForward = fForward ; m_pCur = fForward ? pHead->m_pNext : pHead->m_pPrev ; } void ReBind( DLIST_ENTRY* pHead ) { m_pHead = pHead ; m_pCur = m_fForward ? pHead->m_pNext : pHead->m_pPrev ; } void Prev() { /*++ Routine Description : This function moves the iterator back one slot. Note that m_pHead is the end of the list, and we avoiud setting m_pCur equal to m_pHead ! Arguments : None. Return Value : None. --*/ _ASSERT( m_pCur != m_pHead || m_pHead->IsEmpty() || m_fForward ) ; m_pCur = m_pCur->m_pPrev ; m_fForward = FALSE ; } void Next() { /*++ Routine Description : This function moves the iterator forward one slot. Note that m_pHead is the end of the list, and we avoiud setting m_pCur equal to m_pHead ! Arguments : None. Return Value : None. --*/ _ASSERT( m_pCur != m_pHead || m_pHead->IsEmpty() || !m_fForward ) ; m_pCur = m_pCur->m_pNext ; m_fForward = TRUE ; } void Front() { /*++ Routine Description : Reset the iterator to reference the first item of the list ! Arguments : None. Return Value : None. --*/ m_pCur = m_pHead->m_pNext ; m_fForward = TRUE ; } void Back() { /*++ Routine Description : Reset the iterator to reference the last item of the list ! Arguments : None. Return Value : None. --*/ m_pCur = m_pHead->m_pPrev ; m_fForward = FALSE ; } BOOL AtEnd() { /*++ Routine Description : Return TRUE if we are at the end of the list ! This is a little more complicated to compute - depends on which way we are going ! Arguments : None. Return Value : None. --*/ return m_pCur == m_pHead ; } DLIST_ENTRY* CurrentEntry() { return m_pCur ; } DLIST_ENTRY* RemoveItemEntry() { /*++ Routine Description : Remove the item that the iterator currently references from the list. If we are going forward then the iterator will be setting on the previous element, otherwise the iterator is left on the next element. We have to take care that we don't leave the iterator sitting on an invalid element. Arguments : None. Return Value : Pointer to the removed item. --*/ if( m_pCur == m_pHead ) return 0 ; DLIST_ENTRY* pTemp = m_pCur ; if( m_fForward ) { m_pCur = pTemp->m_pNext; } else { m_pCur = pTemp->m_pPrev ; } pTemp->RemoveEntry() ; return pTemp ; } void InsertBefore( DLIST_ENTRY* p ) { /*++ Routine Description : Insert an item before our current position in the list ! Arguments : None. Return Value : Nothin --*/ m_pCur->InsertBefore( p ) ; } void InsertAfter( DLIST_ENTRY* p ) { /*++ Routine Description : Insert an Item after our current position in the list ! Arguments : None. Return Value : Nothin --*/ m_pCur->InsertAfter( p ) ; } } ; template< class LISTHEAD > class TDListIterator : public DLISTIterator { /*++ Class Description : This class provides an iterator which can walk over a specified List ! --*/ public : typedef LISTHEAD::EXPORTDATA Data ; private : #if 0 // // Make the following functions private ! // They come from DLISTIterator and are not for use by our customers ! // void ReBind( DLIST_ENTRY* pHead, BOOL fForward ) ; void ReBind( DLIST_ENTRY* pHead ) ; #endif // // Make the following functions private ! // They come from DLISTIterator and are not for use by our customers ! // DLIST_ENTRY* RemoveItemEntry() ; // // Make the following functions private ! // They come from DLISTIterator and are not for use by our customers ! // DLIST_ENTRY* CurrentEntry() ; // // Make the following functions private ! // They come from DLISTIterator and are not for use by our customers ! // void InsertBefore( DLIST_ENTRY* ) ; // // Make the following functions private ! // They come from DLISTIterator and are not for use by our customers ! // void InsertAfter( DLIST_ENTRY* ) ; public : TDListIterator( LISTHEAD* pHead, BOOL fForward = TRUE ) : DLISTIterator( pHead, fForward ) { } TDListIterator( LISTHEAD& head, BOOL fForward = TRUE ) : DLISTIterator( &head, fForward ) { } TDListIterator( DLIST_ENTRY* pHead, BOOL fForward = TRUE ) : DLISTIterator( pHead, fForward ) { } void ReBind( LISTHEAD* pHead ) { DLISTIterator::ReBind( pHead ) ; } void ReBind( LISTHEAD* pHead, BOOL fForward ) { DLISTIterator::ReBind( pHead, fForward ) ; } inline Data* Current( ) { return LISTHEAD::Convert( m_pCur ) ; } inline Data* RemoveItem( ) { DLIST_ENTRY* pTemp = DLISTIterator::RemoveItemEntry() ; return LISTHEAD::Convert( pTemp ) ; } inline void InsertBefore( Data* p ) { DLIST_ENTRY* pTemp = LISTHEAD::Convert( p ) ; DLISTIterator::InsertBefore( pTemp ) ; } inline void InsertAfter( Data* p ) { DLIST_ENTRY* pTemp = LISTHEAD::Convert( p ) ; DLISTIterator::InsertAfter( pTemp ) ; } // // For debug purposes - let people know what the head is ! // LISTHEAD* GetHead() { return (LISTHEAD*)m_pHead ; } } ; template< class Data, Data::PFNDLIST pfnConvert > class TDListHead : private DLIST_ENTRY { /*++ Class Description : This class defines the head of a doubly linked list of items of DATAHELPER::LISTDATA We provide all the functions required to manipulate the list, and a mechanism for creating iterators. --*/ public : // // Publicly redefine the type that we deal with into a nice short form ! // typedef Data EXPORTDATA ; private : // // These kinds of iterators are our friends ! // friend class TDListIterator< TDListHead > ; static inline Data* Convert( DLIST_ENTRY* p ) { /*++ Routine Description : This function takes a pointer to a DLIST_ENTRY and returns a pointer to the beginning of the data item ! Arguments : p - pointer to a DLIST_ENTRY found within our list ! Return Value : Pointer to the Data Item containing the referenced DLIST_ENTRY ! --*/ if( p ) { return (Data*)(((PCHAR)p) - (PCHAR)(pfnConvert(0))) ; } return 0 ; } static inline DLIST_ENTRY* Convert( Data* pData ) { return pfnConvert(pData) ; } // // Copy Constructor and Operator= are private, as they don't make sense ! // public : // // Redefine this to be public ! // inline BOOL IsEmpty() { return DLIST_ENTRY::IsEmpty() ; } inline void PushFront( Data* pData ) { /*++ Routine Description : Push the Data item onto the front of the doubly linked list ! Arguments : pData - item to add to the front of the list Return Value : None. --*/ _ASSERT( pData != 0 ) ; _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; DLIST_ENTRY* p = Convert(pData); InsertAfter( p ) ; _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; } inline void PushBack( Data* pData ) { /*++ Routine Description : Push the Data item onto the back of the doubly linked list ! Arguments : pData - item to add to the front of the list Return Value : None. --*/ _ASSERT( pData != 0 ) ; _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; DLIST_ENTRY* p = Convert(pData) ; InsertBefore( p ) ; _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; } inline Data* PopFront() { /*++ Routine Description : Remove the data item from the front of the List ! Arguments : None. Return Value : The front of the list - NULL if empty ! --*/ _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; DLIST_ENTRY* pReturn = 0; if( m_pNext != this ) { pReturn = m_pNext ; pReturn->RemoveEntry() ; } _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; return Convert( pReturn ) ; } inline Data* PopBack() { /*++ Routine Description : Remove the data item from the Back of the List ! Arguments : None. Return Value : The Back of the list - NULL if empty ! --*/ _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; DLIST_ENTRY* pReturn = 0 ; if( m_pPrev != this ) { pReturn = m_pPrev ; pReturn->RemoveEntry() ; } _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; return Convert( pReturn ) ; } static inline void Remove( Data* pData ) { /*++ Routine Description : Remove the specified item from the list ! Arguments : None. Return Value : The Back of the list - NULL if empty ! --*/ DLIST_ENTRY* p = Convert( pData ) ; p->RemoveEntry() ; } inline Data* GetFront() { /*++ Routine Description : Return the data item from the Front of the List ! Arguments : None. Return Value : The Front of the list - NULL if empty ! --*/ _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; if( m_pNext == this ) { return 0 ; } return Convert( m_pNext ) ; } inline Data* GetBack() { /*++ Routine Description : Return the data item from the Back of the List ! Arguments : None. Return Value : The Back of the list - NULL if empty ! --*/ _ASSERT( m_pNext != 0 ) ; _ASSERT( m_pPrev != 0 ) ; if( m_pPrev == this ) { return 0 ; } return Convert( m_pPrev ) ; } inline void Join( TDListHead& head ) { /*++ Routine Description : Take one list and join it with another. The referenced head of the list is not to become an element in the list, and is left with an empty head ! Arguments ; head - the head of the list that is to become empty, and whose elements are to be joined into this list ! Return Value : None. --*/ DLIST_ENTRY::Join( head ) ; } } ; #endif // _TFDLIST_H_