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.

516 lines
14 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation
  3. Module Name:
  4. array.c
  5. Abstract:
  6. This module contains some slightly higher level functions
  7. for dealing the arrays.
  8. Author:
  9. Jay Krell (JayKrell) April 2001
  10. Revision History:
  11. Environment:
  12. anywhere
  13. --*/
  14. #include "ntrtlp.h"
  15. //
  16. // the type of the comparison callback used by qsort and bsearch,
  17. // and optionally RtlMergeSortedArrays and RtlRemoveAdjacentEquivalentArrayElements
  18. //
  19. typedef
  20. int
  21. (__cdecl *
  22. RTL_QSORT_BSEARCH_COMPARISON_FUNCTION)(
  23. CONST VOID * Element1,
  24. CONST VOID * Element2
  25. );
  26. //
  27. // the type of the comparison callback usually used by
  28. // RtlMergeSortedArrays and RtlRemoveAdjacentEquivalentArrayElements
  29. //
  30. typedef
  31. RTL_GENERIC_COMPARE_RESULTS
  32. (NTAPI *
  33. RTL_COMPARE_ARRAY_ELEMENT_FUNCTION)(
  34. PVOID Context,
  35. IN CONST VOID * Element1,
  36. IN CONST VOID * Element2,
  37. IN SIZE_T ElementSize
  38. );
  39. //
  40. // the callback to RtlMergeSortedArrays and RtlRemoveAdjacentEquivalentArrayElements
  41. // to copy an elmement, if not via RtlCopyMemory
  42. //
  43. typedef
  44. VOID
  45. (NTAPI *
  46. RTL_COPY_ARRAY_ELEMENT_FUNCTION)(
  47. PVOID Context,
  48. PVOID To,
  49. CONST VOID * From,
  50. IN SIZE_T ElementSize
  51. );
  52. #define \
  53. RTL_MERGE_SORTED_ARRAYS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE \
  54. (0x00000001)
  55. // deliberately not NTSYSAPI, so it can be linked to statically w/o warning
  56. NTSTATUS
  57. NTAPI
  58. RtlMergeSortedArrays(
  59. IN ULONG Flags,
  60. IN CONST VOID * VoidArray1,
  61. IN SIZE_T Count1,
  62. IN CONST VOID * VoidArray2,
  63. IN SIZE_T Count2,
  64. // VoidResult == NULL is useful for getting the count first.
  65. OUT PVOID VoidResult OPTIONAL,
  66. OUT PSIZE_T OutResultCount,
  67. IN SIZE_T ElementSize,
  68. IN RTL_COMPARE_ARRAY_ELEMENT_FUNCTION CompareCallback,
  69. PVOID CompareContext OPTIONAL,
  70. IN RTL_COPY_ARRAY_ELEMENT_FUNCTION CopyCallback OPTIONAL,
  71. PVOID CopyContext OPTIONAL
  72. );
  73. #define \
  74. RTL_REMOVE_ADJACENT_EQUIVALENT_ARRAY_ELEMENTS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE \
  75. (0x00000001)
  76. // deliberately not NTSYSAPI, so it can be linked to statically w/o warning
  77. NTSTATUS
  78. NTAPI
  79. RtlRemoveAdjacentEquivalentArrayElements(
  80. IN ULONG Flags,
  81. IN OUT PVOID VoidArray,
  82. IN SIZE_T Count,
  83. OUT PSIZE_T OutCount,
  84. IN SIZE_T ElementSize,
  85. IN RTL_COMPARE_ARRAY_ELEMENT_FUNCTION CompareCallback,
  86. PVOID CompareContext OPTIONAL,
  87. IN RTL_COPY_ARRAY_ELEMENT_FUNCTION CopyCallback OPTIONAL,
  88. PVOID CopyContext OPTIONAL
  89. );
  90. typedef CONST VOID* PCVOID;
  91. RTL_GENERIC_COMPARE_RESULTS
  92. NTAPI
  93. RtlpQsortBsearchCompareAdapter(
  94. PVOID VoidContext,
  95. IN PCVOID VoidElement1,
  96. IN PCVOID VoidElement2,
  97. IN SIZE_T ElementSize
  98. )
  99. /*++
  100. Routine Description:
  101. This function is an "adapter" used so people can use comparison functions intended
  102. for use with qsort and bsearch with RtlMergeSortedArrays
  103. and RtlRemoveAdjacentEquivalentArrayElements.
  104. Arguments:
  105. VoidContext - the actual qsort/bsearch comparison callback function
  106. VoidElement1 - an elment to compare
  107. VoidElement2 - another elment to compare
  108. Return Value:
  109. GenericLessThan
  110. GenericGreaterThan
  111. GenericEqual
  112. --*/
  113. {
  114. CONST RTL_QSORT_BSEARCH_COMPARISON_FUNCTION QsortBsearchCompareCallback =
  115. (RTL_QSORT_BSEARCH_COMPARISON_FUNCTION)VoidContext;
  116. CONST int i = (*QsortBsearchCompareCallback)(VoidElement1, VoidElement2);
  117. return (i < 0) ? GenericLessThan : (i > 0) ? GenericGreaterThan : GenericEqual;
  118. }
  119. VOID
  120. NTAPI
  121. RtlpDoNothingCopyArrayElement(
  122. PVOID Context,
  123. OUT PVOID To,
  124. IN CONST VOID * From,
  125. IN SIZE_T ElementSize
  126. )
  127. /*++
  128. Routine Description:
  129. RtlMergeSortedArrays uses this for CopyCallback when ResultArray==NULL, which
  130. is useful for a two pass sequence that first determines
  131. the size of the result, then allocates it, then writes it.
  132. Arguments:
  133. Context - ignored
  134. To - ignored
  135. From - ignored
  136. Return Value:
  137. none
  138. --*/
  139. {
  140. UNREFERENCED_PARAMETER(Context);
  141. UNREFERENCED_PARAMETER(To);
  142. UNREFERENCED_PARAMETER(From);
  143. // do nothing, deliberately
  144. }
  145. NTSTATUS
  146. NTAPI
  147. RtlRemoveAdjacentEquivalentArrayElements( // aka "unique" (if sorted)
  148. IN ULONG Flags,
  149. IN OUT PVOID VoidArray,
  150. IN SIZE_T Count,
  151. OUT PSIZE_T OutCount,
  152. IN SIZE_T ElementSize,
  153. IN RTL_COMPARE_ARRAY_ELEMENT_FUNCTION CompareCallback,
  154. PVOID CompareContext OPTIONAL,
  155. IN RTL_COPY_ARRAY_ELEMENT_FUNCTION CopyCallback OPTIONAL, // defaults to RtlCopyMemory
  156. PVOID CopyContext OPTIONAL
  157. )
  158. /*++
  159. Routine Description:
  160. remove ajdacent equivalent elements from an array
  161. aka "unique", if the array is sorted
  162. Arguments:
  163. VoidArray - the start of a array
  164. Count - the number of elements in the array
  165. ElementSize - the sizes of the elements in the array, in bytes
  166. CompareCallback - a tristate comparison function in the style of qsort/bsearch
  167. Return Value:
  168. The return value is the number of elements contained
  169. in the resulting array.
  170. --*/
  171. {
  172. CONST PUCHAR Base = (PUCHAR)VoidArray;
  173. CONST PUCHAR End = (PUCHAR)(Base + Count * ElementSize);
  174. PUCHAR LastAccepted = Base;
  175. PUCHAR NextAccepted = (Base + ElementSize);
  176. PUCHAR Iterator = NextAccepted;
  177. NTSTATUS Status;
  178. if (OutCount == NULL) {
  179. Status = STATUS_INVALID_PARAMETER;
  180. goto Exit;
  181. }
  182. *OutCount = 0;
  183. if ((Flags & ~RTL_REMOVE_ADJACENT_EQUIVALENT_ARRAY_ELEMENTS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE) != 0) {
  184. Status = STATUS_INVALID_PARAMETER;
  185. goto Exit;
  186. }
  187. if ((Flags & RTL_REMOVE_ADJACENT_EQUIVALENT_ARRAY_ELEMENTS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE) != 0) {
  188. CompareCallback = (RTL_COMPARE_ARRAY_ELEMENT_FUNCTION)RtlpQsortBsearchCompareAdapter;
  189. CompareContext = (PVOID)CompareCallback;
  190. }
  191. if (Count < 2) {
  192. *OutCount = 1;
  193. Status = STATUS_SUCCESS;
  194. goto Exit;
  195. }
  196. Count = 1; // always take the first element
  197. for ( ; Iterator != End ; Iterator += ElementSize) {
  198. if ((*CompareCallback)(CompareContext, Iterator, LastAccepted, ElementSize) != 0) {
  199. if (Iterator != NextAccepted) {
  200. if (CopyCallback != NULL) {
  201. (*CopyCallback)(CopyContext, NextAccepted, Iterator, ElementSize);
  202. } else {
  203. RtlCopyMemory(NextAccepted, Iterator, ElementSize);
  204. }
  205. } else {
  206. // do not bother copying until we have skipped any elements
  207. }
  208. LastAccepted = NextAccepted;
  209. NextAccepted += ElementSize;
  210. Count += 1;
  211. }
  212. }
  213. *OutCount = Count;
  214. Status = STATUS_SUCCESS;
  215. Exit:
  216. return Status;
  217. }
  218. NTSTATUS
  219. NTAPI
  220. RtlMergeSortedArrays(
  221. IN ULONG Flags,
  222. IN PCVOID VoidArray1,
  223. IN SIZE_T Count1,
  224. IN PCVOID VoidArray2,
  225. IN SIZE_T Count2,
  226. OUT PVOID VoidResult OPTIONAL,
  227. OUT PSIZE_T OutResultCount,
  228. IN SIZE_T ElementSize,
  229. IN RTL_COMPARE_ARRAY_ELEMENT_FUNCTION CompareCallback,
  230. PVOID CompareContext OPTIONAL,
  231. IN RTL_COPY_ARRAY_ELEMENT_FUNCTION CopyCallback,
  232. PVOID CopyContext OPTIONAL
  233. )
  234. /*++
  235. Routine Description:
  236. Merge two sorted arrays into a third array.
  237. The third array must be preallocated to the size of the sum
  238. of the sizes of the two input arrays
  239. Arguments:
  240. VoidArray1 - the start of an array
  241. Count1 - the number of elements in the array that starts at VoidArray1
  242. VoidArray2 - the start of another array
  243. Count2 - the number of elements in the array that starts at VoidArray2
  244. VoidResult - the resulting array, must be capable of holding Count1+Count2 elements
  245. if this parameter is not supplied, no copying is done and the just the
  246. resulting required size is returned
  247. ElementSize - the sizes of the elements in all the arrays, in bytes
  248. CompareCallback - a tristate comparison function in the style of qsort/bsearch, plus it takes an additional parameter for context
  249. Return Value:
  250. The return value is the number of elements contained
  251. in the resulting array VoidResult.
  252. --*/
  253. {
  254. PUCHAR Array1 = (PUCHAR)VoidArray1;
  255. PUCHAR Array2 = (PUCHAR)VoidArray2;
  256. PUCHAR ResultArray = (PUCHAR)VoidResult;
  257. SIZE_T ResultCount = 0;
  258. NTSTATUS Status;
  259. if (OutResultCount == NULL) {
  260. Status = STATUS_INVALID_PARAMETER;
  261. goto Exit;
  262. }
  263. *OutResultCount = 0;
  264. if ((Flags & ~RTL_MERGE_SORTED_ARRAYS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE) != 0) {
  265. Status = STATUS_INVALID_PARAMETER;
  266. goto Exit;
  267. }
  268. //
  269. // This is useful to get back the resulting count, before allocating
  270. // the space.
  271. //
  272. if (VoidResult == NULL) {
  273. CopyCallback = RtlpDoNothingCopyArrayElement;
  274. }
  275. if ((Flags & RTL_MERGE_SORTED_ARRAYS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE) != 0) {
  276. CompareCallback = RtlpQsortBsearchCompareAdapter;
  277. CompareContext = (PVOID)CompareCallback;
  278. }
  279. for ( ; Count1 != 0 && Count2 != 0 ; ) {
  280. CONST int CompareResult = (*CompareCallback)(CompareContext, Array1, Array2, ElementSize);
  281. if (CompareResult < 0) {
  282. if (CopyCallback != NULL) {
  283. (*CopyCallback)(CopyContext, ResultArray, Array1, ElementSize);
  284. } else {
  285. RtlCopyMemory(ResultArray, Array1, ElementSize);
  286. }
  287. Array1 += ElementSize;
  288. Count1 -= 1;
  289. }
  290. else if (CompareResult > 0) {
  291. if (CopyCallback != NULL) {
  292. (*CopyCallback)(CopyContext, ResultArray, Array2, ElementSize);
  293. } else {
  294. RtlCopyMemory(ResultArray, Array2, ElementSize);
  295. }
  296. Array2 += ElementSize;
  297. Count2 -= 1;
  298. }
  299. else /* CompareResult == 0 */ {
  300. //
  301. // move past the elements in both arrays, chosing arbitrarily
  302. // which one to take (Array1)
  303. //
  304. if (CopyCallback != NULL) {
  305. (*CopyCallback)(CopyContext, ResultArray, Array1, ElementSize);
  306. } else {
  307. RtlCopyMemory(ResultArray, Array1, ElementSize);
  308. }
  309. Array1 += ElementSize;
  310. Array2 += ElementSize;
  311. Count1 -= 1;
  312. Count2 -= 1;
  313. }
  314. ResultCount += 1;
  315. ResultArray += ElementSize;
  316. }
  317. //
  318. // now pick up the tail of whichever one has any left, if either
  319. //
  320. if (VoidResult == NULL) {
  321. ResultCount += Count1 + Count2;
  322. } else if (Count1 != 0) {
  323. ResultCount += Count1;
  324. if (CopyCallback != NULL) {
  325. while (Count1 != 0) {
  326. //
  327. // perhaps CopyCallback should be copy_n instead of copy_1,
  328. // so we might gain an efficiency over the loop with an uninlinable
  329. // calback..
  330. //
  331. (*CopyCallback)(CopyContext, ResultArray, Array1, ElementSize);
  332. Count1 -= 1;
  333. ResultArray += ElementSize;
  334. Array1 += ElementSize;
  335. }
  336. } else {
  337. RtlCopyMemory(ResultArray, Array1, Count1 * ElementSize);
  338. ResultArray += Count1 * ElementSize;
  339. }
  340. } else if (Count2 != 0) {
  341. ResultCount += Count2;
  342. if (CopyCallback != NULL) {
  343. while (Count2 != 0) {
  344. //
  345. // perhaps CopyCallback should be copy_n instead of copy_1
  346. //
  347. (*CopyCallback)(CopyContext, ResultArray, Array2, ElementSize);
  348. Count2 -= 1;
  349. ResultArray += ElementSize;
  350. Array2 += ElementSize;
  351. }
  352. } else {
  353. RtlCopyMemory(ResultArray, Array2, Count2 * ElementSize);
  354. //optimize away ResultArray += Count2 * ElementSize;
  355. }
  356. }
  357. *OutResultCount = ResultCount;
  358. Status = STATUS_SUCCESS;
  359. Exit:
  360. return ResultCount;
  361. }
  362. #if 0 // test cases
  363. int __cdecl CompareULONG(PCVOID v1, PCVOID v2)
  364. {
  365. ULONG i1 = *(ULONG*)v1;
  366. ULONG i2 = *(ULONG*)v2;
  367. if (i1 > i2)
  368. return 1;
  369. if (i1 < i2)
  370. return -1;
  371. return 0;
  372. }
  373. void RtlpTestUnique(const char * s)
  374. {
  375. ULONG rg[64];
  376. ULONG i;
  377. ULONG len = strlen(s);
  378. for (i = 0 ; i != len ; ++i) rg[i] = s[i];
  379. len = RtlRemoveAdjacentEquivalentArrayElements(1, rg, len, sizeof(rg[0]), (PVOID)CompareULONG, NULL, NULL, NULL);
  380. printf("---");
  381. for (i = 0 ; i != len ; ++i) printf("%lu ", rg[i]);
  382. printf("---\n");
  383. }
  384. void RtlpTestMerge(const char * s, const char * t)
  385. {
  386. ULONG rg1[64];
  387. ULONG rg2[64];
  388. ULONG rg[128];
  389. ULONG i;
  390. ULONG slen = strlen(s);
  391. ULONG tlen = strlen(t);
  392. ULONG len;
  393. printf("merge(");
  394. for (i = 0 ; i != slen ; ++i) rg1[i] = s[i];
  395. for (i = 0 ; i != slen ; ++i) printf("%lu ", rg1[i]);
  396. printf(",");
  397. for (i = 0 ; i != tlen ; ++i) rg2[i] = t[i];
  398. for (i = 0 ; i != tlen ; ++i) printf("%lu ", rg2[i]);
  399. len = RtlMergeSortedArrays(1, rg1, slen, rg2, tlen, rg, sizeof(rg[0]), (PVOID)CompareULONG, NULL, NULL, NULL);
  400. printf(")=(---");
  401. for (i = 0 ; i != len ; ++i) printf("%lu ", rg[i]);
  402. printf(")\n");
  403. }
  404. int __cdecl main()
  405. {
  406. RtlpTestUnique("");
  407. RtlpTestUnique("\1");
  408. RtlpTestUnique("\1\2");
  409. RtlpTestUnique("\1\2\3");
  410. RtlpTestUnique("\1\1\2\3");
  411. RtlpTestUnique("\1\2\2\3");
  412. RtlpTestUnique("\1\2\3\3");
  413. RtlpTestUnique("\1\1\1\2\2\2\2\2\3");
  414. RtlpTestUnique("\1\1\1\2\3\3\3\3\3\3\3\3");
  415. RtlpTestUnique("\1\2\2\2\2\2\2\3\3\3\3\3\3\3\3");
  416. RtlpTestUnique("\1\1\1\1\1\2\2\2\2\2\3\3\3\3\3\3");
  417. RtlpTestUnique("\1\1\1\1\1\2\2\2\2\2\3\3\3\3\3\3\1");
  418. RtlpTestMerge("\1\2\3", "\4\5\6");
  419. RtlpTestMerge("\1\3\5", "\2\4\6");
  420. RtlpTestMerge("\1\2\3", "\2\4\6");
  421. RtlpTestMerge("\1\1\1\2\3", "\2\4\6\6\6");
  422. return 0;
  423. }
  424. #endif