sortbase.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_NoContext);
  53. procedure QuickSort_PtrList_Context(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  54. procedure QuickSort_ItemList_Context(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  55. procedure QuickSort_ItemList_CustomItemExchanger_Context(
  56. Items: Pointer;
  57. ItemCount, ItemSize: SizeUInt;
  58. Comparer: TListSortComparer_Context;
  59. Exchanger: TListSortCustomItemExchanger_Context;
  60. Context: Pointer);
  61. const
  62. QuickSort: TSortingAlgorithm = (
  63. PtrListSorter_NoContextComparer: @QuickSort_PtrList_NoContext;
  64. PtrListSorter_ContextComparer: @QuickSort_PtrList_Context;
  65. ItemListSorter_ContextComparer: @QuickSort_ItemList_Context;
  66. ItemListSorter_CustomItemExchanger_ContextComparer: @QuickSort_ItemList_CustomItemExchanger_Context;
  67. );
  68. var
  69. DefaultSortingAlgorithm: PSortingAlgorithm = @QuickSort;
  70. implementation
  71. Procedure QuickSort_PtrList_NoContext(ItemPtrs: PPointer; L, R : SizeUInt;
  72. Comparer: TListSortComparer_NoContext);
  73. var
  74. I, J, PivotIdx : SizeUInt;
  75. P, Q : Pointer;
  76. begin
  77. repeat
  78. I := L;
  79. J := R;
  80. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  81. P := ItemPtrs[PivotIdx];
  82. repeat
  83. while (I < PivotIdx) and (Comparer(P, ItemPtrs[i]) > 0) do
  84. Inc(I);
  85. while (J > PivotIdx) and (Comparer(P, ItemPtrs[J]) < 0) do
  86. Dec(J);
  87. if I < J then
  88. begin
  89. Q := ItemPtrs[I];
  90. ItemPtrs[I] := ItemPtrs[J];
  91. ItemPtrs[J] := Q;
  92. if PivotIdx = I then
  93. begin
  94. PivotIdx := J;
  95. Inc(I);
  96. end
  97. else if PivotIdx = J then
  98. begin
  99. PivotIdx := I;
  100. Dec(J);
  101. end
  102. else
  103. begin
  104. Inc(I);
  105. Dec(J);
  106. end;
  107. end;
  108. until I >= J;
  109. // sort the smaller range recursively
  110. // sort the bigger range via the loop
  111. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  112. if (PivotIdx - L) < (R - PivotIdx) then
  113. begin
  114. if (L + 1) < PivotIdx then
  115. QuickSort_PtrList_NoContext(ItemPtrs, L, PivotIdx - 1, Comparer);
  116. L := PivotIdx + 1;
  117. end
  118. else
  119. begin
  120. if (PivotIdx + 1) < R then
  121. QuickSort_PtrList_NoContext(ItemPtrs, PivotIdx + 1, R, Comparer);
  122. if (L + 1) < PivotIdx then
  123. R := PivotIdx - 1
  124. else
  125. exit;
  126. end;
  127. until L >= R;
  128. end;
  129. procedure QuickSort_PtrList_NoContext(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_NoContext);
  130. begin
  131. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  132. exit;
  133. QuickSort_PtrList_NoContext(ItemPtrs, 0, ItemCount - 1, Comparer);
  134. end;
  135. procedure QuickSort_PtrList_Context(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  136. procedure QuickSort(L, R : SizeUInt);
  137. var
  138. I, J, PivotIdx : SizeUInt;
  139. P, Q : Pointer;
  140. begin
  141. repeat
  142. I := L;
  143. J := R;
  144. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  145. P := ItemPtrs[PivotIdx];
  146. repeat
  147. while (I < PivotIdx) and (Comparer(P, ItemPtrs[I], Context) > 0) do
  148. Inc(I);
  149. while (J > PivotIdx) and (Comparer(P, ItemPtrs[J], Context) < 0) do
  150. Dec(J);
  151. if I < J then
  152. begin
  153. Q := ItemPtrs[I];
  154. ItemPtrs[I] := ItemPtrs[J];
  155. ItemPtrs[J] := Q;
  156. if PivotIdx = I then
  157. begin
  158. PivotIdx := J;
  159. Inc(I);
  160. end
  161. else if PivotIdx = J then
  162. begin
  163. PivotIdx := I;
  164. Dec(J);
  165. end
  166. else
  167. begin
  168. Inc(I);
  169. Dec(J);
  170. end;
  171. end;
  172. until I >= J;
  173. // sort the smaller range recursively
  174. // sort the bigger range via the loop
  175. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  176. if (PivotIdx - L) < (R - PivotIdx) then
  177. begin
  178. if (L + 1) < PivotIdx then
  179. QuickSort(L, PivotIdx - 1);
  180. L := PivotIdx + 1;
  181. end
  182. else
  183. begin
  184. if (PivotIdx + 1) < R then
  185. QuickSort(PivotIdx + 1, R);
  186. if (L + 1) < PivotIdx then
  187. R := PivotIdx - 1
  188. else
  189. exit;
  190. end;
  191. until L >= R;
  192. end;
  193. begin
  194. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  195. exit;
  196. QuickSort(0, ItemCount - 1);
  197. end;
  198. procedure QuickSort_ItemList_Context(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  199. var
  200. TempBuf: Pointer;
  201. procedure QuickSort(L, R : SizeUInt);
  202. var
  203. I, J, PivotIdx : SizeUInt;
  204. P : Pointer;
  205. begin
  206. repeat
  207. I := L;
  208. J := R;
  209. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  210. P := Items + ItemSize*PivotIdx;
  211. repeat
  212. while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
  213. Inc(I);
  214. while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
  215. Dec(J);
  216. if I < J then
  217. begin
  218. Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
  219. Move((Items + ItemSize*J)^, (Items + ItemSize*I)^, ItemSize);
  220. Move(TempBuf^, (Items + ItemSize*J)^, ItemSize);
  221. if PivotIdx = I then
  222. begin
  223. PivotIdx := J;
  224. P := Items + ItemSize*PivotIdx;
  225. Inc(I);
  226. end
  227. else if PivotIdx = J then
  228. begin
  229. PivotIdx := I;
  230. P := Items + ItemSize*PivotIdx;
  231. Dec(J);
  232. end
  233. else
  234. begin
  235. Inc(I);
  236. Dec(J);
  237. end;
  238. end;
  239. until I >= J;
  240. // sort the smaller range recursively
  241. // sort the bigger range via the loop
  242. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  243. if (PivotIdx - L) < (R - PivotIdx) then
  244. begin
  245. if (L + 1) < PivotIdx then
  246. QuickSort(L, PivotIdx - 1);
  247. L := PivotIdx + 1;
  248. end
  249. else
  250. begin
  251. if (PivotIdx + 1) < R then
  252. QuickSort(PivotIdx + 1, R);
  253. if (L + 1) < PivotIdx then
  254. R := PivotIdx - 1
  255. else
  256. exit;
  257. end;
  258. until L >= R;
  259. end;
  260. begin
  261. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  262. exit;
  263. GetMem(TempBuf, ItemSize);
  264. try
  265. QuickSort(0, ItemCount - 1);
  266. finally
  267. FreeMem(TempBuf, ItemSize);
  268. end;
  269. end;
  270. procedure QuickSort_ItemList_CustomItemExchanger_Context(
  271. Items: Pointer;
  272. ItemCount, ItemSize: SizeUInt;
  273. Comparer: TListSortComparer_Context;
  274. Exchanger: TListSortCustomItemExchanger_Context;
  275. Context: Pointer);
  276. procedure QuickSort(L, R : SizeUInt);
  277. var
  278. I, J, PivotIdx : SizeUInt;
  279. P : Pointer;
  280. begin
  281. repeat
  282. I := L;
  283. J := R;
  284. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  285. P := Items + ItemSize*PivotIdx;
  286. repeat
  287. while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
  288. Inc(I);
  289. while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
  290. Dec(J);
  291. if I < J then
  292. begin
  293. Exchanger(Items + ItemSize*I, Items + ItemSize*J, Context);
  294. if PivotIdx = I then
  295. begin
  296. PivotIdx := J;
  297. P := Items + ItemSize*PivotIdx;
  298. Inc(I);
  299. end
  300. else if PivotIdx = J then
  301. begin
  302. PivotIdx := I;
  303. P := Items + ItemSize*PivotIdx;
  304. Dec(J);
  305. end
  306. else
  307. begin
  308. Inc(I);
  309. Dec(J);
  310. end;
  311. end;
  312. until I >= J;
  313. // sort the smaller range recursively
  314. // sort the bigger range via the loop
  315. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  316. if (PivotIdx - L) < (R - PivotIdx) then
  317. begin
  318. if (L + 1) < PivotIdx then
  319. QuickSort(L, PivotIdx - 1);
  320. L := PivotIdx + 1;
  321. end
  322. else
  323. begin
  324. if (PivotIdx + 1) < R then
  325. QuickSort(PivotIdx + 1, R);
  326. if (L + 1) < PivotIdx then
  327. R := PivotIdx - 1
  328. else
  329. exit;
  330. end;
  331. until L >= R;
  332. end;
  333. begin
  334. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  335. exit;
  336. QuickSort(0, ItemCount - 1);
  337. end;
  338. end.