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.

432 lines
8.3 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. Table.cxx
  5. Abstract:
  6. Implementation of the CHashTable and CTableElement.
  7. Author:
  8. Mario Goertzel [MarioGo]
  9. Revision History:
  10. MarioGo 02-15-95 Bits 'n pieces
  11. MarioGo 12-18-95 Changed from UUID to generic object keys
  12. --*/
  13. #include<or.hxx>
  14. CTableElement *
  15. CTableElement::RemoveMatching(
  16. IN CTableKey &tk,
  17. OUT CTableElement **ppRemoved)
  18. /*++
  19. Routine Description:
  20. Helper function used to remove an element from a
  21. bucket in the hash table.
  22. Arguments:
  23. tk - Key to find the element being removed.
  24. ppRemoved - Contains the element removed or NULL upon return.
  25. Return Value:
  26. Pointer to the remaining elements in the list if any. Use
  27. to replace the current pointer in the bucket.
  28. --*/
  29. {
  30. CTableElement *pcurrent = this;
  31. CTableElement *psaved = 0;
  32. CTableElement *pfirst = this;
  33. while(pcurrent)
  34. {
  35. if (pcurrent->Compare(tk))
  36. {
  37. *ppRemoved = pcurrent;
  38. if (0 != psaved)
  39. {
  40. psaved->_pnext = pcurrent->_pnext;
  41. pcurrent->_pnext = 0;
  42. return(pfirst);
  43. }
  44. else
  45. {
  46. // removing the first element in the list
  47. ASSERT(pcurrent == pfirst);
  48. psaved = pcurrent->_pnext;
  49. pcurrent->_pnext = 0;
  50. return(psaved);
  51. }
  52. }
  53. psaved = pcurrent;
  54. pcurrent = pcurrent->_pnext;
  55. }
  56. *ppRemoved = 0;
  57. return(pfirst);
  58. }
  59. CTableElement *
  60. CTableElement::RemoveMatching(
  61. IN CTableElement *pte,
  62. OUT CTableElement **ppRemoved)
  63. /*++
  64. Routine Description:
  65. Helper function used to remove an element from a bucket in the hash table.
  66. Arguments:
  67. pte - Element to be removed, compared by pointer value.
  68. ppRemoved - Contains the element removed or NULL upon return.
  69. Return Value:
  70. Pointer to the remaining elements in the list if any. Use
  71. to replace the current pointer in the bucket.
  72. --*/
  73. {
  74. CTableElement *pcurrent = this;
  75. CTableElement *psaved = 0;
  76. CTableElement *pfirst = this;
  77. while(pcurrent)
  78. {
  79. if (pcurrent == pte)
  80. {
  81. *ppRemoved = pcurrent;
  82. if (0 != psaved)
  83. {
  84. psaved->_pnext = pcurrent->_pnext;
  85. pcurrent->_pnext = 0;
  86. return(pfirst);
  87. }
  88. else
  89. {
  90. // removing the first element in the list
  91. ASSERT(pcurrent == pfirst);
  92. psaved = pcurrent->_pnext;
  93. pcurrent->_pnext = 0;
  94. return(psaved);
  95. }
  96. }
  97. psaved = pcurrent;
  98. pcurrent = pcurrent->_pnext;
  99. }
  100. *ppRemoved = 0;
  101. return(pfirst);
  102. }
  103. CHashTable::CHashTable(ORSTATUS &status, UINT start_size)
  104. {
  105. _cBuckets = start_size;
  106. _cElements = 0;
  107. _last = 0;
  108. _buckets = new CTableElement *[start_size];
  109. if (0 == _buckets)
  110. {
  111. status = OR_NOMEM;
  112. return;
  113. }
  114. for(UINT i = 0; i < _cBuckets; i++)
  115. {
  116. _buckets[i] = NULL;
  117. }
  118. status = OR_OK;
  119. }
  120. CHashTable::~CHashTable()
  121. {
  122. #if 0
  123. #if DBG
  124. for(UINT i = 0; i < _cBuckets; i++)
  125. ASSERT(_buckets[i] == 0);
  126. #endif
  127. delete _buckets;
  128. #endif
  129. ASSERT(0); // D'tor unused 12/95
  130. }
  131. CTableElement *
  132. CHashTable::Lookup(
  133. IN CTableKey &id
  134. )
  135. {
  136. DWORD hash = id.Hash();
  137. CTableElement *it;
  138. it = _buckets[hash % _cBuckets];
  139. while(it)
  140. {
  141. if (it->Compare(id))
  142. {
  143. return(it);
  144. }
  145. it = it->Next();
  146. }
  147. return(0);
  148. }
  149. void
  150. CHashTable::Add(
  151. IN CTableElement *pElement
  152. )
  153. {
  154. DWORD hash = pElement->Hash();
  155. hash %= _cBuckets;
  156. _buckets[hash] = _buckets[hash]->Insert(pElement);
  157. _cElements++;
  158. if (_cElements > _cBuckets)
  159. {
  160. // Try to grow the table. If the allocation fails no need to worry,
  161. // everything still works but might be a bit slower.
  162. CTableElement **ppte = new CTableElement *[_cBuckets * 2];
  163. if (ppte)
  164. {
  165. UINT i, uiBucketsOld = _cBuckets;
  166. CTableElement *pteT1, *pteT2;
  167. CTableElement **ppteOld = _buckets;
  168. KdPrintEx((DPFLTR_DCOMSS_ID,
  169. DPFLTR_INFO_LEVEL,
  170. "OR: Growing table: %p\n",
  171. this));
  172. // Change to the larger array of buckets.
  173. _cBuckets *= 2;
  174. for(i = 0; i < _cBuckets; i++)
  175. {
  176. ppte[i] = NULL;
  177. }
  178. _buckets = ppte;
  179. // Move every element from the old table into the large table.
  180. for(i = 0; i < uiBucketsOld; i++)
  181. {
  182. pteT1 = ppteOld[i];
  183. while(pteT1)
  184. {
  185. pteT2 = pteT1->Next();
  186. pteT1->Unlink();
  187. // Same as calling Add() but don't update _cElements.
  188. hash = pteT1->Hash();
  189. hash %= _cBuckets;
  190. _buckets[hash] = _buckets[hash]->Insert(pteT1);
  191. pteT1 = pteT2;
  192. }
  193. }
  194. }
  195. }
  196. return;
  197. }
  198. CTableElement *
  199. CHashTable::Remove(
  200. IN CTableKey &id
  201. )
  202. /*++
  203. Routine Description:
  204. Looks up and removes an element from the table.
  205. Arguments:
  206. id - The key to match the element to be removed
  207. Return Value:
  208. NULL - The element was not in the table
  209. non-NULL - A pointer to the element which was removed.
  210. --*/
  211. {
  212. DWORD hash = id.Hash();
  213. CTableElement *pte;
  214. hash %= _cBuckets;
  215. _buckets[hash] = (_buckets[hash])->RemoveMatching(id, &pte);
  216. if (pte)
  217. {
  218. _cElements--;
  219. if (_last == pte)
  220. {
  221. _last = _buckets[hash];
  222. }
  223. }
  224. return pte;
  225. }
  226. void
  227. CHashTable::Remove(
  228. IN CTableElement *pElement
  229. )
  230. /*++
  231. Routine Description:
  232. Used to remove an element from the table given a pointer to it.
  233. Arguments:
  234. pElement - the element to be removed. This pointer value,
  235. keys are not compared.
  236. Return Value:
  237. None
  238. --*/
  239. {
  240. DWORD hash = pElement->Hash();
  241. CTableElement *pte;
  242. hash %= _cBuckets;
  243. _buckets[hash] = (_buckets[hash])->RemoveMatching(pElement, &pte);
  244. if (pte)
  245. {
  246. ASSERT(pte == pElement);
  247. _cElements--;
  248. if (_last == pElement)
  249. {
  250. _last = _buckets[hash];
  251. }
  252. }
  253. return;
  254. }
  255. CTableElement *
  256. CHashTable::Another(
  257. )
  258. /*++
  259. Routine Description:
  260. Returns an element from the table. Usually this will be
  261. element found after the element last returned from this
  262. function. It may, due to races not solved here, repeat
  263. an element or skip an element.
  264. Races occur when accessing the "_last" memeber; this
  265. function is called while holding a shared lock. (More
  266. then one thread may call it at a time.)
  267. This isn't as bad as it sounds. _last can only
  268. be set to null in Remove() which requires exclusive
  269. access.
  270. Arguments:
  271. None
  272. Return Value:
  273. NULL or a pointer to an element in the table.
  274. --*/
  275. {
  276. CTableElement *panother;
  277. int i, end;
  278. if (_cElements == 0)
  279. {
  280. return(0);
  281. }
  282. if (_last)
  283. {
  284. if (panother = _last->Next())
  285. {
  286. if (panother)
  287. {
  288. _last = panother;
  289. return(panother);
  290. }
  291. }
  292. ASSERT(panother == 0);
  293. // no next, start looking from just after last's hash.
  294. i = _last->Hash();
  295. // Exersise for the reader (x + y) mod n == ( x mod n + y mod n ) mod n
  296. end = i = (i + 1) % _cBuckets;
  297. }
  298. else
  299. {
  300. // no last, start from the start.
  301. i = 0;
  302. end = _cBuckets - 1;
  303. }
  304. do
  305. {
  306. if (_buckets[i])
  307. {
  308. panother = _buckets[i];
  309. ASSERT(panother);
  310. _last = panother;
  311. return(panother);
  312. }
  313. i = (i + 1) % _cBuckets;
  314. }
  315. while (i != end);
  316. // Doesn't mean the table is empty, just that we didn't find
  317. // another element. These are not the same thing.
  318. return(0);
  319. }