Leaked source code of windows server 2003
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.

456 lines
9.4 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1994.
  5. //
  6. // File: strhash.cxx
  7. //
  8. // Contents: A hash table for strings
  9. //
  10. // History: 7-Nov-94 BruceFo Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include "headers.hxx"
  14. #pragma hdrstop
  15. #include "strhash.hxx"
  16. const DWORD g_cMinElemsRehash = 3;
  17. const DWORD g_cMinBuckets = 13;
  18. //////////////////////////////////////////////////////////////////////////////
  19. // Methods for the CStrHashBucketElem class
  20. //////////////////////////////////////////////////////////////////////////////
  21. CStrHashBucketElem::CStrHashBucketElem(
  22. IN const WCHAR* pszKey
  23. )
  24. :
  25. m_pszKey(pszKey),
  26. m_next(NULL)
  27. {
  28. INIT_SIG(CStrHashBucketElem);
  29. }
  30. CStrHashBucketElem::~CStrHashBucketElem()
  31. {
  32. CHECK_SIG(CStrHashBucketElem);
  33. }
  34. BOOL
  35. CStrHashBucketElem::IsEqual(
  36. IN const WCHAR* pszKey
  37. )
  38. {
  39. CHECK_SIG(CStrHashBucketElem);
  40. appAssert(NULL != pszKey);
  41. appAssert(NULL != m_pszKey);
  42. // NOTE: case-insensitive compare. This affects the hash function!
  43. return (0 == _wcsicmp(pszKey, m_pszKey));
  44. }
  45. //////////////////////////////////////////////////////////////////////////////
  46. // Methods for the CStrHashBucket class
  47. //////////////////////////////////////////////////////////////////////////////
  48. CStrHashBucket::CStrHashBucket(
  49. VOID
  50. )
  51. :
  52. m_head(NULL)
  53. {
  54. INIT_SIG(CStrHashBucket);
  55. }
  56. CStrHashBucket::~CStrHashBucket(
  57. VOID
  58. )
  59. {
  60. CHECK_SIG(CStrHashBucket);
  61. for (CStrHashBucketElem* x = m_head; NULL != x; )
  62. {
  63. CStrHashBucketElem* tmp = x->m_next;
  64. delete x;
  65. x = tmp;
  66. }
  67. }
  68. HRESULT
  69. CStrHashBucket::Insert(
  70. IN const WCHAR* pszKey
  71. )
  72. {
  73. CHECK_SIG(CStrHashBucket);
  74. appAssert(NULL != pszKey);
  75. CStrHashBucketElem* x = new CStrHashBucketElem(pszKey);
  76. if (NULL == x)
  77. {
  78. return E_OUTOFMEMORY;
  79. }
  80. x->m_next = m_head;
  81. m_head = x;
  82. return S_OK;
  83. }
  84. // return TRUE if it was found and removed, FALSE if it wasn't even found
  85. BOOL
  86. CStrHashBucket::Remove(
  87. IN const WCHAR* pszKey
  88. )
  89. {
  90. CHECK_SIG(CStrHashBucket);
  91. appAssert(NULL != pszKey);
  92. CStrHashBucketElem** pPrev = &m_head;
  93. for (CStrHashBucketElem* x = m_head; NULL != x; x = x->m_next)
  94. {
  95. if (x->IsEqual(pszKey)) // found it
  96. {
  97. *pPrev = x->m_next;
  98. delete x;
  99. return TRUE;
  100. }
  101. pPrev = &x->m_next;
  102. }
  103. return FALSE; // didn't find it
  104. }
  105. BOOL
  106. CStrHashBucket::IsMember(
  107. IN const WCHAR* pszKey
  108. )
  109. {
  110. CHECK_SIG(CStrHashBucket);
  111. appAssert(NULL != pszKey);
  112. for (CStrHashBucketElem* x = m_head; NULL != x; x = x->m_next)
  113. {
  114. if (x->IsEqual(pszKey))
  115. {
  116. return TRUE; // found it
  117. }
  118. }
  119. return FALSE; // didn't find it
  120. }
  121. //////////////////////////////////////////////////////////////////////////////
  122. // Methods for the CStrHashTable class
  123. //////////////////////////////////////////////////////////////////////////////
  124. CStrHashTable::CStrHashTable(
  125. IN DWORD cNumBuckets
  126. )
  127. :
  128. m_cElems(0),
  129. m_cMinNumBuckets(cNumBuckets)
  130. {
  131. INIT_SIG(CStrHashTable);
  132. m_cNumBuckets = max(cNumBuckets, g_cMinBuckets);
  133. m_ht = new CStrHashBucket[m_cNumBuckets];
  134. if (NULL == m_ht)
  135. {
  136. appDebugOut((DEB_ERROR,
  137. "Failed to allocate hash table with %d buckets\n",
  138. m_cNumBuckets));
  139. m_cMinNumBuckets = 0;
  140. m_cNumBuckets = 0;
  141. }
  142. }
  143. HRESULT
  144. CStrHashTable::QueryError(
  145. VOID
  146. )
  147. {
  148. if (NULL == m_ht)
  149. {
  150. return E_OUTOFMEMORY;
  151. }
  152. return S_OK;
  153. }
  154. CStrHashTable::~CStrHashTable(
  155. VOID
  156. )
  157. {
  158. CHECK_SIG(CStrHashTable);
  159. delete[] m_ht;
  160. }
  161. HRESULT
  162. CStrHashTable::Insert(
  163. IN const WCHAR* pszKey
  164. )
  165. {
  166. CHECK_SIG(CStrHashTable);
  167. if (NULL == pszKey)
  168. {
  169. return E_INVALIDARG;
  170. }
  171. appAssert(NULL != m_ht);
  172. DWORD key = HashFunction(pszKey);
  173. if (!(m_ht[key].IsMember(pszKey)))
  174. {
  175. // only insert if the key isn't already in the table.
  176. HRESULT hr = m_ht[key].Insert(pszKey);
  177. CHECK_HRESULT(hr);
  178. if (FAILED(hr))
  179. {
  180. return hr;
  181. }
  182. ++m_cElems;
  183. return CheckRehash();
  184. }
  185. return S_OK;
  186. }
  187. HRESULT
  188. CStrHashTable::Remove(
  189. IN const WCHAR* pszKey
  190. )
  191. {
  192. CHECK_SIG(CStrHashTable);
  193. if (NULL == pszKey)
  194. {
  195. return E_INVALIDARG;
  196. }
  197. appAssert(NULL != m_ht);
  198. if (m_ht[HashFunction(pszKey)].Remove(pszKey))
  199. {
  200. --m_cElems;
  201. return CheckRehash();
  202. }
  203. return S_OK; // key was not found and hence not deleted
  204. }
  205. BOOL
  206. CStrHashTable::IsMember(
  207. IN const WCHAR* pszKey
  208. )
  209. {
  210. CHECK_SIG(CStrHashTable);
  211. if (NULL == pszKey)
  212. {
  213. return FALSE; // invalid argument, really
  214. }
  215. appAssert(NULL != m_ht);
  216. return m_ht[HashFunction(pszKey)].IsMember(pszKey);
  217. }
  218. DWORD
  219. CStrHashTable::Count(
  220. VOID
  221. )
  222. {
  223. CHECK_SIG(CStrHashTable);
  224. return m_cElems;
  225. }
  226. // This returns the iteration data, or NULL on failure
  227. CIterateData*
  228. CStrHashTable::IterateStart(
  229. VOID
  230. )
  231. {
  232. CHECK_SIG(CStrHashTable);
  233. CIterateData* pData = new CIterateData;
  234. if (NULL != pData)
  235. {
  236. IterateGetNext(pData);
  237. }
  238. return pData;
  239. }
  240. const WCHAR*
  241. CStrHashTable::IterateGetData(
  242. IN OUT CIterateData* pCurrent
  243. )
  244. {
  245. CHECK_SIG(CStrHashTable);
  246. appAssert(NULL != pCurrent);
  247. appAssert(ITERATE_END != pCurrent->m_CurrentBucket);
  248. return pCurrent->m_pCurrentElem->m_pszKey;
  249. }
  250. BOOL
  251. CStrHashTable::IterateDone(
  252. IN CIterateData* pCurrent
  253. )
  254. {
  255. CHECK_SIG(CStrHashTable);
  256. appAssert(NULL != pCurrent);
  257. return (ITERATE_END == pCurrent->m_CurrentBucket);
  258. }
  259. VOID
  260. CStrHashTable::IterateEnd(
  261. IN CIterateData* pCurrent
  262. )
  263. {
  264. CHECK_SIG(CStrHashTable);
  265. delete pCurrent;
  266. }
  267. VOID
  268. CStrHashTable::IterateGetNext(
  269. IN OUT CIterateData* pCurrent
  270. )
  271. {
  272. CHECK_SIG(CStrHashTable);
  273. appAssert(NULL != pCurrent);
  274. appAssert(ITERATE_END != pCurrent->m_CurrentBucket);
  275. if (NULL != pCurrent->m_pCurrentElem)
  276. {
  277. if (NULL != pCurrent->m_pCurrentElem->m_next)
  278. {
  279. // just get next element in bucket
  280. pCurrent->m_pCurrentElem = pCurrent->m_pCurrentElem->m_next;
  281. }
  282. else
  283. {
  284. // need to search to new bucket
  285. ++pCurrent->m_CurrentBucket;
  286. IterateGetBucket(pCurrent);
  287. }
  288. }
  289. else
  290. {
  291. IterateGetBucket(pCurrent);
  292. }
  293. }
  294. VOID
  295. CStrHashTable::IterateGetBucket(
  296. IN OUT CIterateData* pCurrent
  297. )
  298. {
  299. CHECK_SIG(CStrHashTable);
  300. appAssert(NULL != m_ht);
  301. for (DWORD i = pCurrent->m_CurrentBucket; i < m_cNumBuckets; i++)
  302. {
  303. if (NULL != m_ht[i].m_head)
  304. {
  305. pCurrent->m_pCurrentElem = m_ht[i].m_head;
  306. pCurrent->m_CurrentBucket = i;
  307. return;
  308. }
  309. }
  310. pCurrent->m_CurrentBucket = ITERATE_END; // we've iterated through all!
  311. }
  312. DWORD
  313. CStrHashTable::HashFunction(
  314. IN const WCHAR* pszKey
  315. )
  316. {
  317. CHECK_SIG(CStrHashTable);
  318. appAssert(NULL != pszKey);
  319. appAssert(m_cNumBuckets > 0);
  320. DWORD total = 0;
  321. for (const WCHAR* p = pszKey; L'\0' != *p; p++)
  322. {
  323. // lower case it, so case-insensitive IsEqual works
  324. total += towlower(*p);
  325. }
  326. return total % m_cNumBuckets;
  327. }
  328. HRESULT
  329. CStrHashTable::CheckRehash(
  330. VOID
  331. )
  332. {
  333. CHECK_SIG(CStrHashTable);
  334. if (m_cElems > g_cMinElemsRehash && m_cElems > m_cMinNumBuckets)
  335. {
  336. if ( (m_cElems > m_cNumBuckets)
  337. || (m_cElems < m_cNumBuckets / 4) )
  338. {
  339. // add one to at least make it odd (for better hashing behavior)
  340. return Rehash(m_cElems * 2 + 1);
  341. }
  342. }
  343. return S_OK;
  344. }
  345. HRESULT
  346. CStrHashTable::Rehash(
  347. IN DWORD cNumBuckets
  348. )
  349. {
  350. CHECK_SIG(CStrHashTable);
  351. cNumBuckets = max(cNumBuckets, g_cMinBuckets);
  352. CStrHashBucket* ht = new CStrHashBucket[cNumBuckets];
  353. if (NULL == ht)
  354. {
  355. // return error, but don't delete the existing table
  356. return E_OUTOFMEMORY;
  357. }
  358. appAssert(NULL != m_ht);
  359. // now transfer all data from old to new
  360. ULONG cOldNumBuckets = m_cNumBuckets;
  361. m_cNumBuckets = cNumBuckets; // set this so HashFunction() uses it
  362. for (ULONG i=0; i < cOldNumBuckets; i++)
  363. {
  364. for (CStrHashBucketElem* x = m_ht[i].m_head; NULL != x; )
  365. {
  366. CStrHashBucketElem* tmp = x->m_next;
  367. // now, just put this bucket in the right place in the new
  368. // hash table. Avoid performing new's and copying the keys.
  369. DWORD bucket = HashFunction(x->m_pszKey);
  370. x->m_next = ht[bucket].m_head;
  371. ht[bucket].m_head = x;
  372. x = tmp;
  373. }
  374. m_ht[i].m_head = NULL; // there isn't anything left in the list!
  375. }
  376. // the data is transfered; complete the switchover
  377. delete[] m_ht;
  378. m_ht = ht;
  379. return S_OK;
  380. }