wxlist.cpp
上传用户:liguizhu
上传日期:2015-11-01
资源大小:2422k
文件大小:24k
源码类别:

P2P编程

开发平台:

Visual C++

  1. //------------------------------------------------------------------------------
  2. // File: WXList.cpp
  3. //
  4. // Desc: DirectShow base classes - implements a non-MFC based generic list
  5. //       template class.
  6. //
  7. // Copyright (c) Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9. /* A generic list of pointers to objects.
  10.    Objectives: avoid using MFC libraries in ndm kernel mode and
  11.    provide a really useful list type.
  12.    The class is thread safe in that separate threads may add and
  13.    delete items in the list concurrently although the application
  14.    must ensure that constructor and destructor access is suitably
  15.    synchronised.
  16.    The list name must not conflict with MFC classes as an
  17.    application may use both
  18.    The nodes form a doubly linked, NULL terminated chain with an anchor
  19.    block (the list object per se) holding pointers to the first and last
  20.    nodes and a count of the nodes.
  21.    There is a node cache to reduce the allocation and freeing overhead.
  22.    It optionally (determined at construction time) has an Event which is
  23.    set whenever the list becomes non-empty and reset whenever it becomes
  24.    empty.
  25.    It optionally (determined at construction time) has a Critical Section
  26.    which is entered during the important part of each operation.  (About
  27.    all you can do outside it is some parameter checking).
  28.    The node cache is a repository of nodes that are NOT in the list to speed
  29.    up storage allocation.  Each list has its own cache to reduce locking and
  30.    serialising.  The list accesses are serialised anyway for a given list - a
  31.    common cache would mean that we would have to separately serialise access
  32.    of all lists within the cache.  Because the cache only stores nodes that are
  33.    not in the list, releasing the cache does not release any list nodes.  This
  34.    means that list nodes can be copied or rechained from one list to another
  35.    without danger of creating a dangling reference if the original cache goes
  36.    away.
  37.    Questionable design decisions:
  38.    1. Retaining the warts for compatibility
  39.    2. Keeping an element count -i.e. counting whenever we do anything
  40.       instead of only when we want the count.
  41.    3. Making the chain pointers NULL terminated.  If the list object
  42.       itself looks just like a node and the list is kept as a ring then
  43.       it reduces the number of special cases.  All inserts look the same.
  44. */
  45. #include <streams.h>
  46. /* set cursor to the position of each element of list in turn  */
  47. #define INTERNALTRAVERSELIST(list, cursor)               
  48. for ( cursor = (list).GetHeadPositionI()           
  49.     ; cursor!=NULL                               
  50.     ; cursor = (list).Next(cursor)                
  51.     )
  52. /* set cursor to the position of each element of list in turn
  53.    in reverse order
  54. */
  55. #define INTERNALREVERSETRAVERSELIST(list, cursor)        
  56. for ( cursor = (list).GetTailPositionI()           
  57.     ; cursor!=NULL                               
  58.     ; cursor = (list).Prev(cursor)                
  59.     )
  60. /* Constructor calls a separate initialisation function that
  61.    creates a node cache, optionally creates a lock object
  62.    and optionally creates a signaling object.
  63.    By default we create a locking object, a DEFAULTCACHE sized
  64.    cache but no event object so the list cannot be used in calls
  65.    to WaitForSingleObject
  66. */
  67. CBaseList::CBaseList(TCHAR *pName,    // Descriptive list name
  68.                      INT iItems) :    // Node cache size
  69. #ifdef DEBUG
  70.     CBaseObject(pName),
  71. #endif
  72.     m_pFirst(NULL),
  73.     m_pLast(NULL),
  74.     m_Count(0),
  75.     m_Cache(iItems)
  76. {
  77. } // constructor
  78. CBaseList::CBaseList(TCHAR *pName) :  // Descriptive list name
  79. #ifdef DEBUG
  80.     CBaseObject(pName),
  81. #endif
  82.     m_pFirst(NULL),
  83.     m_pLast(NULL),
  84.     m_Count(0),
  85.     m_Cache(DEFAULTCACHE)
  86. {
  87. } // constructor
  88. #ifdef UNICODE
  89. CBaseList::CBaseList(CHAR *pName,    // Descriptive list name
  90.                      INT iItems) :    // Node cache size
  91. #ifdef DEBUG
  92.     CBaseObject(pName),
  93. #endif
  94.     m_pFirst(NULL),
  95.     m_pLast(NULL),
  96.     m_Count(0),
  97.     m_Cache(iItems)
  98. {
  99. } // constructor
  100. CBaseList::CBaseList(CHAR *pName) :  // Descriptive list name
  101. #ifdef DEBUG
  102.     CBaseObject(pName),
  103. #endif
  104.     m_pFirst(NULL),
  105.     m_pLast(NULL),
  106.     m_Count(0),
  107.     m_Cache(DEFAULTCACHE)
  108. {
  109. } // constructor
  110. #endif
  111. /* The destructor enumerates all the node objects in the list and
  112.    in the cache deleting each in turn. We do not do any processing
  113.    on the objects that the list holds (i.e. points to) so if they
  114.    represent interfaces for example the creator of the list should
  115.    ensure that each of them is released before deleting us
  116. */
  117. CBaseList::~CBaseList()
  118. {
  119.     /* Delete all our list nodes */
  120.     RemoveAll();
  121. } // destructor
  122. /* Remove all the nodes from the list but don't do anything
  123.    with the objects that each node looks after (this is the
  124.    responsibility of the creator).
  125.    Aa a last act we reset the signalling event
  126.    (if available) to indicate to clients that the list
  127.    does not have any entries in it.
  128. */
  129. void CBaseList::RemoveAll()
  130. {
  131.     /* Free up all the CNode objects NOTE we don't bother putting the
  132.        deleted nodes into the cache as this method is only really called
  133.        in serious times of change such as when we are being deleted at
  134.        which point the cache will be deleted anway */
  135.     CNode *pn = m_pFirst;
  136.     while (pn) {
  137.         CNode *op = pn;
  138.         pn = pn->Next();
  139.         delete op;
  140.     }
  141.     /* Reset the object count and the list pointers */
  142.     m_Count = 0;
  143.     m_pFirst = m_pLast = NULL;
  144. } // RemoveAll
  145. /* Return a position enumerator for the entire list.
  146.    A position enumerator is a pointer to a node object cast to a
  147.    transparent type so all we do is return the head/tail node
  148.    pointer in the list.
  149.    WARNING because the position is a pointer to a node there is
  150.    an implicit assumption for users a the list class that after
  151.    deleting an object from the list that any other position
  152.    enumerators that you have may be invalid (since the node
  153.    may be gone).
  154. */
  155. POSITION CBaseList::GetHeadPositionI() const
  156. {
  157.     return (POSITION) m_pFirst;
  158. } // GetHeadPosition
  159. POSITION CBaseList::GetTailPositionI() const
  160. {
  161.     return (POSITION) m_pLast;
  162. } // GetTailPosition
  163. /* Get the number of objects in the list,
  164.    Get the lock before accessing the count.
  165.    Locking may not be entirely necessary but it has the side effect
  166.    of making sure that all operations are complete before we get it.
  167.    So for example if a list is being added to this list then that
  168.    will have completed in full before we continue rather than seeing
  169.    an intermediate albeit valid state
  170. */
  171. int CBaseList::GetCountI() const
  172. {
  173.     return m_Count;
  174. } // GetCount
  175. /* Return the object at rp, update rp to the next object from
  176.    the list or NULL if you have moved over the last object.
  177.    You may still call this function once we return NULL but
  178.    we will continue to return a NULL position value
  179. */
  180. void *CBaseList::GetNextI(POSITION& rp) const
  181. {
  182.     /* have we reached the end of the list */
  183.     if (rp == NULL) {
  184.         return NULL;
  185.     }
  186.     /* Lock the object before continuing */
  187.     void *pObject;
  188.     /* Copy the original position then step on */
  189.     CNode *pn = (CNode *) rp;
  190.     ASSERT(pn != NULL);
  191.     rp = (POSITION) pn->Next();
  192.     /* Get the object at the original position from the list */
  193.     pObject = pn->GetData();
  194.     // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.
  195.     return pObject;
  196. } //GetNext
  197. /* Return the object at p.
  198.    Asking for the object at NULL ASSERTs then returns NULL
  199.    The object is NOT locked.  The list is not being changed
  200.    in any way.  If another thread is busy deleting the object
  201.    then locking would only result in a change from one bad
  202.    behaviour to another.
  203. */
  204. void *CBaseList::GetI(POSITION p) const
  205. {
  206.     if (p == NULL) {
  207.         return NULL;
  208.     }
  209.     CNode * pn = (CNode *) p;
  210.     void *pObject = pn->GetData();
  211.     // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.
  212.     return pObject;
  213. } //Get
  214. /* Return the first position in the list which holds the given pointer.
  215.    Return NULL if it's not found.
  216. */
  217. POSITION CBaseList::FindI( void * pObj) const
  218. {
  219.     POSITION pn;
  220.     INTERNALTRAVERSELIST(*this, pn){
  221.         if (GetI(pn)==pObj) {
  222.             return pn;
  223.         }
  224.     }
  225.     return NULL;
  226. } // Find
  227. /* Remove the first node in the list (deletes the pointer to its object
  228.    from the list, does not free the object itself).
  229.    Return the pointer to its object or NULL if empty
  230. */
  231. void *CBaseList::RemoveHeadI()
  232. {
  233.     /* All we do is get the head position and ask for that to be deleted.
  234.        We could special case this since some of the code path checking
  235.        in Remove() is redundant as we know there is no previous
  236.        node for example but it seems to gain little over the
  237.        added complexity
  238.     */
  239.     return RemoveI((POSITION)m_pFirst);
  240. } // RemoveHead
  241. /* Remove the last node in the list (deletes the pointer to its object
  242.    from the list, does not free the object itself).
  243.    Return the pointer to its object or NULL if empty
  244. */
  245. void *CBaseList::RemoveTailI()
  246. {
  247.     /* All we do is get the tail position and ask for that to be deleted.
  248.        We could special case this since some of the code path checking
  249.        in Remove() is redundant as we know there is no previous
  250.        node for example but it seems to gain little over the
  251.        added complexity
  252.     */
  253.     return RemoveI((POSITION)m_pLast);
  254. } // RemoveTail
  255. /* Remove the pointer to the object in this position from the list.
  256.    Deal with all the chain pointers
  257.    Return a pointer to the object removed from the list.
  258.    The node object that is freed as a result
  259.    of this operation is added to the node cache where
  260.    it can be used again.
  261.    Remove(NULL) is a harmless no-op - but probably is a wart.
  262. */
  263. void *CBaseList::RemoveI(POSITION pos)
  264. {
  265.     /* Lock the critical section before continuing */
  266.     // ASSERT (pos!=NULL);     // Removing NULL is to be harmless!
  267.     if (pos==NULL) return NULL;
  268.     CNode *pCurrent = (CNode *) pos;
  269.     ASSERT(pCurrent != NULL);
  270.     /* Update the previous node */
  271.     CNode *pNode = pCurrent->Prev();
  272.     if (pNode == NULL) {
  273.         m_pFirst = pCurrent->Next();
  274.     } else {
  275.         pNode->SetNext(pCurrent->Next());
  276.     }
  277.     /* Update the following node */
  278.     pNode = pCurrent->Next();
  279.     if (pNode == NULL) {
  280.         m_pLast = pCurrent->Prev();
  281.     } else {
  282.         pNode->SetPrev(pCurrent->Prev());
  283.     }
  284.     /* Get the object this node was looking after */
  285.     void *pObject = pCurrent->GetData();
  286.     // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.
  287.     /* Try and add the node object to the cache -
  288.        a NULL return code from the cache means we ran out of room.
  289.        The cache size is fixed by a constructor argument when the
  290.        list is created and defaults to DEFAULTCACHE.
  291.        This means that the cache will have room for this many
  292.        node objects. So if you have a list of media samples
  293.        and you know there will never be more than five active at
  294.        any given time of them for example then override the default
  295.        constructor
  296.     */
  297.     m_Cache.AddToCache(pCurrent);
  298.     /* If the list is empty then reset the list event */
  299.     --m_Count;
  300.     ASSERT(m_Count >= 0);
  301.     return pObject;
  302. } // Remove
  303. /* Add this object to the tail end of our list
  304.    Return the new tail position.
  305. */
  306. POSITION CBaseList::AddTailI(void *pObject)
  307. {
  308.     /* Lock the critical section before continuing */
  309.     CNode *pNode;
  310.     // ASSERT(pObject);   // NULL pointers in the list are allowed.
  311.     /* If there is a node objects in the cache then use
  312.        that otherwise we will have to create a new one */
  313.     pNode = (CNode *) m_Cache.RemoveFromCache();
  314.     if (pNode == NULL) {
  315.         pNode = new CNode;
  316.     }
  317.     /* Check we have a valid object */
  318.     if (pNode == NULL) {
  319.         return NULL;
  320.     }
  321.     /* Initialise all the CNode object
  322.        just in case it came from the cache
  323.     */
  324.     pNode->SetData(pObject);
  325.     pNode->SetNext(NULL);
  326.     pNode->SetPrev(m_pLast);
  327.     if (m_pLast == NULL) {
  328.         m_pFirst = pNode;
  329.     } else {
  330.         m_pLast->SetNext(pNode);
  331.     }
  332.     /* Set the new last node pointer and also increment the number
  333.        of list entries, the critical section is unlocked when we
  334.        exit the function
  335.     */
  336.     m_pLast = pNode;
  337.     ++m_Count;
  338.     return (POSITION) pNode;
  339. } // AddTail(object)
  340. /* Add this object to the head end of our list
  341.    Return the new head position.
  342. */
  343. POSITION CBaseList::AddHeadI(void *pObject)
  344. {
  345.     CNode *pNode;
  346.     // ASSERT(pObject);  // NULL pointers in the list are allowed.
  347.     /* If there is a node objects in the cache then use
  348.        that otherwise we will have to create a new one */
  349.     pNode = (CNode *) m_Cache.RemoveFromCache();
  350.     if (pNode == NULL) {
  351.         pNode = new CNode;
  352.     }
  353.     /* Check we have a valid object */
  354.     if (pNode == NULL) {
  355.         return NULL;
  356.     }
  357.     /* Initialise all the CNode object
  358.        just in case it came from the cache
  359.     */
  360.     pNode->SetData(pObject);
  361.     /* chain it in (set four pointers) */
  362.     pNode->SetPrev(NULL);
  363.     pNode->SetNext(m_pFirst);
  364.     if (m_pFirst == NULL) {
  365.         m_pLast = pNode;
  366.     } else {
  367.         m_pFirst->SetPrev(pNode);
  368.     }
  369.     m_pFirst = pNode;
  370.     ++m_Count;
  371.     return (POSITION) pNode;
  372. } // AddHead(object)
  373. /* Add all the elements in *pList to the tail of this list.
  374.    Return TRUE if it all worked, FALSE if it didn't.
  375.    If it fails some elements may have been added.
  376. */
  377. BOOL CBaseList::AddTail(CBaseList *pList)
  378. {
  379.     /* lock the object before starting then enumerate
  380.        each entry in the source list and add them one by one to
  381.        our list (while still holding the object lock)
  382.        Lock the other list too.
  383.     */
  384.     POSITION pos = pList->GetHeadPositionI();
  385.     while (pos) {
  386.        if (NULL == AddTailI(pList->GetNextI(pos))) {
  387.            return FALSE;
  388.        }
  389.     }
  390.     return TRUE;
  391. } // AddTail(list)
  392. /* Add all the elements in *pList to the head of this list.
  393.    Return TRUE if it all worked, FALSE if it didn't.
  394.    If it fails some elements may have been added.
  395. */
  396. BOOL CBaseList::AddHead(CBaseList *pList)
  397. {
  398.     /* lock the object before starting then enumerate
  399.        each entry in the source list and add them one by one to
  400.        our list (while still holding the object lock)
  401.        Lock the other list too.
  402.        To avoid reversing the list, traverse it backwards.
  403.     */
  404.     POSITION pos;
  405.     INTERNALREVERSETRAVERSELIST(*pList, pos) {
  406.         if (NULL== AddHeadI(pList->GetI(pos))){
  407.             return FALSE;
  408.         }
  409.     }
  410.     return TRUE;
  411. } // AddHead(list)
  412. /* Add the object after position p
  413.    p is still valid after the operation.
  414.    AddAfter(NULL,x) adds x to the start - same as AddHead
  415.    Return the position of the new object, NULL if it failed
  416. */
  417. POSITION  CBaseList::AddAfterI(POSITION pos, void * pObj)
  418. {
  419.     if (pos==NULL)
  420.         return AddHeadI(pObj);
  421.     /* As someone else might be furkling with the list -
  422.        Lock the critical section before continuing
  423.     */
  424.     CNode *pAfter = (CNode *) pos;
  425.     ASSERT(pAfter != NULL);
  426.     if (pAfter==m_pLast)
  427.         return AddTailI(pObj);
  428.     /* set pnode to point to a new node, preferably from the cache */
  429.     CNode *pNode = (CNode *) m_Cache.RemoveFromCache();
  430.     if (pNode == NULL) {
  431.         pNode = new CNode;
  432.     }
  433.     /* Check we have a valid object */
  434.     if (pNode == NULL) {
  435.         return NULL;
  436.     }
  437.     /* Initialise all the CNode object
  438.        just in case it came from the cache
  439.     */
  440.     pNode->SetData(pObj);
  441.     /* It is to be added to the middle of the list - there is a before
  442.        and after node.  Chain it after pAfter, before pBefore.
  443.     */
  444.     CNode * pBefore = pAfter->Next();
  445.     ASSERT(pBefore != NULL);
  446.     /* chain it in (set four pointers) */
  447.     pNode->SetPrev(pAfter);
  448.     pNode->SetNext(pBefore);
  449.     pBefore->SetPrev(pNode);
  450.     pAfter->SetNext(pNode);
  451.     ++m_Count;
  452.     return (POSITION) pNode;
  453. } // AddAfter(object)
  454. BOOL CBaseList::AddAfter(POSITION p, CBaseList *pList)
  455. {
  456.     POSITION pos;
  457.     INTERNALTRAVERSELIST(*pList, pos) {
  458.         /* p follows along the elements being added */
  459.         p = AddAfterI(p, pList->GetI(pos));
  460.         if (p==NULL) return FALSE;
  461.     }
  462.     return TRUE;
  463. } // AddAfter(list)
  464. /* Mirror images:
  465.    Add the element or list after position p.
  466.    p is still valid after the operation.
  467.    AddBefore(NULL,x) adds x to the end - same as AddTail
  468. */
  469. POSITION CBaseList::AddBeforeI(POSITION pos, void * pObj)
  470. {
  471.     if (pos==NULL)
  472.         return AddTailI(pObj);
  473.     /* set pnode to point to a new node, preferably from the cache */
  474.     CNode *pBefore = (CNode *) pos;
  475.     ASSERT(pBefore != NULL);
  476.     if (pBefore==m_pFirst)
  477.         return AddHeadI(pObj);
  478.     CNode * pNode = (CNode *) m_Cache.RemoveFromCache();
  479.     if (pNode == NULL) {
  480.         pNode = new CNode;
  481.     }
  482.     /* Check we have a valid object */
  483.     if (pNode == NULL) {
  484.         return NULL;
  485.     }
  486.     /* Initialise all the CNode object
  487.        just in case it came from the cache
  488.     */
  489.     pNode->SetData(pObj);
  490.     /* It is to be added to the middle of the list - there is a before
  491.        and after node.  Chain it after pAfter, before pBefore.
  492.     */
  493.     CNode * pAfter = pBefore->Prev();
  494.     ASSERT(pAfter != NULL);
  495.     /* chain it in (set four pointers) */
  496.     pNode->SetPrev(pAfter);
  497.     pNode->SetNext(pBefore);
  498.     pBefore->SetPrev(pNode);
  499.     pAfter->SetNext(pNode);
  500.     ++m_Count;
  501.     return (POSITION) pNode;
  502. } // Addbefore(object)
  503. BOOL CBaseList::AddBefore(POSITION p, CBaseList *pList)
  504. {
  505.     POSITION pos;
  506.     INTERNALREVERSETRAVERSELIST(*pList, pos) {
  507.         /* p follows along the elements being added */
  508.         p = AddBeforeI(p, pList->GetI(pos));
  509.         if (p==NULL) return FALSE;
  510.     }
  511.     return TRUE;
  512. } // AddBefore(list)
  513. /* Split *this after position p in *this
  514.    Retain as *this the tail portion of the original *this
  515.    Add the head portion to the tail end of *pList
  516.    Return TRUE if it all worked, FALSE if it didn't.
  517.    e.g.
  518.       foo->MoveToTail(foo->GetHeadPosition(), bar);
  519.           moves one element from the head of foo to the tail of bar
  520.       foo->MoveToTail(NULL, bar);
  521.           is a no-op
  522.       foo->MoveToTail(foo->GetTailPosition, bar);
  523.           concatenates foo onto the end of bar and empties foo.
  524.    A better, except excessively long name might be
  525.        MoveElementsFromHeadThroughPositionToOtherTail
  526. */
  527. BOOL CBaseList::MoveToTail
  528.         (POSITION pos, CBaseList *pList)
  529. {
  530.     /* Algorithm:
  531.        Note that the elements (including their order) in the concatenation
  532.        of *pList to the head of *this is invariant.
  533.        1. Count elements to be moved
  534.        2. Join *pList onto the head of this to make one long chain
  535.        3. Set first/Last pointers in *this and *pList
  536.        4. Break the chain at the new place
  537.        5. Adjust counts
  538.        6. Set/Reset any events
  539.     */
  540.     if (pos==NULL) return TRUE;  // no-op.  Eliminates special cases later.
  541.     /* Make cMove the number of nodes to move */
  542.     CNode * p = (CNode *)pos;
  543.     int cMove = 0;            // number of nodes to move
  544.     while(p!=NULL) {
  545.        p = p->Prev();
  546.        ++cMove;
  547.     }
  548.     /* Join the two chains together */
  549.     if (pList->m_pLast!=NULL)
  550.         pList->m_pLast->SetNext(m_pFirst);
  551.     if (m_pFirst!=NULL)
  552.         m_pFirst->SetPrev(pList->m_pLast);
  553.     /* set first and last pointers */
  554.     p = (CNode *)pos;
  555.     if (pList->m_pFirst==NULL)
  556.         pList->m_pFirst = m_pFirst;
  557.     m_pFirst = p->Next();
  558.     if (m_pFirst==NULL)
  559.         m_pLast = NULL;
  560.     pList->m_pLast = p;
  561.     /* Break the chain after p to create the new pieces */
  562.     if (m_pFirst!=NULL)
  563.         m_pFirst->SetPrev(NULL);
  564.     p->SetNext(NULL);
  565.     /* Adjust the counts */
  566.     m_Count -= cMove;
  567.     pList->m_Count += cMove;
  568.     return TRUE;
  569. } // MoveToTail
  570. /* Mirror image of MoveToTail:
  571.    Split *this before position p in *this.
  572.    Retain in *this the head portion of the original *this
  573.    Add the tail portion to the start (i.e. head) of *pList
  574.    Return TRUE if it all worked, FALSE if it didn't.
  575.    e.g.
  576.       foo->MoveToHead(foo->GetTailPosition(), bar);
  577.           moves one element from the tail of foo to the head of bar
  578.       foo->MoveToHead(NULL, bar);
  579.           is a no-op
  580.       foo->MoveToHead(foo->GetHeadPosition, bar);
  581.           concatenates foo onto the start of bar and empties foo.
  582. */
  583. BOOL CBaseList::MoveToHead
  584.         (POSITION pos, CBaseList *pList)
  585. {
  586.     /* See the comments on the algorithm in MoveToTail */
  587.     if (pos==NULL) return TRUE;  // no-op.  Eliminates special cases later.
  588.     /* Make cMove the number of nodes to move */
  589.     CNode * p = (CNode *)pos;
  590.     int cMove = 0;            // number of nodes to move
  591.     while(p!=NULL) {
  592.        p = p->Next();
  593.        ++cMove;
  594.     }
  595.     /* Join the two chains together */
  596.     if (pList->m_pFirst!=NULL)
  597.         pList->m_pFirst->SetPrev(m_pLast);
  598.     if (m_pLast!=NULL)
  599.         m_pLast->SetNext(pList->m_pFirst);
  600.     /* set first and last pointers */
  601.     p = (CNode *)pos;
  602.     if (pList->m_pLast==NULL)
  603.         pList->m_pLast = m_pLast;
  604.     m_pLast = p->Prev();
  605.     if (m_pLast==NULL)
  606.         m_pFirst = NULL;
  607.     pList->m_pFirst = p;
  608.     /* Break the chain after p to create the new pieces */
  609.     if (m_pLast!=NULL)
  610.         m_pLast->SetNext(NULL);
  611.     p->SetPrev(NULL);
  612.     /* Adjust the counts */
  613.     m_Count -= cMove;
  614.     pList->m_Count += cMove;
  615.     return TRUE;
  616. } // MoveToHead
  617. /* Reverse the order of the [pointers to] objects in *this
  618. */
  619. void CBaseList::Reverse()
  620. {
  621.     /* algorithm:
  622.        The obvious booby trap is that you flip pointers around and lose
  623.        addressability to the node that you are going to process next.
  624.        The easy way to avoid this is do do one chain at a time.
  625.        Run along the forward chain,
  626.        For each node, set the reverse pointer to the one ahead of us.
  627.        The reverse chain is now a copy of the old forward chain, including
  628.        the NULL termination.
  629.        Run along the reverse chain (i.e. old forward chain again)
  630.        For each node set the forward pointer of the node ahead to point back
  631.        to the one we're standing on.
  632.        The first node needs special treatment,
  633.        it's new forward pointer is NULL.
  634.        Finally set the First/Last pointers
  635.     */
  636.     CNode * p;
  637.     // Yes we COULD use a traverse, but it would look funny!
  638.     p = m_pFirst;
  639.     while (p!=NULL) {
  640.         CNode * q;
  641.         q = p->Next();
  642.         p->SetNext(p->Prev());
  643.         p->SetPrev(q);
  644.         p = q;
  645.     }
  646.     p = m_pFirst;
  647.     m_pFirst = m_pLast;
  648.     m_pLast = p;
  649. #if 0     // old version
  650.     if (m_pFirst==NULL) return;          // empty list
  651.     if (m_pFirst->Next()==NULL) return;  // single node list
  652.     /* run along forward chain */
  653.     for ( p = m_pFirst
  654.         ; p!=NULL
  655.         ; p = p->Next()
  656.         ){
  657.         p->SetPrev(p->Next());
  658.     }
  659.     /* special case first element */
  660.     m_pFirst->SetNext(NULL);     // fix the old first element
  661.     /* run along new reverse chain i.e. old forward chain again */
  662.     for ( p = m_pFirst           // start at the old first element
  663.         ; p->Prev()!=NULL        // while there's a node still to be set
  664.         ; p = p->Prev()          // work in the same direction as before
  665.         ){
  666.         p->Prev()->SetNext(p);
  667.     }
  668.     /* fix forward and reverse pointers
  669.        - the triple XOR swap would work but all the casts look hideous */
  670.     p = m_pFirst;
  671.     m_pFirst = m_pLast;
  672.     m_pLast = p;
  673. #endif
  674. } // Reverse