Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

540 lines
14 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. // class CMapKeyToValue - a mapping from 'KEY's to 'VALUE's, passed in as
  3. // pv/cb pairs. The keys can be variable length, although we optmizize the
  4. // case when they are all the same.
  5. //
  6. /////////////////////////////////////////////////////////////////////////////
  7. #include "headers.cxx"
  8. #pragma hdrstop
  9. #include <olecoll.h>
  10. #include "map_kv.h"
  11. #include "plex.h"
  12. /////////////////////////////////////////////////////////////////////////////
  13. //CMapKeyToValue::CMapKeyToValue(DWORD memctx, UINT cbValue, UINT cbKey,
  14. CMapKeyToValue::CMapKeyToValue(UINT cbValue, UINT cbKey,
  15. int nBlockSize, LPFNHASHKEY lpfnHashKey, UINT nHashSize)
  16. {
  17. Assert(nBlockSize > 0);
  18. m_cbValue = cbValue;
  19. m_cbKey = cbKey;
  20. m_cbKeyInAssoc = cbKey == 0 ? sizeof(CKeyWrap) : cbKey;
  21. m_pHashTable = NULL;
  22. m_nHashTableSize = nHashSize;
  23. m_lpfnHashKey = lpfnHashKey;
  24. m_nCount = 0;
  25. m_pFreeList = NULL;
  26. m_pBlocks = NULL;
  27. m_nBlockSize = nBlockSize;
  28. //if (memctx == MEMCTX_SAME)
  29. // memctx = CoMemctxOf(this);
  30. //m_memctx = memctx;
  31. //Assert(m_memctx != MEMCTX_UNKNOWN);
  32. }
  33. CMapKeyToValue::~CMapKeyToValue()
  34. {
  35. ASSERT_VALID(this);
  36. RemoveAll();
  37. Assert(m_nCount == 0);
  38. }
  39. // simple, default hash function
  40. // REVIEW: need to check the value in this for GUIDs and strings
  41. STDAPI_(UINT) MKVDefaultHashKey(LPVOID pKey, UINT cbKey)
  42. {
  43. UINT hash = 0;
  44. BYTE FAR* lpb = (BYTE FAR*)pKey;
  45. while (cbKey-- != 0)
  46. hash = 257 * hash + *lpb++;
  47. return hash;
  48. }
  49. BOOL CMapKeyToValue::InitHashTable()
  50. {
  51. ASSERT_VALID(this);
  52. Assert(m_nHashTableSize > 0);
  53. if (m_pHashTable != NULL)
  54. return TRUE;
  55. Assert(m_nCount == 0);
  56. if ((m_pHashTable = (CAssoc FAR* FAR*)CoTaskMemAlloc(m_nHashTableSize * sizeof(CAssoc FAR*))) == NULL)
  57. return FALSE;
  58. memset(m_pHashTable, 0, sizeof(CAssoc FAR*) * m_nHashTableSize);
  59. ASSERT_VALID(this);
  60. return TRUE;
  61. }
  62. void CMapKeyToValue::RemoveAll()
  63. {
  64. ASSERT_VALID(this);
  65. // free all key values and then hash table
  66. if (m_pHashTable != NULL)
  67. {
  68. // destroy assocs
  69. for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++)
  70. {
  71. register CAssoc FAR* pAssoc;
  72. for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
  73. pAssoc = pAssoc->pNext)
  74. // assoc itself is freed by FreeDataChain below
  75. FreeAssocKey(pAssoc);
  76. }
  77. // free hash table
  78. CoTaskMemFree(m_pHashTable);
  79. m_pHashTable = NULL;
  80. }
  81. m_nCount = 0;
  82. m_pFreeList = NULL;
  83. m_pBlocks->FreeDataChain();
  84. m_pBlocks = NULL;
  85. ASSERT_VALID(this);
  86. }
  87. /////////////////////////////////////////////////////////////////////////////
  88. // Assoc helpers
  89. // CAssoc's are singly linked all the time
  90. CMapKeyToValue::CAssoc FAR*
  91. CMapKeyToValue::NewAssoc(UINT hash, LPVOID pKey, UINT cbKey, LPVOID pValue)
  92. {
  93. if (m_pFreeList == NULL)
  94. {
  95. // add another block
  96. CPlex FAR* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize, SizeAssoc());
  97. if (newBlock == NULL)
  98. return NULL;
  99. // chain them into free list
  100. register BYTE FAR* pbAssoc = (BYTE FAR*) newBlock->data();
  101. // free in reverse order to make it easier to debug
  102. pbAssoc += (m_nBlockSize - 1) * SizeAssoc();
  103. for (int i = m_nBlockSize-1; i >= 0; i--, pbAssoc -= SizeAssoc())
  104. {
  105. ((CAssoc FAR*)pbAssoc)->pNext = m_pFreeList;
  106. m_pFreeList = (CAssoc FAR*)pbAssoc;
  107. }
  108. }
  109. Assert(m_pFreeList != NULL); // we must have something
  110. CMapKeyToValue::CAssoc FAR* pAssoc = m_pFreeList;
  111. // init all fields except pNext while still on free list
  112. pAssoc->nHashValue = hash;
  113. if (!SetAssocKey(pAssoc, pKey, cbKey))
  114. return NULL;
  115. SetAssocValue(pAssoc, pValue);
  116. // remove from free list after successfully initializing it (except pNext)
  117. m_pFreeList = m_pFreeList->pNext;
  118. m_nCount++;
  119. Assert(m_nCount > 0); // make sure we don't overflow
  120. return pAssoc;
  121. }
  122. // free individual assoc by freeing key and putting on free list
  123. void CMapKeyToValue::FreeAssoc(CMapKeyToValue::CAssoc FAR* pAssoc)
  124. {
  125. pAssoc->pNext = m_pFreeList;
  126. m_pFreeList = pAssoc;
  127. m_nCount--;
  128. Assert(m_nCount >= 0); // make sure we don't underflow
  129. FreeAssocKey(pAssoc);
  130. }
  131. // find association (or return NULL)
  132. CMapKeyToValue::CAssoc FAR*
  133. CMapKeyToValue::GetAssocAt(LPVOID pKey, UINT cbKey, UINT FAR& nHash) const
  134. {
  135. if (m_lpfnHashKey)
  136. nHash = (*m_lpfnHashKey)(pKey, cbKey) % m_nHashTableSize;
  137. else
  138. nHash = MKVDefaultHashKey(pKey, cbKey) % m_nHashTableSize;
  139. if (m_pHashTable == NULL)
  140. return NULL;
  141. // see if it exists
  142. register CAssoc FAR* pAssoc;
  143. for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext)
  144. {
  145. if (CompareAssocKey(pAssoc, pKey, cbKey))
  146. return pAssoc;
  147. }
  148. return NULL;
  149. }
  150. BOOL CMapKeyToValue::CompareAssocKey(CAssoc FAR* pAssoc, LPVOID pKey2, UINT cbKey2) const
  151. {
  152. LPVOID pKey1;
  153. UINT cbKey1;
  154. GetAssocKeyPtr(pAssoc, &pKey1, &cbKey1);
  155. return cbKey1 == cbKey2 && memcmp(pKey1, pKey2, cbKey1) == 0;
  156. }
  157. BOOL CMapKeyToValue::SetAssocKey(CAssoc FAR* pAssoc, LPVOID pKey, UINT cbKey) const
  158. {
  159. Assert(cbKey == m_cbKey || m_cbKey == 0);
  160. if (m_cbKey == 0)
  161. {
  162. Assert(m_cbKeyInAssoc == sizeof(CKeyWrap));
  163. // alloc, set size and pointer
  164. if ((pAssoc->key.pKey = CoTaskMemAlloc(cbKey)) == NULL)
  165. return FALSE;
  166. pAssoc->key.cbKey = cbKey;
  167. }
  168. LPVOID pKeyTo;
  169. GetAssocKeyPtr(pAssoc, &pKeyTo, &cbKey);
  170. memcpy(pKeyTo, pKey, cbKey);
  171. return TRUE;
  172. }
  173. // gets pointer to key and its length
  174. void CMapKeyToValue::GetAssocKeyPtr(CAssoc FAR* pAssoc, LPVOID FAR* ppKey,UINT FAR* pcbKey) const
  175. {
  176. if (m_cbKey == 0)
  177. {
  178. // variable length key; go indirect
  179. *ppKey = pAssoc->key.pKey;
  180. *pcbKey = pAssoc->key.cbKey;
  181. }
  182. else
  183. {
  184. // fixed length key; key in assoc
  185. *ppKey = (LPVOID)&pAssoc->key;
  186. *pcbKey = m_cbKey;
  187. }
  188. }
  189. void CMapKeyToValue::FreeAssocKey(CAssoc FAR* pAssoc) const
  190. {
  191. if (m_cbKey == 0)
  192. CoTaskMemFree(pAssoc->key.pKey);
  193. }
  194. void CMapKeyToValue::GetAssocValuePtr(CAssoc FAR* pAssoc, LPVOID FAR* ppValue) const
  195. {
  196. *ppValue = (char FAR*)&pAssoc->key + m_cbKeyInAssoc;
  197. }
  198. void CMapKeyToValue::GetAssocValue(CAssoc FAR* pAssoc, LPVOID pValue) const
  199. {
  200. LPVOID pValueFrom;
  201. GetAssocValuePtr(pAssoc, &pValueFrom);
  202. Assert(pValue != NULL);
  203. memcpy(pValue, pValueFrom, m_cbValue);
  204. }
  205. void CMapKeyToValue::SetAssocValue(CAssoc FAR* pAssoc, LPVOID pValue) const
  206. {
  207. LPVOID pValueTo;
  208. GetAssocValuePtr(pAssoc, &pValueTo);
  209. if (pValue == NULL)
  210. memset(pValueTo, 0, m_cbValue);
  211. else
  212. memcpy(pValueTo, pValue, m_cbValue);
  213. }
  214. /////////////////////////////////////////////////////////////////////////////
  215. // lookup value given key; return FALSE if key not found; in that
  216. // case, the value is set to all zeros
  217. BOOL CMapKeyToValue::Lookup(LPVOID pKey, UINT cbKey, LPVOID pValue) const
  218. {
  219. UINT nHash;
  220. HMAPKEY hmapkey = (HMAPKEY)GetAssocAt(pKey, cbKey, nHash);
  221. return LookupHKey(hmapkey, pValue);
  222. //return LookupHKey((HMAPKEY)GetAssocAt(pKey, cbKey, nHash), pValue);
  223. }
  224. // lookup value given key; return FALSE if NULL (or bad) key; in that
  225. // case, the value is set to all zeros
  226. BOOL CMapKeyToValue::LookupHKey(HMAPKEY hKey, LPVOID pValue) const
  227. {
  228. // REVIEW: would like some way to verify that hKey is valid
  229. register CAssoc FAR* pAssoc = (CAssoc FAR*)hKey;
  230. if (pAssoc == NULL)
  231. {
  232. memset(pValue, 0, m_cbValue);
  233. return FALSE; // not in map
  234. }
  235. ASSERT_VALID(this);
  236. GetAssocValue(pAssoc, pValue);
  237. return TRUE;
  238. }
  239. // lookup and if not found add; returns FALSE only if OOM; if added,
  240. // value added and pointer passed are set to zeros.
  241. BOOL CMapKeyToValue::LookupAdd(LPVOID pKey, UINT cbKey, LPVOID pValue) const
  242. {
  243. if (Lookup(pKey, cbKey, pValue))
  244. return TRUE;
  245. // value set to zeros since lookup failed
  246. return ((CMapKeyToValue FAR*)this)->SetAt(pKey, cbKey, NULL);
  247. }
  248. // the only place new assocs are created; return FALSE if OOM;
  249. // never returns FALSE if keys already exists
  250. BOOL CMapKeyToValue::SetAt(LPVOID pKey, UINT cbKey, LPVOID pValue)
  251. {
  252. UINT nHash;
  253. register CAssoc FAR* pAssoc;
  254. ASSERT_VALID(this);
  255. if ((pAssoc = GetAssocAt(pKey, cbKey, nHash)) == NULL)
  256. {
  257. if (!InitHashTable())
  258. // out of memory
  259. return FALSE;
  260. // it doesn't exist, add a new Association
  261. if ((pAssoc = NewAssoc(nHash, pKey, cbKey, pValue)) == NULL)
  262. return FALSE;
  263. // put into hash table
  264. pAssoc->pNext = m_pHashTable[nHash];
  265. m_pHashTable[nHash] = pAssoc;
  266. ASSERT_VALID(this);
  267. }
  268. else
  269. {
  270. SetAssocValue(pAssoc, pValue);
  271. }
  272. return TRUE;
  273. }
  274. // set existing hkey to value; return FALSE if NULL or bad key
  275. BOOL CMapKeyToValue::SetAtHKey(HMAPKEY hKey, LPVOID pValue)
  276. {
  277. // REVIEW: would like some way to verify that hKey is valid
  278. register CAssoc FAR* pAssoc = (CAssoc FAR*)hKey;
  279. if (pAssoc == NULL)
  280. return FALSE; // not in map
  281. ASSERT_VALID(this);
  282. SetAssocValue(pAssoc, pValue);
  283. return TRUE;
  284. }
  285. // remove key - return TRUE if removed
  286. BOOL CMapKeyToValue::RemoveKey(LPVOID pKey, UINT cbKey)
  287. {
  288. ASSERT_VALID(this);
  289. if (m_pHashTable == NULL)
  290. return FALSE; // nothing in the table
  291. register CAssoc FAR* FAR* ppAssocPrev;
  292. UINT i;
  293. if (m_lpfnHashKey)
  294. i = (*m_lpfnHashKey)(pKey, cbKey) % m_nHashTableSize;
  295. else
  296. i = MKVDefaultHashKey(pKey, cbKey) % m_nHashTableSize;
  297. ppAssocPrev = &m_pHashTable[i];
  298. CAssoc FAR* pAssoc;
  299. for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext)
  300. {
  301. if (CompareAssocKey(pAssoc, pKey, cbKey))
  302. {
  303. // remove it
  304. *ppAssocPrev = pAssoc->pNext; // remove from list
  305. FreeAssoc(pAssoc);
  306. ASSERT_VALID(this);
  307. return TRUE;
  308. }
  309. ppAssocPrev = &pAssoc->pNext;
  310. }
  311. return FALSE; // not found
  312. }
  313. // remove key based on pAssoc (HMAPKEY)
  314. BOOL CMapKeyToValue::RemoveHKey(HMAPKEY hKey)
  315. {
  316. ASSERT_VALID(this);
  317. if (m_pHashTable == NULL)
  318. return FALSE; // nothing in the table
  319. // REVIEW: would like some way to verify that hKey is valid
  320. CAssoc FAR* pAssoc = (CAssoc FAR*)hKey;
  321. if (pAssoc == NULL || pAssoc->nHashValue >= m_nHashTableSize)
  322. // null hkey or bad hash value
  323. return FALSE;
  324. register CAssoc FAR* FAR* ppAssocPrev;
  325. ppAssocPrev = &m_pHashTable[pAssoc->nHashValue];
  326. while (*ppAssocPrev != NULL)
  327. {
  328. if (*ppAssocPrev == pAssoc)
  329. {
  330. // remove it
  331. *ppAssocPrev = pAssoc->pNext; // remove from list
  332. FreeAssoc(pAssoc);
  333. ASSERT_VALID(this);
  334. return TRUE;
  335. }
  336. ppAssocPrev = &(*ppAssocPrev)->pNext;
  337. }
  338. return FALSE; // not found (must have a messed up list or passed
  339. // a key from another list)
  340. }
  341. HMAPKEY CMapKeyToValue::GetHKey(LPVOID pKey, UINT cbKey) const
  342. {
  343. UINT nHash;
  344. ASSERT_VALID(this);
  345. return (HMAPKEY)GetAssocAt(pKey, cbKey, nHash);
  346. }
  347. /////////////////////////////////////////////////////////////////////////////
  348. // Iterating
  349. // for fixed length keys, copies key to pKey; pcbKey can be NULL;
  350. // for variable length keys, copies pointer to key to pKey; sets pcbKey.
  351. void CMapKeyToValue::GetNextAssoc(POSITION FAR* pNextPosition,
  352. LPVOID pKey, UINT FAR* pcbKey, LPVOID pValue) const
  353. {
  354. ASSERT_VALID(this);
  355. Assert(m_pHashTable != NULL); // never call on empty map
  356. register CAssoc FAR* pAssocRet = (CAssoc FAR*)*pNextPosition;
  357. Assert(pAssocRet != NULL);
  358. if (pAssocRet == (CAssoc FAR*) BEFORE_START_POSITION)
  359. {
  360. // find the first association
  361. for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
  362. if ((pAssocRet = m_pHashTable[nBucket]) != NULL)
  363. break;
  364. Assert(pAssocRet != NULL); // must find something
  365. }
  366. // find next association
  367. CAssoc FAR* pAssocNext;
  368. if ((pAssocNext = pAssocRet->pNext) == NULL)
  369. {
  370. // go to next bucket
  371. for (UINT nBucket = pAssocRet->nHashValue + 1;
  372. nBucket < m_nHashTableSize; nBucket++)
  373. if ((pAssocNext = m_pHashTable[nBucket]) != NULL)
  374. break;
  375. }
  376. // fill in return data
  377. *pNextPosition = (POSITION) pAssocNext;
  378. // fill in key/pointer to key
  379. LPVOID pKeyFrom;
  380. UINT cbKey;
  381. GetAssocKeyPtr(pAssocRet, &pKeyFrom, &cbKey);
  382. if (m_cbKey == 0)
  383. // variable length key; just return pointer to key itself
  384. *(void FAR* FAR*)pKey = pKeyFrom;
  385. else
  386. memcpy(pKey, pKeyFrom, cbKey);
  387. if (pcbKey != NULL)
  388. *pcbKey = cbKey;
  389. // get value
  390. GetAssocValue(pAssocRet, pValue);
  391. }
  392. /////////////////////////////////////////////////////////////////////////////
  393. void CMapKeyToValue::AssertValid() const
  394. {
  395. #ifdef _DEBUG
  396. Assert(m_cbKeyInAssoc == (m_cbKey == 0 ? sizeof(CKeyWrap) : m_cbKey));
  397. Assert(m_nHashTableSize > 0);
  398. Assert(m_nCount == 0 || m_pHashTable != NULL);
  399. if (m_pHashTable != NULL)
  400. Assert(!IsBadReadPtr(m_pHashTable, m_nHashTableSize * sizeof(CAssoc FAR*)));
  401. if (m_lpfnHashKey)
  402. Assert(!IsBadCodePtr((FARPROC)m_lpfnHashKey));
  403. if (m_pFreeList != NULL)
  404. Assert(!IsBadReadPtr(m_pFreeList, SizeAssoc()));
  405. if (m_pBlocks != NULL)
  406. Assert(!IsBadReadPtr(m_pBlocks, SizeAssoc() * m_nBlockSize));
  407. // some collections live as global variables in the libraries, but
  408. // have their existance in some context. Also, we can't check shared
  409. // collections since we might be checking the etask collection
  410. // which would cause an infinite recursion.
  411. // REVIEW: Assert(m_memctx == MEMCTX_SHARED ||
  412. // CoMemctxOf(this) == MEMCTX_UNKNOWN || CoMemctxOf(this) == m_memctx);
  413. #endif //_DEBUG
  414. }