sortbase.pp 10 KB

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