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.

635 lines
16 KiB

  1. #include "nt.h"
  2. #include "ntdef.h"
  3. #include "ntrtl.h"
  4. #include "nturtl.h"
  5. #include "sxs-rtl.h"
  6. #include "skiplist.h"
  7. #include "xmlassert.h"
  8. typedef unsigned char *PBYTE;
  9. NTSTATUS
  10. RtlpFindChunkForElementIndex(
  11. PRTL_GROWING_LIST pList,
  12. ULONG ulIndex,
  13. PRTL_GROWING_LIST_CHUNK *ppListChunk,
  14. SIZE_T *pulChunkOffset
  15. )
  16. /*++
  17. Purpose:
  18. Finds the chunk for the given index. This could probably be made faster if
  19. and when we start using skiplists. As it stands, we just have to walk through
  20. the list until the index looked for is inside one of the lists.
  21. Parameters:
  22. pList - Growing list management structure
  23. ulIndex - Index requested by the caller
  24. ppListChunk - Pointer to a pointer to a list chunk. On return, points to
  25. the list chunk containing the index.
  26. pulChunkOffset - Offset into the chunk (in elements) that was requested
  27. Returns:
  28. STATUS_SUCCESS - Chunk was found, ppListChunk and pulChunkOffset point to
  29. the values listed in the 'parameters' section.
  30. STATUS_NOT_FOUND - The index was beyond the end of the chunk sections.
  31. --*/
  32. {
  33. PRTL_GROWING_LIST_CHUNK pHere = NULL;
  34. //
  35. // Is the index in the internal list?
  36. //
  37. ASSERT(ulIndex >= pList->cInternalElements);
  38. ASSERT(pList != NULL);
  39. ASSERT(ppListChunk != NULL);
  40. *ppListChunk = NULL;
  41. if (pulChunkOffset) {
  42. *pulChunkOffset = 0;
  43. }
  44. //
  45. // Chop off the number of elements in the internal list
  46. //
  47. ulIndex -= pList->cInternalElements;
  48. //
  49. // Move through list chunks until the index is inside one
  50. // of them. A smarter bear would have made all the chunks the
  51. // same size and could then have just skipped ahead the right
  52. // number, avoiding comparisons.
  53. //
  54. pHere = pList->pFirstChunk;
  55. while ((ulIndex >= pList->cElementsPerChunk) && pHere) {
  56. pHere = pHere->pNextChunk;
  57. ulIndex -= pList->cElementsPerChunk;
  58. }
  59. //
  60. // Set pointer over
  61. //
  62. if (ulIndex < pList->cElementsPerChunk) {
  63. *ppListChunk = pHere;
  64. }
  65. //
  66. // And if the caller cared what chunk this was in, then tell them.
  67. //
  68. if (pulChunkOffset && *ppListChunk) {
  69. *pulChunkOffset = ulIndex;
  70. }
  71. return pHere ? STATUS_SUCCESS : STATUS_NOT_FOUND;
  72. }
  73. NTSTATUS
  74. RtlInitializeGrowingList(
  75. PRTL_GROWING_LIST pList,
  76. SIZE_T cbElementSize,
  77. ULONG cElementsPerChunk,
  78. PVOID pvInitialListBuffer,
  79. SIZE_T cbInitialListBuffer,
  80. PRTL_ALLOCATOR Allocation
  81. )
  82. {
  83. if ((pList == NULL) ||
  84. (cElementsPerChunk == 0) ||
  85. (cbElementSize == 0))
  86. {
  87. return STATUS_INVALID_PARAMETER;
  88. }
  89. RtlZeroMemory(pList, sizeof(*pList));
  90. pList->cbElementSize = cbElementSize;
  91. pList->cElementsPerChunk = cElementsPerChunk;
  92. pList->Allocator = *Allocation;
  93. //
  94. // Set up the initial list pointer
  95. //
  96. if (pvInitialListBuffer != NULL) {
  97. pList->pvInternalList = pvInitialListBuffer;
  98. // Conversion downwards to a ulong, but it's still valid, right?
  99. pList->cInternalElements = (ULONG)(cbInitialListBuffer / cbElementSize);
  100. pList->cTotalElements = pList->cInternalElements;
  101. }
  102. return STATUS_SUCCESS;
  103. }
  104. NTSTATUS
  105. RtlpExpandGrowingList(
  106. PRTL_GROWING_LIST pList,
  107. ULONG ulMinimalIndexCount
  108. )
  109. /*++
  110. Purpose:
  111. Given a growing list, expand it to be able to contain at least
  112. ulMinimalIndexCount elements. Does this by allocating chunks via the
  113. allocator in the list structure and adding them to the growing list
  114. chunk set.
  115. Parameters:
  116. pList - Growing list structure to be expanded
  117. ulMinimalIndexCount - On return, the pList will have at least enough
  118. slots to contain this many elements.
  119. Return codes:
  120. STATUS_SUCCESS - Enough list chunks were allocated to hold the
  121. requested number of elements.
  122. STATUS_NO_MEMORY - Ran out of memory during allocation. Any allocated
  123. chunks were left allocated and remain owned by the growing list
  124. until destruction.
  125. STATUS_INVALID_PARAMETER - pList was NULL or invalid.
  126. --*/
  127. {
  128. NTSTATUS status = STATUS_SUCCESS;
  129. ULONG ulNecessaryChunks = 0;
  130. ULONG ulExtraElements = ulMinimalIndexCount;
  131. SIZE_T BytesInChunk;
  132. if ((pList == NULL) || (pList->Allocator.pfnAlloc == NULL)) {
  133. return STATUS_INVALID_PARAMETER;
  134. }
  135. //
  136. // Already got enough elements in the list? Great. The caller
  137. // was a bit overactive.
  138. //
  139. if (pList->cTotalElements > ulMinimalIndexCount) {
  140. return STATUS_SUCCESS;
  141. }
  142. //
  143. // Whack off the number of elements already on the list.
  144. //
  145. ulExtraElements -= pList->cTotalElements;
  146. //
  147. // How many chunks is that? Remember to round up.
  148. //
  149. ulNecessaryChunks = ulExtraElements / pList->cElementsPerChunk;
  150. ulNecessaryChunks++;
  151. //
  152. // Let's go allocate them, one by one
  153. //
  154. BytesInChunk = (pList->cbElementSize * pList->cElementsPerChunk) +
  155. sizeof(RTL_GROWING_LIST_CHUNK);
  156. while (ulNecessaryChunks--) {
  157. PRTL_GROWING_LIST_CHUNK pNewChunk = NULL;
  158. //
  159. // Allocate some memory for the chunk
  160. //
  161. status = pList->Allocator.pfnAlloc(BytesInChunk, (PVOID*)&pNewChunk, pList->Allocator.pvContext);
  162. if (!NT_SUCCESS(status)) {
  163. return STATUS_NO_MEMORY;
  164. }
  165. //
  166. // Set up the new chunk
  167. //
  168. pNewChunk->pGrowingListParent = pList;
  169. pNewChunk->pNextChunk = NULL;
  170. if (pList->pLastChunk) {
  171. //
  172. // Swizzle the list of chunks to include this one
  173. //
  174. pList->pLastChunk->pNextChunk = pNewChunk;
  175. }
  176. pList->pLastChunk = pNewChunk;
  177. pList->cTotalElements += pList->cElementsPerChunk;
  178. //
  179. // If there wasn't a first chunk, this one is.
  180. //
  181. if (pList->pFirstChunk == NULL) {
  182. pList->pFirstChunk = pNewChunk;
  183. }
  184. }
  185. return STATUS_SUCCESS;
  186. }
  187. NTSTATUS
  188. RtlIndexIntoGrowingList(
  189. PRTL_GROWING_LIST pList,
  190. ULONG ulIndex,
  191. PVOID *ppvPointerToSpace,
  192. BOOLEAN fGrowingAllowed
  193. )
  194. {
  195. NTSTATUS status = STATUS_SUCCESS;
  196. if ((pList == NULL) || (ppvPointerToSpace == NULL)) {
  197. return STATUS_INVALID_PARAMETER;
  198. }
  199. *ppvPointerToSpace = NULL;
  200. //
  201. // If the index is beyond the current total number of elements, but we're
  202. // not allowing growing, then say it wasn't found. Otherwise, we'll always
  203. // grow the array as necessary to contain the index passed.
  204. //
  205. if ((ulIndex >= pList->cTotalElements) && !fGrowingAllowed) {
  206. return STATUS_NOT_FOUND;
  207. }
  208. //
  209. // This element is in the internal list, so just figure out where
  210. // and point at it. Do this only if there's an internal element
  211. // list.
  212. //
  213. if ((ulIndex < pList->cInternalElements) && pList->cInternalElements) {
  214. //
  215. // The pointer to the space they want is ulIndex*pList->cbElementSize
  216. // bytes down the pointer pList->pvInternalList
  217. //
  218. *ppvPointerToSpace = ((PBYTE)(pList->pvInternalList)) + (ulIndex * pList->cbElementSize);
  219. return STATUS_SUCCESS;
  220. }
  221. //
  222. // Otherwise, the index is outside the internal list, find out which one
  223. // it was supposed to be in.
  224. //
  225. else {
  226. PRTL_GROWING_LIST_CHUNK pThisChunk = NULL;
  227. SIZE_T ulNewOffset = 0;
  228. PBYTE pbData = NULL;
  229. status = RtlpFindChunkForElementIndex(pList, ulIndex, &pThisChunk, &ulNewOffset);
  230. //
  231. // Success! Go move the chunk pointer past the header of the growing list
  232. // chunk, and then index off it to find the right place.
  233. //
  234. if (NT_SUCCESS(status)) {
  235. pbData = ((PBYTE)(pThisChunk + 1)) + (pList->cbElementSize * ulNewOffset);
  236. }
  237. //
  238. // Otherwise, the chunk wasn't found, so we have to go allocate some new
  239. // chunks to hold it, then try again.
  240. //
  241. else if (status == STATUS_NOT_FOUND) {
  242. //
  243. // Expand the list
  244. //
  245. if (!NT_SUCCESS(status = RtlpExpandGrowingList(pList, ulIndex))) {
  246. goto Exit;
  247. }
  248. //
  249. // Look again
  250. //
  251. status = RtlpFindChunkForElementIndex(pList, ulIndex, &pThisChunk, &ulNewOffset);
  252. if (!NT_SUCCESS(status)) {
  253. goto Exit;
  254. }
  255. //
  256. // Adjust pointers
  257. //
  258. pbData = ((PBYTE)(pThisChunk + 1)) + (pList->cbElementSize * ulNewOffset);
  259. }
  260. else {
  261. goto Exit;
  262. }
  263. //
  264. // One of the above should have set the pbData pointer to point at the requested
  265. // grown-list space.
  266. //
  267. *ppvPointerToSpace = pbData;
  268. }
  269. Exit:
  270. return status;
  271. }
  272. NTSTATUS
  273. RtlDestroyGrowingList(
  274. PRTL_GROWING_LIST pList
  275. )
  276. /*++
  277. Purpose:
  278. Destroys (deallocates) all the chunks that had been allocated to this
  279. growing list structure. Returns the list to the "fresh" state of having
  280. only the 'internal' element count.
  281. Parameters:
  282. pList - List structure to be destroyed
  283. Returns:
  284. STATUS_SUCCESS - Structure was completely cleaned out
  285. --*/
  286. {
  287. NTSTATUS status = STATUS_SUCCESS;
  288. if ((pList == NULL) || (pList->Allocator.pfnFree == NULL)) {
  289. return STATUS_INVALID_PARAMETER;
  290. }
  291. //
  292. // Zing through and kill all the list bits
  293. //
  294. while (pList->pFirstChunk != NULL) {
  295. PRTL_GROWING_LIST_CHUNK pHere;
  296. pHere = pList->pFirstChunk;
  297. pList->pFirstChunk = pList->pFirstChunk->pNextChunk;
  298. if (!NT_SUCCESS(status = pList->Allocator.pfnFree(pHere, pList->Allocator.pvContext))) {
  299. return status;
  300. }
  301. pList->cTotalElements -= pList->cElementsPerChunk;
  302. }
  303. ASSERT(pList->pFirstChunk == NULL);
  304. //
  305. // Reset the things that change as we expand the list
  306. //
  307. pList->pLastChunk = pList->pFirstChunk = NULL;
  308. pList->cTotalElements = pList->cInternalElements;
  309. return status;
  310. }
  311. NTSTATUS
  312. RtlCloneGrowingList(
  313. ULONG ulFlags,
  314. PRTL_GROWING_LIST pDestination,
  315. PRTL_GROWING_LIST pSource,
  316. ULONG ulSourceCount
  317. )
  318. {
  319. NTSTATUS status = STATUS_SUCCESS;
  320. ULONG ul;
  321. PVOID pvSourceCursor, pvDestCursor;
  322. SIZE_T cbBytes;
  323. //
  324. // No flags, no null values, element byte size has to match,
  325. // and the source/dest can't be the same.
  326. //
  327. if (((ulFlags != 0) || !pDestination || !pSource) ||
  328. (pDestination->cbElementSize != pSource->cbElementSize) ||
  329. (pDestination == pSource))
  330. return STATUS_INVALID_PARAMETER;
  331. cbBytes = pDestination->cbElementSize;
  332. //
  333. // Now copy bytes around
  334. //
  335. for (ul = 0; ul < ulSourceCount; ul++) {
  336. status = RtlIndexIntoGrowingList(pSource, ul, &pvSourceCursor, FALSE);
  337. if (!NT_SUCCESS(status))
  338. goto Exit;
  339. status = RtlIndexIntoGrowingList(pDestination, ul, &pvDestCursor, TRUE);
  340. if (!NT_SUCCESS(status))
  341. goto Exit;
  342. RtlCopyMemory(pvDestCursor, pvSourceCursor, cbBytes);
  343. }
  344. status = STATUS_SUCCESS;
  345. Exit:
  346. return status;
  347. }
  348. NTSTATUS
  349. RtlAllocateGrowingList(
  350. PRTL_GROWING_LIST *ppGrowingList,
  351. SIZE_T cbThingSize,
  352. PRTL_ALLOCATOR Allocation
  353. )
  354. {
  355. PRTL_GROWING_LIST pvWorkingList = NULL;
  356. NTSTATUS status = STATUS_SUCCESS;
  357. if (ppGrowingList != NULL)
  358. *ppGrowingList = NULL;
  359. else
  360. return STATUS_INVALID_PARAMETER;
  361. if (!Allocation)
  362. return STATUS_INVALID_PARAMETER_3;
  363. //
  364. // Allocate space
  365. //
  366. status = Allocation->pfnAlloc(sizeof(RTL_GROWING_LIST), &pvWorkingList, Allocation->pvContext);
  367. if (!NT_SUCCESS(status)) {
  368. goto Exit;
  369. }
  370. //
  371. // Set up the structure
  372. //
  373. status = RtlInitializeGrowingList(
  374. pvWorkingList,
  375. cbThingSize,
  376. 8,
  377. NULL,
  378. 0,
  379. Allocation);
  380. if (!NT_SUCCESS(status)) {
  381. goto Exit;
  382. }
  383. *ppGrowingList = pvWorkingList;
  384. pvWorkingList = NULL;
  385. status = STATUS_SUCCESS;
  386. Exit:
  387. if (pvWorkingList) {
  388. Allocation->pfnFree(pvWorkingList, Allocation->pvContext);
  389. }
  390. return status;
  391. }
  392. NTSTATUS
  393. RtlSearchGrowingList(
  394. PRTL_GROWING_LIST TheList,
  395. ULONG ItemCount,
  396. PFN_LIST_COMPARISON_CALLBACK SearchCallback,
  397. PVOID SearchTarget,
  398. PVOID SearchContext,
  399. PVOID *pvFoundItem
  400. )
  401. {
  402. NTSTATUS status = STATUS_SUCCESS;
  403. ULONG ul;
  404. int CompareResult = 0;
  405. if (pvFoundItem)
  406. *pvFoundItem = NULL;
  407. // if (TheList->ulFlags & GROWING_LIST_FLAG_IS_SORTED) {
  408. if (0) {
  409. }
  410. else {
  411. ULONG uTemp = ItemCount;
  412. ULONG uOffset = 0;
  413. PRTL_GROWING_LIST_CHUNK Chunklet;
  414. ul = 0;
  415. //
  416. // Scan the internal item list.
  417. //
  418. while ((ul < ItemCount) && (ul < TheList->cInternalElements)) {
  419. PVOID pvHere = (PVOID)(((ULONG_PTR)TheList->pvInternalList) + uOffset);
  420. status = SearchCallback(TheList, SearchTarget, pvHere, SearchContext, &CompareResult);
  421. if (!NT_SUCCESS(status)) {
  422. goto Exit;
  423. }
  424. if (CompareResult == 0) {
  425. if (pvFoundItem)
  426. *pvFoundItem = pvHere;
  427. status = STATUS_SUCCESS;
  428. goto Exit;
  429. }
  430. uOffset += TheList->cbElementSize;
  431. ul++;
  432. }
  433. //
  434. // Ok, we ran out of internal elements, do the same thing here but on the chunk list
  435. //
  436. Chunklet = TheList->pFirstChunk;
  437. while ((ul < ItemCount) && Chunklet) {
  438. PVOID Data = (PVOID)(Chunklet + 1);
  439. ULONG ulHighOffset = TheList->cElementsPerChunk * TheList->cbElementSize;
  440. uOffset = 0;
  441. //
  442. // Spin through the items in this chunklet
  443. //
  444. while (uOffset < ulHighOffset) {
  445. PVOID pvHere = (PVOID)(((ULONG_PTR)Data) + uOffset);
  446. status = SearchCallback(TheList, SearchTarget, pvHere, SearchContext, &CompareResult);
  447. if (!NT_SUCCESS(status)) {
  448. goto Exit;
  449. }
  450. if (CompareResult == 0) {
  451. if (pvFoundItem)
  452. *pvFoundItem = pvHere;
  453. status = STATUS_SUCCESS;
  454. goto Exit;
  455. }
  456. uOffset += TheList->cbElementSize;
  457. }
  458. }
  459. //
  460. // If we got here, we didn't find it in either the internal list or the external one.
  461. //
  462. status = STATUS_NOT_FOUND;
  463. if (pvFoundItem)
  464. *pvFoundItem = NULL;
  465. }
  466. Exit:
  467. return status;
  468. }