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.

612 lines
14 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. dict.c
  5. Abstract:
  6. This module implements a dictionary package. A dictionary is a
  7. generic mapping of an arbitrary domain to an arbitrary range.
  8. This implementation uses a hash-table to give constant time insert
  9. and delete access to the elements in the table, assuming the table is
  10. relativly close in size to the number of elements in the table.
  11. Author:
  12. Matthew D Hendel (math) 9-Feb-2001
  13. Revision History:
  14. --*/
  15. #include "precomp.h"
  16. #define DICT_TAG ('tciD')
  17. #ifdef ExAllocatePool
  18. #undef ExAllocatePool
  19. #define ExAllocatePool(Type, Size) ExAllocatePoolWithTag(Type, Size, DICT_TAG)
  20. #endif
  21. #ifdef ExFreePool
  22. #undef ExFreePool
  23. #define ExFreePool(Type) ExFreePoolWithTag (Type, DICT_TAG)
  24. #endif
  25. NTSTATUS
  26. StorCreateDictionary(
  27. IN PSTOR_DICTIONARY Dictionary,
  28. IN ULONG EntryCount,
  29. IN POOL_TYPE PoolType,
  30. IN STOR_DICTIONARY_GET_KEY_ROUTINE GetKeyRoutine,
  31. IN STOR_DICTIONARY_COMPARE_KEY_ROUTINE CompareKeyRoutine, OPTIONAL
  32. IN STOR_DICTIONARY_HASH_KEY_ROUTINE HashKeyRoutine OPTIONAL
  33. )
  34. /*++
  35. Routine Description:
  36. Initialize a dictionary object.
  37. Arguments:
  38. Dictionary - Supplies the dictionary object to initialize.
  39. EntryCount - Supplies the initial number of empty slots in the dictioanry
  40. table. This number can increase via a call to StorSetElementCount.
  41. PoolType - Pool type of memory to be used.
  42. GetKeyRoutine - User-supplied routine to get a key from a specific
  43. element.
  44. CompareKeyRoutine - User-supplied routine to compare the keys of
  45. two elements. If this routine is not supplied, the default
  46. comparison will be used which assumes the values of the keys
  47. are ULONGs.
  48. HashKeyRoutine - User-supplied routine to has the key to a ULONG.
  49. If this routine is not supplied, the default has routine
  50. merely returns the value of the key as a ULONG.
  51. Return Value:
  52. NTSTATUS code.
  53. --*/
  54. {
  55. ULONG i;
  56. PLIST_ENTRY Entries;
  57. Dictionary->MaxEntryCount = EntryCount;
  58. Dictionary->EntryCount = 0;
  59. Dictionary->PoolType = PoolType;
  60. Dictionary->GetKeyRoutine = GetKeyRoutine;
  61. if (CompareKeyRoutine != NULL) {
  62. Dictionary->CompareKeyRoutine = CompareKeyRoutine;
  63. } else {
  64. Dictionary->CompareKeyRoutine = StorCompareUlongKey;
  65. }
  66. if (HashKeyRoutine != NULL) {
  67. Dictionary->HashKeyRoutine = HashKeyRoutine;
  68. } else {
  69. Dictionary->HashKeyRoutine = StorHashUlongKey;
  70. }
  71. Entries = ExAllocatePool (PoolType,
  72. EntryCount * sizeof (LIST_ENTRY));
  73. if (Entries == NULL) {
  74. return STATUS_NO_MEMORY;
  75. }
  76. //
  77. // Initialize the table of lists.
  78. //
  79. for (i = 0; i < EntryCount; i++) {
  80. InitializeListHead (&Entries[i]);
  81. }
  82. Dictionary->Entries = Entries;
  83. return STATUS_SUCCESS;
  84. }
  85. NTSTATUS
  86. StorDeleteDictionary(
  87. IN PSTOR_DICTIONARY Dictionary
  88. )
  89. /*++
  90. Routine Description:
  91. Delete the dictionary and all resources held by the dictionary.
  92. NB: This routine does not delete all of the individual elements from
  93. the dictionary -- it can't. You should delete the elements from the
  94. dictionary before calling this routine.
  95. Arguments:
  96. Dictionary - Supplies the dictinoary to delete.
  97. Return Value:
  98. NTSTATUS code.
  99. --*/
  100. {
  101. if (Dictionary->EntryCount != 0) {
  102. ASSERT (FALSE);
  103. //
  104. //NB: should we define a new NTSTATUS value for
  105. //STATUS_NOT_EMPTY condition?
  106. //
  107. return STATUS_DIRECTORY_NOT_EMPTY;
  108. }
  109. ASSERT (Dictionary->Entries != NULL);
  110. ExFreePool (Dictionary->Entries);
  111. return STATUS_SUCCESS;
  112. }
  113. NTSTATUS
  114. StorInsertDictionary(
  115. IN PSTOR_DICTIONARY Dictionary,
  116. IN PSTOR_DICTIONARY_ENTRY Entry
  117. )
  118. /*++
  119. Routine Description:
  120. Insert an entry into the dictionary.
  121. Arguments:
  122. Dictionary - Supplies the dictionary to insert into.
  123. Entry - Supplies the entry to insert.
  124. Return Value:
  125. NTSTATUS code.
  126. --*/
  127. {
  128. NTSTATUS Status;
  129. ULONG Index;
  130. PLIST_ENTRY NextEntry;
  131. PLIST_ENTRY ListHead;
  132. LONG Comparison;
  133. STOR_DICTIONARY_GET_KEY_ROUTINE GetKeyRoutine;
  134. STOR_DICTIONARY_COMPARE_KEY_ROUTINE CompareRoutine;
  135. STOR_DICTIONARY_HASH_KEY_ROUTINE HashRoutine;
  136. GetKeyRoutine = Dictionary->GetKeyRoutine;
  137. CompareRoutine = Dictionary->CompareKeyRoutine;
  138. HashRoutine = Dictionary->HashKeyRoutine;
  139. Index = (HashRoutine (GetKeyRoutine (Entry)) % Dictionary->MaxEntryCount);
  140. ListHead = &Dictionary->Entries[Index];
  141. //
  142. // Otherwise, walk the list searching for the place to insert the entry.
  143. //
  144. for (NextEntry = ListHead->Flink;
  145. NextEntry != ListHead;
  146. NextEntry = NextEntry->Flink) {
  147. Comparison = CompareRoutine (GetKeyRoutine (NextEntry),
  148. GetKeyRoutine (Entry));
  149. if (Comparison == 0) {
  150. return STATUS_DUPLICATE_OBJECTID;
  151. } else if (Comparison < 0) {
  152. //
  153. // Insert the entry directly before this entry.
  154. Entry->Flink = NextEntry;
  155. Entry->Blink = NextEntry->Blink;
  156. Entry->Flink->Blink = Entry;
  157. Entry->Blink->Flink = Entry;
  158. ASSERT (Entry->Flink->Blink == Entry);
  159. ASSERT (Entry->Blink->Flink == Entry);
  160. #if DBG
  161. for (NextEntry = ListHead->Flink;
  162. NextEntry != ListHead;
  163. NextEntry = NextEntry->Flink) {
  164. NOTHING;
  165. }
  166. #endif
  167. Dictionary->EntryCount++;
  168. return STATUS_SUCCESS;
  169. } else {
  170. //
  171. // Continue searching
  172. //
  173. ASSERT (Comparison > 0);
  174. }
  175. }
  176. //
  177. // We'll only exit the loop if there isn't a entry less than the
  178. // one we're inserting. The list is either empty, or all of the
  179. // entries in the list are less than the one we're inserting.
  180. // In either case, the correct action is to add and entry to the
  181. // end of the list.
  182. //
  183. Dictionary->EntryCount++;
  184. InsertTailList (ListHead, Entry);
  185. return STATUS_SUCCESS;
  186. }
  187. NTSTATUS
  188. StorFindDictionary(
  189. IN PSTOR_DICTIONARY Dictionary,
  190. IN PVOID Key,
  191. OUT PSTOR_DICTIONARY_ENTRY* EntryBuffer OPTIONAL
  192. )
  193. /*++
  194. Routine Description:
  195. Find an entry in the dictionary and, optionally, retun the found
  196. entry.
  197. Arguments:
  198. Dictionary - Supplies the dictionary to search through.
  199. Key - Supplies the key of the entry to search for.
  200. EntryBuffer - Supplies an optional buffer where the entry will be copied
  201. if found.
  202. Return Value:
  203. STATUS_NOT_FOUND - If the entry could not be found.
  204. STATUS_SUCCESS - If the entry was successfully found.
  205. Other NTSTATUS code - For other errors.
  206. --*/
  207. {
  208. NTSTATUS Status;
  209. LONG Comparison;
  210. ULONG Index;
  211. PLIST_ENTRY ListHead;
  212. PLIST_ENTRY NextEntry;
  213. STOR_DICTIONARY_GET_KEY_ROUTINE GetKeyRoutine;
  214. STOR_DICTIONARY_COMPARE_KEY_ROUTINE CompareRoutine;
  215. STOR_DICTIONARY_HASH_KEY_ROUTINE HashRoutine;
  216. GetKeyRoutine = Dictionary->GetKeyRoutine;
  217. CompareRoutine = Dictionary->CompareKeyRoutine;
  218. HashRoutine = Dictionary->HashKeyRoutine;
  219. Index = HashRoutine (Key) % Dictionary->MaxEntryCount;
  220. ListHead = &Dictionary->Entries[Index];
  221. Status = STATUS_NOT_FOUND;
  222. for (NextEntry = ListHead->Flink;
  223. NextEntry != ListHead;
  224. NextEntry = NextEntry->Flink) {
  225. Comparison = CompareRoutine (GetKeyRoutine (NextEntry), Key);
  226. if (Comparison == 0) {
  227. //
  228. // Found it.
  229. //
  230. Status = STATUS_SUCCESS;
  231. if (EntryBuffer) {
  232. *EntryBuffer = NextEntry;
  233. }
  234. break;
  235. } else if (Comparison < 0) {
  236. //
  237. // Done searching
  238. //
  239. Status = STATUS_NOT_FOUND;
  240. if (EntryBuffer) {
  241. *EntryBuffer = NULL;
  242. }
  243. break;
  244. } else {
  245. //
  246. // Continue searching
  247. //
  248. ASSERT (Comparison > 0);
  249. }
  250. }
  251. return Status;
  252. }
  253. NTSTATUS
  254. StorRemoveDictionary(
  255. IN PSTOR_DICTIONARY Dictionary,
  256. IN PVOID Key,
  257. OUT PSTOR_DICTIONARY_ENTRY* EntryBuffer OPTIONAL
  258. )
  259. /*++
  260. Routine Description:
  261. Remove an entry from the dictionary.
  262. Arguments:
  263. Dictioanry - Supplies the dictionary to remove the entry from.
  264. Key - Supplies the key used to identify the entry.
  265. EntryBuffer - Optional parameter that supplies a buffer to copy the
  266. removed entry into.
  267. Return Value:
  268. STATUS_NOT_FOUND - If the entry was not found.
  269. STATUS_SUCCESS - If the entry was successfully removed.
  270. Other NTSTATUS code - other error condition.
  271. --*/
  272. {
  273. NTSTATUS Status;
  274. PSTOR_DICTIONARY_ENTRY Entry;
  275. Entry = NULL;
  276. Status = StorFindDictionary (Dictionary, Key, &Entry);
  277. if (NT_SUCCESS (Status)) {
  278. RemoveEntryList (Entry);
  279. Dictionary->EntryCount--;
  280. }
  281. if (EntryBuffer) {
  282. *EntryBuffer = Entry;
  283. }
  284. return Status;
  285. }
  286. NTSTATUS
  287. StorAdjustDictionarySize(
  288. IN PSTOR_DICTIONARY Dictionary,
  289. IN ULONG MaxEntryCount
  290. )
  291. /*++
  292. Routine Description:
  293. Adjust the number of bins in the underlying hash table. Having the
  294. number of bins relativly large compared to the number of entries in
  295. the table gives much better performance.
  296. Adjusting the size of the dictionary is an expensive operation. It
  297. takes about the same amount of time to adjust the dictionary size as
  298. it does to delete the dictionary and create a new one.
  299. Arguments:
  300. Dictionary - Supplies the dictionary whose size is to be adjusted.
  301. MaxEntryCount - Supplies the new maximum entry count. This can be
  302. greater or less than the current entry count. (It can
  303. actually be the same as the current entry count, but
  304. doing so merely wastes time.)
  305. Return Value:
  306. NTSTATUS code.
  307. --*/
  308. {
  309. NTSTATUS Status;
  310. ULONG i;
  311. ULONG OldMaxEntryCount;
  312. PLIST_ENTRY OldEntries;
  313. PLIST_ENTRY Entries;
  314. PLIST_ENTRY Head;
  315. PLIST_ENTRY Entry;
  316. OldEntries = Dictionary->Entries;
  317. OldMaxEntryCount = Dictionary->MaxEntryCount;
  318. Entries = ExAllocatePool (Dictionary->PoolType,
  319. sizeof (LIST_ENTRY) * MaxEntryCount);
  320. if (Entries == NULL) {
  321. return STATUS_NO_MEMORY;
  322. }
  323. for (i = 0; i < MaxEntryCount; i++) {
  324. InitializeListHead (&Entries[i]);
  325. }
  326. //
  327. // Save the old dictionary
  328. //
  329. OldEntries = Dictionary->Entries;
  330. OldMaxEntryCount = Dictionary->MaxEntryCount;
  331. //
  332. // Replace it with the new, empty one
  333. //
  334. Dictionary->Entries = Entries;
  335. Dictionary->MaxEntryCount = MaxEntryCount;
  336. //
  337. // Remove all the old entries, placing them in the new dictioanry
  338. //
  339. for (i = 0; i < OldMaxEntryCount; i++) {
  340. Head = &OldEntries[i];
  341. while (!IsListEmpty (Head)) {
  342. Entry = RemoveHeadList (Head);
  343. Status = StorInsertDictionary (Dictionary, Entry);
  344. ASSERT (NT_SUCCESS (Status));
  345. }
  346. }
  347. ExFreePool (Entries);
  348. return STATUS_SUCCESS;
  349. }
  350. VOID
  351. StorEnumerateDictionary(
  352. IN PSTOR_DICTIONARY Dictionary,
  353. IN PSTOR_DICTIONARY_ENUMERATOR Enumerator
  354. )
  355. /*++
  356. Routine Description:
  357. Enumerate the entries in the dictionary.
  358. Arguments:
  359. Dictionary - Supplies the dictionary to enumerate.
  360. Enumerator - Supplies an enumerator used to enumerate the dictionary.
  361. To halt the enumeration, the enumerator should return FALSE.
  362. Caveats:
  363. The entries are listed in ARBITRARY ORDER. This is not an ordered
  364. enumeration.
  365. Multiple enumerations of the list can happen at the same time. But
  366. the list CANNOT BE MODIFIED while it is being enumerated.
  367. Return Value:
  368. None.
  369. --*/
  370. {
  371. ULONG i;
  372. PLIST_ENTRY NextEntry;
  373. PLIST_ENTRY ListHead;
  374. BOOLEAN Continue;
  375. REVIEW();
  376. for (i = 0; i < Dictionary->MaxEntryCount; i++) {
  377. ListHead = &Dictionary->Entries[i];
  378. for (NextEntry = ListHead->Flink;
  379. NextEntry != ListHead;
  380. NextEntry = NextEntry->Flink) {
  381. Continue = Enumerator->EnumerateEntry (Enumerator, NextEntry);
  382. if (!Continue) {
  383. return ;
  384. }
  385. }
  386. }
  387. }
  388. LONG
  389. StorCompareUlongKey(
  390. IN PVOID Key1,
  391. IN PVOID Key2
  392. )
  393. /*++
  394. Routine Description:
  395. Compare key routine for ULONG keys.
  396. Arguments:
  397. Key1 - First key to compare.
  398. Key2 - Second key to compare.
  399. Return Value:
  400. -1 - if Key1 < Key2
  401. 0 - if Key1 == Key2
  402. 1 - if Key1 > Key2
  403. --*/
  404. {
  405. if (Key1 < Key2) {
  406. return -1;
  407. } else if (Key1 == Key2) {
  408. return 0;
  409. } else {
  410. return 1;
  411. }
  412. }
  413. ULONG
  414. StorHashUlongKey(
  415. IN PVOID Key
  416. )
  417. /*++
  418. Routine Description:
  419. Hash routine for ULONG keys.
  420. Arguments:
  421. Key - Supplies the key to hash.
  422. Return Value:
  423. Hash code for the key.
  424. --*/
  425. {
  426. return (ULONG)(ULONG_PTR)Key;
  427. }