|
|
1.1 root 1: /////////////////////////////////////////////////////////////////////////////
2: // class CMap - a mapping from 'KEY's to 'VALUE's, passed in as
3: // ARG_KEY and/or ARG_VALUE parameters.
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 KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
21: class CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE> : public CObject
22: {
23: #if IS_SERIAL
24: DECLARE_SERIAL(CMap)
25: #else
26: DECLARE_DYNAMIC(CMap)
27: #endif //!IS_SERIAL
28: protected:
29: // Association
30: struct CAssoc
31: {
32: CAssoc* pNext;
33: UINT nHashValue; // needed for efficient iteration
34: KEY key;
35: VALUE value;
36: };
37: public:
38:
39: // Construction
40: CMap(int nBlockSize=10);
41:
42: // Attributes
43: // number of elements
44: int GetCount() const
45: { return m_nCount; }
46: BOOL IsEmpty() const
47: { return m_nCount == 0; }
48: // Lookup
49: BOOL Lookup(ARG_KEY key, VALUE& rValue) const;
50:
51: // Operations
52: // Lookup and add if not there
53: VALUE& operator[](ARG_KEY key);
54:
55: // add a new (key, value) pair
56: void SetAt(ARG_KEY key, ARG_VALUE newValue)
57: { (*this)[key] = newValue; }
58:
59: // removing existing (key, ?) pair
60: BOOL RemoveKey(ARG_KEY key);
61: void RemoveAll();
62:
63: // iterating all (key, value) pairs
64: POSITION GetStartPosition() const
65: { return (m_nCount == 0) ? NULL : BEFORE_START_POSITION; }
66: void GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) const;
67:
68: // advanced features for derived classes
69: UINT GetHashTableSize() const
70: { return m_nHashTableSize; }
71: void InitHashTable(UINT hashSize);
72:
73: // Overridables: special non-virtual (see map implementation for details)
74: // Routine used to user-provided hash keys
75: UINT HashKey(ARG_KEY key) const;
76:
77: // Implementation
78: protected:
79: CAssoc** m_pHashTable;
80: UINT m_nHashTableSize;
81: int m_nCount;
82: CAssoc* m_pFreeList;
83: struct CPlex* m_pBlocks;
84: int m_nBlockSize;
85:
86: CAssoc* NewAssoc();
87: void FreeAssoc(CAssoc*);
88: CAssoc* GetAssocAt(ARG_KEY, UINT&) const;
89:
90: public:
91: ~CMap();
92: #if IS_SERIAL
93: void Serialize(CArchive&);
94: #endif //IS_SERIAL
95: #ifdef _DEBUG
96: void Dump(CDumpContext&) const;
97: void AssertValid() const;
98: #endif
99: };
100:
101: //$IMPLEMENT_TEMPLATE
102: /////////////////////////////////////////////////////////////////////////////
103: //
104: // Implementation of Map from KEY to VALUE
105: //
106: /////////////////////////////////////////////////////////////////////////////
107:
108: #include "afxcoll.h"
109: #pragma hdrstop
110:
111: #include "plex.h"
112:
113: #ifdef AFX_COLL_SEG
114: #pragma code_seg(AFX_COLL_SEG)
115: #endif
116:
117: #if IS_SERIAL
118: IMPLEMENT_SERIAL(CMap, CObject, 0);
119: #else
120: IMPLEMENT_DYNAMIC(CMap, CObject);
121: #endif //!IS_SERIAL
122:
123: #ifdef _DEBUG
124: #undef THIS_FILE
125: static char BASED_CODE THIS_FILE[] = __FILE__;
126: #endif
127:
128: #if HAS_CREATE
129: #include "elements.h" // used for special creation
130: #endif
131:
132: #define new DEBUG_NEW
133:
134: /////////////////////////////////////////////////////////////////////////////
135:
136: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
137: CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::CMap(int nBlockSize)
138: {
139: ASSERT(nBlockSize > 0);
140:
141: m_pHashTable = NULL;
142: m_nHashTableSize = 17; // default size
143: m_nCount = 0;
144: m_pFreeList = NULL;
145: m_pBlocks = NULL;
146: m_nBlockSize = nBlockSize;
147: }
148:
149: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
150: inline UINT CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::HashKey(ARG_KEY key) const
151: {
152: // default identity hash - works for most primitive values
153: return _AFX_FP_OFF(key) >> 4;
154: }
155:
156:
157: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
158: void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::InitHashTable(UINT nHashSize)
159: //
160: // Used to force allocation of a hash table or to override the default
161: // hash table size of (which is fairly small)
162: {
163: ASSERT_VALID(this);
164: ASSERT(m_nCount == 0);
165: ASSERT(nHashSize > 0);
166:
167: // if had a hash table - get rid of it
168: if (m_pHashTable != NULL)
169: delete m_pHashTable;
170: m_pHashTable = NULL;
171:
172: m_pHashTable = new CAssoc* [nHashSize];
173: memset(m_pHashTable, 0, sizeof(CAssoc*) * nHashSize);
174: m_nHashTableSize = nHashSize;
175: }
176:
177: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
178: void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::RemoveAll()
179: {
180: ASSERT_VALID(this);
181:
182: if (m_pHashTable != NULL)
183: {
184: #if HAS_CREATE
185: // destroy elements (values only)
186: for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++)
187: {
188: register CAssoc* pAssoc;
189: for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
190: pAssoc = pAssoc->pNext)
191: {
192: DestructElement(&pAssoc->value);
193: }
194: }
195: #endif
196:
197: // free hash table
198: delete m_pHashTable;
199: m_pHashTable = NULL;
200: }
201:
202: m_nCount = 0;
203: m_pFreeList = NULL;
204: m_pBlocks->FreeDataChain();
205: m_pBlocks = NULL;
206: }
207:
208: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
209: CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::~CMap()
210: {
211: RemoveAll();
212: ASSERT(m_nCount == 0);
213: }
214:
215: /////////////////////////////////////////////////////////////////////////////
216: // Assoc helpers
217: // same as CList implementation except we store CAssoc's not CNode's
218: // and CAssoc's are singly linked all the time
219:
220: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
221: CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::CAssoc* CMap::NewAssoc()
222: {
223: if (m_pFreeList == NULL)
224: {
225: // add another block
226: CPlex* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CMap::CAssoc));
227: // chain them into free list
228: register CMap::CAssoc* pAssoc = (CMap::CAssoc*) newBlock->data();
229: // free in reverse order to make it easier to debug
230: pAssoc += m_nBlockSize - 1;
231: for (int i = m_nBlockSize-1; i >= 0; i--, pAssoc--)
232: {
233: pAssoc->pNext = m_pFreeList;
234: m_pFreeList = pAssoc;
235: }
236: }
237: ASSERT(m_pFreeList != NULL); // we must have something
238:
239: CMap::CAssoc* pAssoc = m_pFreeList;
240: m_pFreeList = m_pFreeList->pNext;
241: m_nCount++;
242: ASSERT(m_nCount > 0); // make sure we don't overflow
243: memset(&pAssoc->key, 0, sizeof(KEY));
244: #if HAS_CREATE
245: ConstructElement(&pAssoc->key); // special construct values
246: #else
247: memset(&pAssoc->value, 0, sizeof(VALUE));
248: #endif
249: return pAssoc;
250: }
251:
252: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
253: void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::FreeAssoc(CMap::CAssoc* pAssoc)
254: {
255: #if HAS_CREATE
256: DestructElement(&pAssoc->value);
257: #endif
258: pAssoc->pNext = m_pFreeList;
259: m_pFreeList = pAssoc;
260: m_nCount--;
261: ASSERT(m_nCount >= 0); // make sure we don't underflow
262: }
263:
264: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
265: CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::CAssoc*
266: CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::GetAssocAt(ARG_KEY key, UINT& nHash) const
267: // find association (or return NULL)
268: {
269: nHash = HashKey(key) % m_nHashTableSize;
270:
271: if (m_pHashTable == NULL)
272: return NULL;
273:
274: // see if it exists
275: register CAssoc* pAssoc;
276: for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext)
277: {
278: if (pAssoc->key == key)
279: return pAssoc;
280: }
281: return NULL;
282: }
283:
284: /////////////////////////////////////////////////////////////////////////////
285:
286: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
287: BOOL CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::Lookup(ARG_KEY key, VALUE& rValue) const
288: {
289: ASSERT_VALID(this);
290:
291: UINT nHash;
292: register CAssoc* pAssoc = GetAssocAt(key, nHash);
293: if (pAssoc == NULL)
294: return FALSE; // not in map
295:
296: rValue = pAssoc->value;
297: return TRUE;
298: }
299:
300: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
301: VALUE& CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::operator[](ARG_KEY key)
302: {
303: ASSERT_VALID(this);
304:
305: UINT nHash;
306: register CAssoc* pAssoc;
307: if ((pAssoc = GetAssocAt(key, nHash)) == NULL)
308: {
309: if (m_pHashTable == NULL)
310: InitHashTable(m_nHashTableSize);
311:
312: // it doesn't exist, add a new Association
313: pAssoc = NewAssoc();
314: pAssoc->nHashValue = nHash;
315: pAssoc->key = key;
316: // 'pAssoc->value' is a constructed object, nothing more
317:
318: // put into hash table
319: pAssoc->pNext = m_pHashTable[nHash];
320: m_pHashTable[nHash] = pAssoc;
321: }
322: return pAssoc->value; // return new reference
323: }
324:
325:
326: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
327: BOOL CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::RemoveKey(ARG_KEY key)
328: // remove key - return TRUE if removed
329: {
330: ASSERT_VALID(this);
331:
332: if (m_pHashTable == NULL)
333: return FALSE; // nothing in the table
334:
335: register CAssoc** ppAssocPrev;
336: ppAssocPrev = &m_pHashTable[HashKey(key) % m_nHashTableSize];
337:
338: CAssoc* pAssoc;
339: for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext)
340: {
341: if (pAssoc->key == key)
342: {
343: // remove it
344: *ppAssocPrev = pAssoc->pNext; // remove from list
345: FreeAssoc(pAssoc);
346: return TRUE;
347: }
348: ppAssocPrev = &pAssoc->pNext;
349: }
350: return FALSE; // not found
351: }
352:
353:
354: /////////////////////////////////////////////////////////////////////////////
355: // Iterating
356:
357: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
358: void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::GetNextAssoc(POSITION& rNextPosition,
359: KEY& rKey, VALUE& rValue) const
360: {
361: ASSERT_VALID(this);
362: ASSERT(m_pHashTable != NULL); // never call on empty map
363:
364: register CAssoc* pAssocRet = (CAssoc*)rNextPosition;
365: ASSERT(pAssocRet != NULL);
366:
367: if (pAssocRet == (CAssoc*) BEFORE_START_POSITION)
368: {
369: // find the first association
370: for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
371: if ((pAssocRet = m_pHashTable[nBucket]) != NULL)
372: break;
373: ASSERT(pAssocRet != NULL); // must find something
374: }
375:
376: // find next association
377: CAssoc* pAssocNext;
378: if ((pAssocNext = pAssocRet->pNext) == NULL)
379: {
380: // go to next bucket
381: for (UINT nBucket = pAssocRet->nHashValue + 1;
382: nBucket < m_nHashTableSize; nBucket++)
383: if ((pAssocNext = m_pHashTable[nBucket]) != NULL)
384: break;
385: }
386:
387: rNextPosition = (POSITION) pAssocNext;
388:
389: // fill in return data
390: rKey = pAssocRet->key;
391: rValue = pAssocRet->value;
392: }
393:
394: /////////////////////////////////////////////////////////////////////////////
395: // Serialization
396:
397: #if IS_SERIAL
398: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
399: void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::Serialize(CArchive& ar)
400: {
401: ASSERT_VALID(this);
402:
403: CObject::Serialize(ar);
404:
405: if (ar.IsStoring())
406: {
407: ar << (WORD) m_nCount;
408: if (m_nCount == 0)
409: return; // nothing more to do
410:
411: ASSERT(m_pHashTable != NULL);
412: for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++)
413: {
414: CAssoc* pAssoc;
415: for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
416: pAssoc = pAssoc->pNext)
417: {
418: ar << pAssoc->key;
419: ar << pAssoc->value;
420: }
421: }
422: }
423: else
424: {
425: WORD wNewCount;
426: ar >> wNewCount;
427: while (wNewCount--)
428: {
429: KEY newKey;
430: VALUE newValue;
431: ar >> newKey;
432: ar >> newValue;
433: SetAt(newKey, newValue);
434: }
435: }
436: }
437: #endif //IS_SERIAL
438:
439: /////////////////////////////////////////////////////////////////////////////
440: // Diagnostics
441:
442: #ifdef _DEBUG
443:
444: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
445: void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::Dump(CDumpContext& dc) const
446: {
447: ASSERT_VALID(this);
448:
449: #define MAKESTRING(x) #x
450: dc << "a " MAKESTRING(CMap) " with " << m_nCount << " elements";
451: #undef MAKESTRING
452: if (dc.GetDepth() > 0)
453: {
454: // Dump in format "[key] -> value"
455: POSITION pos = GetStartPosition();
456: KEY key;
457: VALUE val;
458:
459: dc << "\n";
460: while (pos != NULL)
461: {
462: GetNextAssoc(pos, key, val);
463: dc << "\n\t[" << key << "] = " << val;
464: }
465: }
466: }
467:
468: template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE>
469: void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::AssertValid() const
470: {
471: CObject::AssertValid();
472:
473: ASSERT(m_nHashTableSize > 0);
474: ASSERT(m_nCount == 0 || m_pHashTable != NULL);
475: // non-empty map should have hash table
476: }
477: #endif //_DEBUG
478:
479: /////////////////////////////////////////////////////////////////////////////
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.