|
|
1.1 root 1: /////////////////////////////////////////////////////////////////////////////
2: // class CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE> - a list containing 'TYPE' elements
3: // passed in parameters as ARG_TYPE
4: //
5: // This is a part of the Microsoft Foundation Classes C++ library.
6: // Copyright (C) 1992 Microsoft Corporation
7: // All rights reserved.
8: //
9: // This source code is only intended as a supplement to the
10: // Microsoft Foundation Classes Reference and Microsoft
11: // QuickHelp documentation provided with the library.
12: // See these sources for detailed information regarding the
13: // Microsoft Foundation Classes product.
14: /////////////////////////////////////////////////////////////////////////////
15:
16: //$DECLARE_TEMPLATE
17:
18: /////////////////////////////////////////////////////////////////////////////
19:
20: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
21: class CList : public CObject
22: {
23: #if IS_SERIAL
24: DECLARE_SERIAL(CList)
25: #else
26: DECLARE_DYNAMIC(CList)
27: #endif //!IS_SERIAL
28:
29: protected:
30: struct CNode
31: {
32: CNode* pNext;
33: CNode* pPrev;
34: TYPE data;
35: };
36: public:
37:
38: // Construction
39: CList(int nBlockSize=10);
40:
41: // Attributes (head and tail)
42: // count of elements
43: int GetCount() const
44: { return m_nCount; }
45: BOOL IsEmpty() const
46: { return m_nCount == 0; }
47:
48: // peek at head or tail
49: TYPE& GetHead()
50: { ASSERT(m_pNodeHead != NULL);
51: return m_pNodeHead->data; }
52: TYPE GetHead() const
53: { ASSERT(m_pNodeHead != NULL);
54: return m_pNodeHead->data; }
55: TYPE& GetTail()
56: { ASSERT(m_pNodeTail != NULL);
57: return m_pNodeTail->data; }
58: TYPE GetTail() const
59: { ASSERT(m_pNodeTail != NULL);
60: return m_pNodeTail->data; }
61:
62: // Operations
63: // get head or tail (and remove it) - don't call on empty list !
64: TYPE RemoveHead();
65: TYPE RemoveTail();
66:
67: // add before head or after tail
68: POSITION AddHead(ARG_TYPE newElement);
69: POSITION AddTail(ARG_TYPE newElement);
70:
71: // add another list of elements before head or after tail
72: void AddHead(CList* pNewList);
73: void AddTail(CList* pNewList);
74:
75: // remove all elements
76: void RemoveAll();
77:
78: // iteration
79: POSITION GetHeadPosition() const
80: { return (POSITION) m_pNodeHead; }
81: POSITION GetTailPosition() const
82: { return (POSITION) m_pNodeTail; }
83: TYPE& GetNext(POSITION& rPosition) // return *Position++
84: { CNode* pNode = (CNode*) rPosition;
85: ASSERT(pNode != NULL);
86: rPosition = (POSITION) pNode->pNext;
87: return pNode->data; }
88: TYPE GetNext(POSITION& rPosition) const // return *Position++
89: { CNode* pNode = (CNode*) rPosition;
90: ASSERT(pNode != NULL);
91: rPosition = (POSITION) pNode->pNext;
92: return pNode->data; }
93: TYPE& GetPrev(POSITION& rPosition) // return *Position--
94: { CNode* pNode = (CNode*) rPosition;
95: ASSERT(pNode != NULL);
96: rPosition = (POSITION) pNode->pPrev;
97: return pNode->data; }
98: TYPE GetPrev(POSITION& rPosition) const // return *Position--
99: { CNode* pNode = (CNode*) rPosition;
100: ASSERT(pNode != NULL);
101: rPosition = (POSITION) pNode->pPrev;
102: return pNode->data; }
103:
104: // getting/modifying an element at a given position
105: TYPE& GetAt(POSITION position)
106: { CNode* pNode = (CNode*) position;
107: ASSERT(pNode != NULL);
108: return pNode->data; }
109: TYPE GetAt(POSITION position) const
110: { CNode* pNode = (CNode*) position;
111: ASSERT(pNode != NULL);
112: return pNode->data; }
113: void SetAt(POSITION pos, ARG_TYPE newElement)
114: { CNode* pNode = (CNode*) pos;
115: ASSERT(pNode != NULL);
116: pNode->data = newElement; }
117: void RemoveAt(POSITION position);
118:
119: // inserting before or after a given position
120: POSITION InsertBefore(POSITION position, ARG_TYPE newElement);
121: POSITION InsertAfter(POSITION position, ARG_TYPE newElement);
122:
123: // helper functions (note: O(n) speed)
124: POSITION Find(ARG_TYPE searchValue, POSITION startAfter = NULL) const;
125: // defaults to starting at the HEAD
126: // return NULL if not found
127: POSITION FindIndex(int nIndex) const;
128: // get the 'nIndex'th element (may return NULL)
129:
130: // Implementation
131: protected:
132: CNode* m_pNodeHead;
133: CNode* m_pNodeTail;
134: int m_nCount;
135: CNode* m_pNodeFree;
136: struct CPlex* m_pBlocks;
137: int m_nBlockSize;
138:
139: CNode* NewNode(CNode*, CNode*);
140: void FreeNode(CNode*);
141:
142: public:
143: ~CList();
144: #if IS_SERIAL
145: void Serialize(CArchive&);
146: #endif //IS_SERIAL
147: #ifdef _DEBUG
148: void Dump(CDumpContext&) const;
149: void AssertValid() const;
150: #endif
151: };
152:
153: //$IMPLEMENT_TEMPLATE
154:
155: /////////////////////////////////////////////////////////////////////////////
156: //
157: // Implementation of List of TYPEs
158: //
159: /////////////////////////////////////////////////////////////////////////////
160:
161: #include "afxcoll.h"
162: #pragma hdrstop
163:
164: #include "plex.h"
165:
166: #ifdef AFX_COLL_SEG
167: #pragma code_seg(AFX_COLL_SEG)
168: #endif
169:
170: #if IS_SERIAL
171: IMPLEMENT_SERIAL(CList, CObject, 0);
172: #else
173: IMPLEMENT_DYNAMIC(CList, CObject);
174: #endif //!IS_SERIAL
175:
176: #ifdef _DEBUG
177: #undef THIS_FILE
178: static char BASED_CODE THIS_FILE[] = __FILE__;
179: #endif
180:
181: #if HAS_CREATE
182: #include "elements.h" // used for special creation
183: #endif
184:
185: /////////////////////////////////////////////////////////////////////////////
186:
187: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
188: CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::CList(int nBlockSize)
189: {
190: ASSERT(nBlockSize > 0);
191:
192: m_nCount = 0;
193: m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
194: m_pBlocks = NULL;
195: m_nBlockSize = nBlockSize;
196: }
197:
198: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
199: void CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::RemoveAll()
200: {
201: ASSERT_VALID(this);
202:
203: // destroy elements
204: #if HAS_CREATE
205: register CNode* pNode;
206: for (pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext)
207: DestructElement(&pNode->data);
208: #endif
209:
210: m_nCount = 0;
211: m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
212: m_pBlocks->FreeDataChain();
213: m_pBlocks = NULL;
214: }
215:
216: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
217: CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::~CList()
218: {
219: RemoveAll();
220: ASSERT(m_nCount == 0);
221: }
222:
223: /////////////////////////////////////////////////////////////////////////////
224: // Node helpers
225: /*
226: * Implementation note: CNode's are stored in CPlex blocks and
227: * chained together. Free blocks are maintained in a singly linked list
228: * using the 'pNext' member of CNode with 'm_pNodeFree' as the head.
229: * Used blocks are maintained in a doubly linked list using both 'pNext'
230: * and 'pPrev' as links and 'm_pNodeHead' and 'm_pNodeTail'
231: * as the head/tail.
232: *
233: * We never free a CPlex block unless the List is destroyed or RemoveAll()
234: * is used - so the total number of CPlex blocks may grow large depending
235: * on the maximum past size of the list.
236: */
237:
238: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
239: CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::CNode*
240: CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::NewNode(CList::CNode* pPrev, CList::CNode* pNext)
241: {
242: if (m_pNodeFree == NULL)
243: {
244: // add another block
245: CPlex* pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize,
246: sizeof(CNode));
247:
248: // chain them into free list
249: CNode* pNode = (CNode*) pNewBlock->data();
250: // free in reverse order to make it easier to debug
251: pNode += m_nBlockSize - 1;
252: for (int i = m_nBlockSize-1; i >= 0; i--, pNode--)
253: {
254: pNode->pNext = m_pNodeFree;
255: m_pNodeFree = pNode;
256: }
257: }
258: ASSERT(m_pNodeFree != NULL); // we must have something
259:
260: register CList::CNode* pNode = m_pNodeFree;
261: m_pNodeFree = m_pNodeFree->pNext;
262: pNode->pPrev = pPrev;
263: pNode->pNext = pNext;
264: m_nCount++;
265: ASSERT(m_nCount > 0); // make sure we don't overflow
266:
267: #if HAS_CREATE
268: ConstructElement(&pNode->data);
269: #else
270: memset(&pNode->data, 0, sizeof(TYPE)); // zero fill
271: #endif
272: return pNode;
273: }
274:
275: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
276: void CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::FreeNode(CList::CNode* pNode)
277: {
278: #if HAS_CREATE
279: DestructElement(&pNode->data);
280: #endif
281: pNode->pNext = m_pNodeFree;
282: m_pNodeFree = pNode;
283: m_nCount--;
284: ASSERT(m_nCount >= 0); // make sure we don't underflow
285: }
286:
287: /////////////////////////////////////////////////////////////////////////////
288:
289: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
290: POSITION CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::AddHead(ARG_TYPE newElement)
291: {
292: ASSERT_VALID(this);
293:
294: CNode* pNewNode = NewNode(NULL, m_pNodeHead);
295: pNewNode->data = newElement;
296: if (m_pNodeHead != NULL)
297: m_pNodeHead->pPrev = pNewNode;
298: else
299: m_pNodeTail = pNewNode;
300: m_pNodeHead = pNewNode;
301: return (POSITION) pNewNode;
302: }
303:
304: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
305: POSITION CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::AddTail(ARG_TYPE newElement)
306: {
307: ASSERT_VALID(this);
308:
309: CNode* pNewNode = NewNode(m_pNodeTail, NULL);
310: pNewNode->data = newElement;
311: if (m_pNodeTail != NULL)
312: m_pNodeTail->pNext = pNewNode;
313: else
314: m_pNodeHead = pNewNode;
315: m_pNodeTail = pNewNode;
316: return (POSITION) pNewNode;
317: }
318:
319: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
320: void CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::AddHead(CList* pNewList)
321: {
322: ASSERT_VALID(this);
323:
324: ASSERT(pNewList != NULL);
325: ASSERT(pNewList->IsKindOf(RUNTIME_CLASS(CList)));
326: ASSERT_VALID(pNewList);
327:
328: // add a list of same elements to head (maintain order)
329: POSITION pos = pNewList->GetTailPosition();
330: while (pos != NULL)
331: AddHead(pNewList->GetPrev(pos));
332: }
333:
334: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
335: void CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::AddTail(CList* pNewList)
336: {
337: ASSERT_VALID(this);
338: ASSERT(pNewList != NULL);
339: ASSERT(pNewList->IsKindOf(RUNTIME_CLASS(CList)));
340: ASSERT_VALID(pNewList);
341:
342: // add a list of same elements
343: POSITION pos = pNewList->GetHeadPosition();
344: while (pos)
345: AddTail(pNewList->GetNext(pos));
346: }
347:
348: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
349: TYPE CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::RemoveHead()
350: {
351: ASSERT_VALID(this);
352: ASSERT(m_pNodeHead != NULL); // don't call on empty list !!!
353:
354: CNode* pOldNode = m_pNodeHead;
355: ASSERT(pOldNode != NULL);
356: TYPE returnValue = pOldNode->data;
357:
358: m_pNodeHead = pOldNode->pNext;
359: if (m_pNodeHead != NULL)
360: m_pNodeHead->pPrev = NULL;
361: else
362: m_pNodeTail = NULL;
363: FreeNode(pOldNode);
364: return returnValue;
365: }
366:
367: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
368: TYPE CList<TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>::RemoveTail()
369: {
370: ASSERT_VALID(this);
371: ASSERT(m_pNodeTail != NULL); // don't call on empty list !!!
372:
373: CNode* pOldNode = m_pNodeTail;
374: ASSERT(pOldNode != NULL);
375: TYPE returnValue = pOldNode->data;
376:
377: m_pNodeTail = pOldNode->pPrev;
378: if (m_pNodeTail != NULL)
379: m_pNodeTail->pNext = NULL;
380: else
381: m_pNodeHead = NULL;
382: FreeNode(pOldNode);
383: return returnValue;
384: }
385:
386: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
387: POSITION CList<TYPE>::InsertBefore(POSITION position, ARG_TYPE newElement)
388: {
389: ASSERT_VALID(this);
390:
391: if (position == NULL)
392: return AddHead(newElement); // insert before nothing -> head of the list
393:
394: // Insert it before position
395: CNode* pOldNode = (CNode*) position;
396: CNode* pNewNode = NewNode(pOldNode->pPrev, pOldNode);
397: pNewNode->data = newElement;
398:
399: if (pOldNode->pPrev != NULL)
400: {
401: pOldNode->pPrev->pNext = pNewNode;
402: }
403: else
404: {
405: ASSERT(pOldNode == m_pNodeHead);
406: m_pNodeHead = pNewNode;
407: }
408: pOldNode->pPrev = pNewNode;
409: return (POSITION) pNewNode;
410: }
411:
412: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
413: POSITION CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::InsertAfter(POSITION position, ARG_TYPE newElement)
414: {
415: ASSERT_VALID(this);
416:
417: if (position == NULL)
418: return AddTail(newElement); // insert after nothing -> tail of the list
419:
420: // Insert it before position
421: CNode* pOldNode = (CNode*) position;
422: CNode* pNewNode = NewNode(pOldNode, pOldNode->pNext);
423: pNewNode->data = newElement;
424:
425: if (pOldNode->pNext != NULL)
426: {
427: pOldNode->pNext->pPrev = pNewNode;
428: }
429: else
430: {
431: ASSERT(pOldNode == m_pNodeTail);
432: m_pNodeTail = pNewNode;
433: }
434: pOldNode->pNext = pNewNode;
435: return (POSITION) pNewNode;
436: }
437:
438: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
439: void CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::RemoveAt(POSITION position)
440: {
441: ASSERT_VALID(this);
442: ASSERT(position != NULL);
443:
444: CNode* pOldNode = (CNode*) position;
445:
446: // remove pOldNode from list
447: if (pOldNode == m_pNodeHead)
448: m_pNodeHead = pOldNode->pNext;
449: else
450: pOldNode->pPrev->pNext = pOldNode->pNext;
451: if (pOldNode == m_pNodeTail)
452: m_pNodeTail = pOldNode->pPrev;
453: else
454: pOldNode->pNext->pPrev = pOldNode->pPrev;
455: FreeNode(pOldNode);
456: }
457:
458:
459: /////////////////////////////////////////////////////////////////////////////
460: // slow operations
461:
462: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
463: POSITION CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::FindIndex(int nIndex) const
464: {
465: ASSERT_VALID(this);
466: ASSERT(nIndex >= 0);
467:
468: if (nIndex >= m_nCount)
469: return NULL; // went too far
470:
471: register CNode* pNode = m_pNodeHead;
472: while (nIndex--)
473: pNode = pNode->pNext;
474: return (POSITION) pNode;
475: }
476:
477: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
478: POSITION CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::Find(ARG_TYPE searchValue, POSITION startAfter) const
479: {
480: ASSERT_VALID(this);
481:
482: register CNode* pNode = (CNode*) startAfter;
483: if (pNode == NULL)
484: pNode = m_pNodeHead; // start at head
485: else
486: pNode = pNode->pNext; // start after the one specified
487:
488: for (; pNode != NULL; pNode = pNode->pNext)
489: if (pNode->data == searchValue)
490: return (POSITION) pNode;
491: return NULL;
492: }
493:
494: /////////////////////////////////////////////////////////////////////////////
495: // Serialization
496:
497: #if IS_SERIAL
498: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
499: void CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::Serialize(CArchive& ar)
500: {
501: ASSERT_VALID(this);
502:
503: CObject::Serialize(ar);
504:
505: if (ar.IsStoring())
506: {
507: ar << (WORD) m_nCount;
508: for (CNode* pNode = m_pNodeHead; pNode != NULL;
509: pNode = pNode->pNext)
510: ar << pNode->data;
511: }
512: else
513: {
514: WORD nNewCount;
515: ar >> nNewCount;
516: while (nNewCount--)
517: {
518: TYPE newData;
519: ar >> newData;
520: AddTail(newData);
521: }
522: }
523: }
524: #endif //IS_SERIAL
525:
526: /////////////////////////////////////////////////////////////////////////////
527: // Diagnostics
528:
529: #ifdef _DEBUG
530: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
531: void CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::Dump(CDumpContext& dc) const
532: {
533: ASSERT_VALID(this);
534:
535: #define MAKESTRING(x) #x
536: dc << "a " MAKESTRING(CList) " with " << m_nCount << " elements";
537: #undef MAKESTRING
538: if (dc.GetDepth() > 0)
539: {
540: POSITION pos = GetHeadPosition();
541: dc << "\n";
542:
543: while (pos != NULL)
544: dc << "\n\t" << GetNext(pos);
545: }
546: }
547:
548: template <class TYPE, class ARG_TYPE, int IS_SERIAL, int HAS_CREATE>
549: void CList<TYPE, ARG_TYPE, IS_SERIAL, HAS_CREATE>::AssertValid() const
550: {
551: CObject::AssertValid();
552:
553: if (m_nCount == 0)
554: {
555: // empty list
556: ASSERT(m_pNodeHead == NULL);
557: ASSERT(m_pNodeTail == NULL);
558: }
559: else
560: {
561: // non-empty list
562: ASSERT(m_pNodeHead != NULL);
563: ASSERT(m_pNodeTail != NULL);
564: }
565: }
566: #endif //_DEBUG
567:
568: /////////////////////////////////////////////////////////////////////////////
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.