sortbase.pp 12 KB

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