sortbase.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. {
  2. This file is part of the Free Pascal Run Time Library (rtl)
  3. Copyright (c) 1999-2019 by the Free Pascal development team
  4. This file provides the base for the pluggable sorting algorithm
  5. support. It also provides a default QuickSort implementation.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit sortbase;
  13. {$MODE objfpc}
  14. interface
  15. type
  16. TListSortComparer_NoContext = function(Item1, Item2: Pointer): Integer;
  17. TPtrListSorter_NoContext = procedure(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_NoContext);
  18. TItemListSorter_NoContext = procedure(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_NoContext);
  19. TListSortComparer_Context = function(Item1, Item2, Context: Pointer): Integer;
  20. TListSortCustomItemExchanger_Context = procedure(Item1, Item2, Context: Pointer);
  21. TPtrListSorter_Context = procedure(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  22. TItemListSorter_Context = procedure(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  23. TItemListSorter_CustomItemExchanger_Context = procedure(Items: Pointer;
  24. ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context;
  25. Exchanger: TListSortCustomItemExchanger_Context; Context: Pointer);
  26. PSortingAlgorithm = ^TSortingAlgorithm;
  27. TSortingAlgorithm = record
  28. PtrListSorter_NoContextComparer: TPtrListSorter_NoContext;
  29. PtrListSorter_ContextComparer: TPtrListSorter_Context;
  30. ItemListSorter_ContextComparer: TItemListSorter_Context;
  31. ItemListSorter_CustomItemExchanger_ContextComparer: TItemListSorter_CustomItemExchanger_Context;
  32. end;
  33. {
  34. QuickSort
  35. Average performance: O(n log n)
  36. Worst performance: O(n*n)
  37. Extra memory use: O(log n) on the stack
  38. Stable: no
  39. Additional notes: Uses the middle element as the pivot. This makes it work
  40. well also on already sorted sequences, which can occur
  41. often in practice. As expected from QuickSort, it works
  42. best on random sequences and is usually the fastest
  43. algorithm to sort them. It is, however, possible for a
  44. malicious user to craft special sequences, which trigger
  45. its worst O(n*n) case. They can also occur in practice,
  46. although they are very unlikely. If this is not an
  47. acceptable risk (e.g. for high risk applications,
  48. security-conscious applications or applications with hard
  49. real-time requirements), another sorting algorithm must
  50. be used.
  51. }
  52. procedure QuickSort_PtrList_NoContext(
  53. ItemPtrs: PPointer;
  54. ItemCount: SizeUInt;
  55. Comparer: TListSortComparer_NoContext);
  56. procedure QuickSort_PtrList_Context(
  57. ItemPtrs: PPointer;
  58. ItemCount: SizeUInt;
  59. Comparer: TListSortComparer_Context;
  60. Context: Pointer);
  61. procedure QuickSort_ItemList_Context(
  62. Items: Pointer;
  63. ItemCount, ItemSize: SizeUInt;
  64. Comparer: TListSortComparer_Context;
  65. Context: Pointer);
  66. procedure QuickSort_ItemList_CustomItemExchanger_Context(
  67. Items: Pointer;
  68. ItemCount, ItemSize: SizeUInt;
  69. Comparer: TListSortComparer_Context;
  70. Exchanger: TListSortCustomItemExchanger_Context;
  71. Context: Pointer);
  72. const
  73. QuickSort: TSortingAlgorithm = (
  74. PtrListSorter_NoContextComparer: @QuickSort_PtrList_NoContext;
  75. PtrListSorter_ContextComparer: @QuickSort_PtrList_Context;
  76. ItemListSorter_ContextComparer: @QuickSort_ItemList_Context;
  77. ItemListSorter_CustomItemExchanger_ContextComparer: @QuickSort_ItemList_CustomItemExchanger_Context;
  78. );
  79. var
  80. DefaultSortingAlgorithm: PSortingAlgorithm = @QuickSort;
  81. implementation
  82. Procedure QuickSort_PtrList_NoContext(ItemPtrs: PPointer; L, R : SizeUInt;
  83. Comparer: TListSortComparer_NoContext);
  84. var
  85. I, J, PivotIdx : SizeUInt;
  86. P, Q : Pointer;
  87. begin
  88. repeat
  89. I := L;
  90. J := R;
  91. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  92. P := ItemPtrs[PivotIdx];
  93. repeat
  94. while (I < PivotIdx) and (Comparer(P, ItemPtrs[i]) > 0) do
  95. Inc(I);
  96. while (J > PivotIdx) and (Comparer(P, ItemPtrs[J]) < 0) do
  97. Dec(J);
  98. if I < J then
  99. begin
  100. Q := ItemPtrs[I];
  101. ItemPtrs[I] := ItemPtrs[J];
  102. ItemPtrs[J] := Q;
  103. if PivotIdx = I then
  104. begin
  105. PivotIdx := J;
  106. Inc(I);
  107. end
  108. else if PivotIdx = J then
  109. begin
  110. PivotIdx := I;
  111. Dec(J);
  112. end
  113. else
  114. begin
  115. Inc(I);
  116. Dec(J);
  117. end;
  118. end;
  119. until I >= J;
  120. // sort the smaller range recursively
  121. // sort the bigger range via the loop
  122. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  123. if (PivotIdx - L) < (R - PivotIdx) then
  124. begin
  125. if (L + 1) < PivotIdx then
  126. QuickSort_PtrList_NoContext(ItemPtrs, L, PivotIdx - 1, Comparer);
  127. L := PivotIdx + 1;
  128. end
  129. else
  130. begin
  131. if (PivotIdx + 1) < R then
  132. QuickSort_PtrList_NoContext(ItemPtrs, PivotIdx + 1, R, Comparer);
  133. if (L + 1) < PivotIdx then
  134. R := PivotIdx - 1
  135. else
  136. exit;
  137. end;
  138. until L >= R;
  139. end;
  140. procedure QuickSort_PtrList_NoContext(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_NoContext);
  141. begin
  142. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  143. exit;
  144. QuickSort_PtrList_NoContext(ItemPtrs, 0, ItemCount - 1, Comparer);
  145. end;
  146. procedure QuickSort_PtrList_Context(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  147. procedure QuickSort(L, R : SizeUInt);
  148. var
  149. I, J, PivotIdx : SizeUInt;
  150. P, Q : Pointer;
  151. begin
  152. repeat
  153. I := L;
  154. J := R;
  155. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  156. P := ItemPtrs[PivotIdx];
  157. repeat
  158. while (I < PivotIdx) and (Comparer(P, ItemPtrs[I], Context) > 0) do
  159. Inc(I);
  160. while (J > PivotIdx) and (Comparer(P, ItemPtrs[J], Context) < 0) do
  161. Dec(J);
  162. if I < J then
  163. begin
  164. Q := ItemPtrs[I];
  165. ItemPtrs[I] := ItemPtrs[J];
  166. ItemPtrs[J] := Q;
  167. if PivotIdx = I then
  168. begin
  169. PivotIdx := J;
  170. Inc(I);
  171. end
  172. else if PivotIdx = J then
  173. begin
  174. PivotIdx := I;
  175. Dec(J);
  176. end
  177. else
  178. begin
  179. Inc(I);
  180. Dec(J);
  181. end;
  182. end;
  183. until I >= J;
  184. // sort the smaller range recursively
  185. // sort the bigger range via the loop
  186. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  187. if (PivotIdx - L) < (R - PivotIdx) then
  188. begin
  189. if (L + 1) < PivotIdx then
  190. QuickSort(L, PivotIdx - 1);
  191. L := PivotIdx + 1;
  192. end
  193. else
  194. begin
  195. if (PivotIdx + 1) < R then
  196. QuickSort(PivotIdx + 1, R);
  197. if (L + 1) < PivotIdx then
  198. R := PivotIdx - 1
  199. else
  200. exit;
  201. end;
  202. until L >= R;
  203. end;
  204. begin
  205. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  206. exit;
  207. QuickSort(0, ItemCount - 1);
  208. end;
  209. procedure QuickSort_ItemList_Context(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  210. var
  211. TempBuf: Pointer;
  212. procedure QuickSort(L, R : SizeUInt);
  213. var
  214. I, J, PivotIdx : SizeUInt;
  215. P : Pointer;
  216. begin
  217. repeat
  218. I := L;
  219. J := R;
  220. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  221. P := Items + ItemSize*PivotIdx;
  222. repeat
  223. while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
  224. Inc(I);
  225. while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
  226. Dec(J);
  227. if I < J then
  228. begin
  229. Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
  230. Move((Items + ItemSize*J)^, (Items + ItemSize*I)^, ItemSize);
  231. Move(TempBuf^, (Items + ItemSize*J)^, ItemSize);
  232. if PivotIdx = I then
  233. begin
  234. PivotIdx := J;
  235. P := Items + ItemSize*PivotIdx;
  236. Inc(I);
  237. end
  238. else if PivotIdx = J then
  239. begin
  240. PivotIdx := I;
  241. P := Items + ItemSize*PivotIdx;
  242. Dec(J);
  243. end
  244. else
  245. begin
  246. Inc(I);
  247. Dec(J);
  248. end;
  249. end;
  250. until I >= J;
  251. // sort the smaller range recursively
  252. // sort the bigger range via the loop
  253. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  254. if (PivotIdx - L) < (R - PivotIdx) then
  255. begin
  256. if (L + 1) < PivotIdx then
  257. QuickSort(L, PivotIdx - 1);
  258. L := PivotIdx + 1;
  259. end
  260. else
  261. begin
  262. if (PivotIdx + 1) < R then
  263. QuickSort(PivotIdx + 1, R);
  264. if (L + 1) < PivotIdx then
  265. R := PivotIdx - 1
  266. else
  267. exit;
  268. end;
  269. until L >= R;
  270. end;
  271. begin
  272. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  273. exit;
  274. GetMem(TempBuf, ItemSize);
  275. {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
  276. try
  277. QuickSort(0, ItemCount - 1);
  278. finally
  279. FreeMem(TempBuf, ItemSize);
  280. end;
  281. {$else FPC_HAS_FEATURE_EXCEPTIONS}
  282. QuickSort(0, ItemCount - 1);
  283. FreeMem(TempBuf, ItemSize);
  284. {$endif FPC_HAS_FEATURE_EXCEPTIONS}
  285. end;
  286. procedure QuickSort_ItemList_CustomItemExchanger_Context(
  287. Items: Pointer;
  288. ItemCount, ItemSize: SizeUInt;
  289. Comparer: TListSortComparer_Context;
  290. Exchanger: TListSortCustomItemExchanger_Context;
  291. Context: Pointer);
  292. procedure QuickSort(L, R : SizeUInt);
  293. var
  294. I, J, PivotIdx : SizeUInt;
  295. P : Pointer;
  296. begin
  297. repeat
  298. I := L;
  299. J := R;
  300. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  301. P := Items + ItemSize*PivotIdx;
  302. repeat
  303. while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
  304. Inc(I);
  305. while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
  306. Dec(J);
  307. if I < J then
  308. begin
  309. Exchanger(Items + ItemSize*I, Items + ItemSize*J, Context);
  310. if PivotIdx = I then
  311. begin
  312. PivotIdx := J;
  313. P := Items + ItemSize*PivotIdx;
  314. Inc(I);
  315. end
  316. else if PivotIdx = J then
  317. begin
  318. PivotIdx := I;
  319. P := Items + ItemSize*PivotIdx;
  320. Dec(J);
  321. end
  322. else
  323. begin
  324. Inc(I);
  325. Dec(J);
  326. end;
  327. end;
  328. until I >= J;
  329. // sort the smaller range recursively
  330. // sort the bigger range via the loop
  331. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  332. if (PivotIdx - L) < (R - PivotIdx) then
  333. begin
  334. if (L + 1) < PivotIdx then
  335. QuickSort(L, PivotIdx - 1);
  336. L := PivotIdx + 1;
  337. end
  338. else
  339. begin
  340. if (PivotIdx + 1) < R then
  341. QuickSort(PivotIdx + 1, R);
  342. if (L + 1) < PivotIdx then
  343. R := PivotIdx - 1
  344. else
  345. exit;
  346. end;
  347. until L >= R;
  348. end;
  349. begin
  350. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  351. exit;
  352. QuickSort(0, ItemCount - 1);
  353. end;
  354. end.