string_sort.hpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. // Details for a templated general-case hybrid-radix string_sort.
  2. // Copyright Steven J. Ross 2001 - 2014.
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. // See http://www.boost.org/libs/sort for library home page.
  7. /*
  8. Some improvements suggested by:
  9. Phil Endecott and Frank Gennari
  10. */
  11. #ifndef BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_HPP
  12. #define BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_HPP
  13. #include <algorithm>
  14. #include <vector>
  15. #include <cstring>
  16. #include <limits>
  17. #include <functional>
  18. #include <boost/static_assert.hpp>
  19. #include <boost/serialization/static_warning.hpp>
  20. #include <boost/utility/enable_if.hpp>
  21. #include <boost/sort/spreadsort/detail/constants.hpp>
  22. #include <boost/sort/spreadsort/detail/spreadsort_common.hpp>
  23. #include <boost/cstdint.hpp>
  24. namespace boost {
  25. namespace sort {
  26. namespace spreadsort {
  27. namespace detail {
  28. static const int max_step_size = 64;
  29. //Offsetting on identical characters. This function works a chunk of
  30. //characters at a time for cache efficiency and optimal worst-case
  31. //performance.
  32. template<class RandomAccessIter, class Unsigned_char_type>
  33. inline void
  34. update_offset(RandomAccessIter first, RandomAccessIter finish,
  35. size_t &char_offset)
  36. {
  37. const int char_size = sizeof(Unsigned_char_type);
  38. size_t nextOffset = char_offset;
  39. int step_size = max_step_size / char_size;
  40. while (true) {
  41. RandomAccessIter curr = first;
  42. do {
  43. //Ignore empties, but if the nextOffset would exceed the length or
  44. //not match, exit; we've found the last matching character
  45. //This will reduce the step_size if the current step doesn't match.
  46. if ((*curr).size() > char_offset) {
  47. if((*curr).size() <= (nextOffset + step_size)) {
  48. step_size = (*curr).size() - nextOffset - 1;
  49. if (step_size < 1) {
  50. char_offset = nextOffset;
  51. return;
  52. }
  53. }
  54. const int step_byte_size = step_size * char_size;
  55. if (memcmp(curr->data() + nextOffset, first->data() + nextOffset,
  56. step_byte_size) != 0) {
  57. if (step_size == 1) {
  58. char_offset = nextOffset;
  59. return;
  60. }
  61. step_size = (step_size > 4) ? 4 : 1;
  62. continue;
  63. }
  64. }
  65. ++curr;
  66. } while (curr != finish);
  67. nextOffset += step_size;
  68. }
  69. }
  70. //Offsetting on identical characters. This function works a character
  71. //at a time for optimal worst-case performance.
  72. template<class RandomAccessIter, class Get_char, class Get_length>
  73. inline void
  74. update_offset(RandomAccessIter first, RandomAccessIter finish,
  75. size_t &char_offset, Get_char get_character, Get_length length)
  76. {
  77. size_t nextOffset = char_offset;
  78. while (true) {
  79. RandomAccessIter curr = first;
  80. do {
  81. //ignore empties, but if the nextOffset would exceed the length or
  82. //not match, exit; we've found the last matching character
  83. if (length(*curr) > char_offset && (length(*curr) <= (nextOffset + 1)
  84. || get_character((*curr), nextOffset) != get_character((*first), nextOffset))) {
  85. char_offset = nextOffset;
  86. return;
  87. }
  88. } while (++curr != finish);
  89. ++nextOffset;
  90. }
  91. }
  92. //This comparison functor assumes strings are identical up to char_offset
  93. template<class Data_type, class Unsigned_char_type>
  94. struct offset_less_than {
  95. offset_less_than(size_t char_offset) : fchar_offset(char_offset){}
  96. inline bool operator()(const Data_type &x, const Data_type &y) const
  97. {
  98. size_t minSize = (std::min)(x.size(), y.size());
  99. for (size_t u = fchar_offset; u < minSize; ++u) {
  100. BOOST_STATIC_ASSERT(sizeof(x[u]) == sizeof(Unsigned_char_type));
  101. if (static_cast<Unsigned_char_type>(x[u]) !=
  102. static_cast<Unsigned_char_type>(y[u])) {
  103. return static_cast<Unsigned_char_type>(x[u]) <
  104. static_cast<Unsigned_char_type>(y[u]);
  105. }
  106. }
  107. return x.size() < y.size();
  108. }
  109. size_t fchar_offset;
  110. };
  111. //Compares strings assuming they are identical up to char_offset
  112. template<class Data_type, class Unsigned_char_type>
  113. struct offset_greater_than {
  114. offset_greater_than(size_t char_offset) : fchar_offset(char_offset){}
  115. inline bool operator()(const Data_type &x, const Data_type &y) const
  116. {
  117. size_t minSize = (std::min)(x.size(), y.size());
  118. for (size_t u = fchar_offset; u < minSize; ++u) {
  119. BOOST_STATIC_ASSERT(sizeof(x[u]) == sizeof(Unsigned_char_type));
  120. if (static_cast<Unsigned_char_type>(x[u]) !=
  121. static_cast<Unsigned_char_type>(y[u])) {
  122. return static_cast<Unsigned_char_type>(x[u]) >
  123. static_cast<Unsigned_char_type>(y[u]);
  124. }
  125. }
  126. return x.size() > y.size();
  127. }
  128. size_t fchar_offset;
  129. };
  130. //This comparison functor assumes strings are identical up to char_offset
  131. template<class Data_type, class Get_char, class Get_length>
  132. struct offset_char_less_than {
  133. offset_char_less_than(size_t char_offset) : fchar_offset(char_offset){}
  134. inline bool operator()(const Data_type &x, const Data_type &y) const
  135. {
  136. size_t minSize = (std::min)(length(x), length(y));
  137. for (size_t u = fchar_offset; u < minSize; ++u) {
  138. if (get_character(x, u) != get_character(y, u)) {
  139. return get_character(x, u) < get_character(y, u);
  140. }
  141. }
  142. return length(x) < length(y);
  143. }
  144. size_t fchar_offset;
  145. Get_char get_character;
  146. Get_length length;
  147. };
  148. //String sorting recursive implementation
  149. template <class RandomAccessIter, class Unsigned_char_type>
  150. inline void
  151. string_sort_rec(RandomAccessIter first, RandomAccessIter last,
  152. size_t char_offset,
  153. std::vector<RandomAccessIter> &bin_cache,
  154. unsigned cache_offset, size_t *bin_sizes)
  155. {
  156. typedef typename std::iterator_traits<RandomAccessIter>::value_type
  157. Data_type;
  158. //This section makes handling of long identical substrings much faster
  159. //with a mild average performance impact.
  160. //Iterate to the end of the empties. If all empty, return
  161. while ((*first).size() <= char_offset) {
  162. if (++first == last)
  163. return;
  164. }
  165. RandomAccessIter finish = last - 1;
  166. //Getting the last non-empty
  167. for (;(*finish).size() <= char_offset; --finish);
  168. ++finish;
  169. //Offsetting on identical characters. This section works
  170. //a few characters at a time for optimal worst-case performance.
  171. update_offset<RandomAccessIter, Unsigned_char_type>(first, finish,
  172. char_offset);
  173. const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
  174. //Equal worst-case of radix and comparison is when bin_count = n*log(n).
  175. const unsigned max_size = bin_count;
  176. const unsigned membin_count = bin_count + 1;
  177. unsigned cache_end;
  178. RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
  179. cache_end, membin_count) + 1;
  180. //Calculating the size of each bin; this takes roughly 10% of runtime
  181. for (RandomAccessIter current = first; current != last; ++current) {
  182. if ((*current).size() <= char_offset) {
  183. bin_sizes[0]++;
  184. }
  185. else
  186. bin_sizes[static_cast<Unsigned_char_type>((*current)[char_offset])
  187. + 1]++;
  188. }
  189. //Assign the bin positions
  190. bin_cache[cache_offset] = first;
  191. for (unsigned u = 0; u < membin_count - 1; u++)
  192. bin_cache[cache_offset + u + 1] =
  193. bin_cache[cache_offset + u] + bin_sizes[u];
  194. //Swap into place
  195. RandomAccessIter next_bin_start = first;
  196. //handling empty bins
  197. RandomAccessIter * local_bin = &(bin_cache[cache_offset]);
  198. next_bin_start += bin_sizes[0];
  199. RandomAccessIter * target_bin;
  200. //Iterating over each element in the bin of empties
  201. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  202. ++current) {
  203. //empties belong in this bin
  204. while ((*current).size() > char_offset) {
  205. target_bin =
  206. bins + static_cast<Unsigned_char_type>((*current)[char_offset]);
  207. iter_swap(current, (*target_bin)++);
  208. }
  209. }
  210. *local_bin = next_bin_start;
  211. //iterate backwards to find the last bin with elements in it
  212. //this saves iterations in multiple loops
  213. unsigned last_bin = bin_count - 1;
  214. for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin);
  215. //This dominates runtime, mostly in the swap and bin lookups
  216. for (unsigned u = 0; u < last_bin; ++u) {
  217. local_bin = bins + u;
  218. next_bin_start += bin_sizes[u + 1];
  219. //Iterating over each element in this bin
  220. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  221. ++current) {
  222. //Swapping into place until the correct element has been swapped in
  223. for (target_bin = bins + static_cast<Unsigned_char_type>
  224. ((*current)[char_offset]); target_bin != local_bin;
  225. target_bin = bins + static_cast<Unsigned_char_type>
  226. ((*current)[char_offset])) iter_swap(current, (*target_bin)++);
  227. }
  228. *local_bin = next_bin_start;
  229. }
  230. bins[last_bin] = last;
  231. //Recursing
  232. RandomAccessIter lastPos = bin_cache[cache_offset];
  233. //Skip this loop for empties
  234. for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2;
  235. lastPos = bin_cache[u], ++u) {
  236. size_t count = bin_cache[u] - lastPos;
  237. //don't sort unless there are at least two items to Compare
  238. if (count < 2)
  239. continue;
  240. //using boost::sort::pdqsort if its worst-case is better
  241. if (count < max_size)
  242. boost::sort::pdqsort(lastPos, bin_cache[u],
  243. offset_less_than<Data_type, Unsigned_char_type>(char_offset + 1));
  244. else
  245. string_sort_rec<RandomAccessIter, Unsigned_char_type>(lastPos,
  246. bin_cache[u], char_offset + 1, bin_cache, cache_end, bin_sizes);
  247. }
  248. }
  249. //Sorts strings in reverse order, with empties at the end
  250. template <class RandomAccessIter, class Unsigned_char_type>
  251. inline void
  252. reverse_string_sort_rec(RandomAccessIter first, RandomAccessIter last,
  253. size_t char_offset,
  254. std::vector<RandomAccessIter> &bin_cache,
  255. unsigned cache_offset,
  256. size_t *bin_sizes)
  257. {
  258. typedef typename std::iterator_traits<RandomAccessIter>::value_type
  259. Data_type;
  260. //This section makes handling of long identical substrings much faster
  261. //with a mild average performance impact.
  262. RandomAccessIter curr = first;
  263. //Iterate to the end of the empties. If all empty, return
  264. while ((*curr).size() <= char_offset) {
  265. if (++curr == last)
  266. return;
  267. }
  268. //Getting the last non-empty
  269. while ((*(--last)).size() <= char_offset);
  270. ++last;
  271. //Offsetting on identical characters. This section works
  272. //a few characters at a time for optimal worst-case performance.
  273. update_offset<RandomAccessIter, Unsigned_char_type>(curr, last,
  274. char_offset);
  275. RandomAccessIter * target_bin;
  276. const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
  277. //Equal worst-case of radix and comparison when bin_count = n*log(n).
  278. const unsigned max_size = bin_count;
  279. const unsigned membin_count = bin_count + 1;
  280. const unsigned max_bin = bin_count - 1;
  281. unsigned cache_end;
  282. RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
  283. cache_end, membin_count);
  284. RandomAccessIter * end_bin = &(bin_cache[cache_offset + max_bin]);
  285. //Calculating the size of each bin; this takes roughly 10% of runtime
  286. for (RandomAccessIter current = first; current != last; ++current) {
  287. if ((*current).size() <= char_offset) {
  288. bin_sizes[bin_count]++;
  289. }
  290. else
  291. bin_sizes[max_bin - static_cast<Unsigned_char_type>
  292. ((*current)[char_offset])]++;
  293. }
  294. //Assign the bin positions
  295. bin_cache[cache_offset] = first;
  296. for (unsigned u = 0; u < membin_count - 1; u++)
  297. bin_cache[cache_offset + u + 1] =
  298. bin_cache[cache_offset + u] + bin_sizes[u];
  299. //Swap into place
  300. RandomAccessIter next_bin_start = last;
  301. //handling empty bins
  302. RandomAccessIter * local_bin = &(bin_cache[cache_offset + bin_count]);
  303. RandomAccessIter lastFull = *local_bin;
  304. //Iterating over each element in the bin of empties
  305. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  306. ++current) {
  307. //empties belong in this bin
  308. while ((*current).size() > char_offset) {
  309. target_bin =
  310. end_bin - static_cast<Unsigned_char_type>((*current)[char_offset]);
  311. iter_swap(current, (*target_bin)++);
  312. }
  313. }
  314. *local_bin = next_bin_start;
  315. next_bin_start = first;
  316. //iterate backwards to find the last non-empty bin
  317. //this saves iterations in multiple loops
  318. unsigned last_bin = max_bin;
  319. for (; last_bin && !bin_sizes[last_bin]; --last_bin);
  320. //This dominates runtime, mostly in the swap and bin lookups
  321. for (unsigned u = 0; u < last_bin; ++u) {
  322. local_bin = bins + u;
  323. next_bin_start += bin_sizes[u];
  324. //Iterating over each element in this bin
  325. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  326. ++current) {
  327. //Swapping into place until the correct element has been swapped in
  328. for (target_bin =
  329. end_bin - static_cast<Unsigned_char_type>((*current)[char_offset]);
  330. target_bin != local_bin;
  331. target_bin =
  332. end_bin - static_cast<Unsigned_char_type>((*current)[char_offset]))
  333. iter_swap(current, (*target_bin)++);
  334. }
  335. *local_bin = next_bin_start;
  336. }
  337. bins[last_bin] = lastFull;
  338. //Recursing
  339. RandomAccessIter lastPos = first;
  340. //Skip this loop for empties
  341. for (unsigned u = cache_offset; u <= cache_offset + last_bin;
  342. lastPos = bin_cache[u], ++u) {
  343. size_t count = bin_cache[u] - lastPos;
  344. //don't sort unless there are at least two items to Compare
  345. if (count < 2)
  346. continue;
  347. //using boost::sort::pdqsort if its worst-case is better
  348. if (count < max_size)
  349. boost::sort::pdqsort(lastPos, bin_cache[u], offset_greater_than<Data_type,
  350. Unsigned_char_type>(char_offset + 1));
  351. else
  352. reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type>
  353. (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end, bin_sizes);
  354. }
  355. }
  356. //String sorting recursive implementation
  357. template <class RandomAccessIter, class Unsigned_char_type, class Get_char,
  358. class Get_length>
  359. inline void
  360. string_sort_rec(RandomAccessIter first, RandomAccessIter last,
  361. size_t char_offset, std::vector<RandomAccessIter> &bin_cache,
  362. unsigned cache_offset, size_t *bin_sizes,
  363. Get_char get_character, Get_length length)
  364. {
  365. typedef typename std::iterator_traits<RandomAccessIter>::value_type
  366. Data_type;
  367. //This section makes handling of long identical substrings much faster
  368. //with a mild average performance impact.
  369. //Iterate to the end of the empties. If all empty, return
  370. while (length(*first) <= char_offset) {
  371. if (++first == last)
  372. return;
  373. }
  374. RandomAccessIter finish = last - 1;
  375. //Getting the last non-empty
  376. for (;length(*finish) <= char_offset; --finish);
  377. ++finish;
  378. update_offset(first, finish, char_offset, get_character, length);
  379. const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
  380. //Equal worst-case of radix and comparison is when bin_count = n*log(n).
  381. const unsigned max_size = bin_count;
  382. const unsigned membin_count = bin_count + 1;
  383. unsigned cache_end;
  384. RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
  385. cache_end, membin_count) + 1;
  386. //Calculating the size of each bin; this takes roughly 10% of runtime
  387. for (RandomAccessIter current = first; current != last; ++current) {
  388. if (length(*current) <= char_offset) {
  389. bin_sizes[0]++;
  390. }
  391. else
  392. bin_sizes[get_character((*current), char_offset) + 1]++;
  393. }
  394. //Assign the bin positions
  395. bin_cache[cache_offset] = first;
  396. for (unsigned u = 0; u < membin_count - 1; u++)
  397. bin_cache[cache_offset + u + 1] =
  398. bin_cache[cache_offset + u] + bin_sizes[u];
  399. //Swap into place
  400. RandomAccessIter next_bin_start = first;
  401. //handling empty bins
  402. RandomAccessIter * local_bin = &(bin_cache[cache_offset]);
  403. next_bin_start += bin_sizes[0];
  404. RandomAccessIter * target_bin;
  405. //Iterating over each element in the bin of empties
  406. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  407. ++current) {
  408. //empties belong in this bin
  409. while (length(*current) > char_offset) {
  410. target_bin = bins + get_character((*current), char_offset);
  411. iter_swap(current, (*target_bin)++);
  412. }
  413. }
  414. *local_bin = next_bin_start;
  415. //iterate backwards to find the last bin with elements in it
  416. //this saves iterations in multiple loops
  417. unsigned last_bin = bin_count - 1;
  418. for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin);
  419. //This dominates runtime, mostly in the swap and bin lookups
  420. for (unsigned ii = 0; ii < last_bin; ++ii) {
  421. local_bin = bins + ii;
  422. next_bin_start += bin_sizes[ii + 1];
  423. //Iterating over each element in this bin
  424. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  425. ++current) {
  426. //Swapping into place until the correct element has been swapped in
  427. for (target_bin = bins + get_character((*current), char_offset);
  428. target_bin != local_bin;
  429. target_bin = bins + get_character((*current), char_offset))
  430. iter_swap(current, (*target_bin)++);
  431. }
  432. *local_bin = next_bin_start;
  433. }
  434. bins[last_bin] = last;
  435. //Recursing
  436. RandomAccessIter lastPos = bin_cache[cache_offset];
  437. //Skip this loop for empties
  438. for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2;
  439. lastPos = bin_cache[u], ++u) {
  440. size_t count = bin_cache[u] - lastPos;
  441. //don't sort unless there are at least two items to Compare
  442. if (count < 2)
  443. continue;
  444. //using boost::sort::pdqsort if its worst-case is better
  445. if (count < max_size)
  446. boost::sort::pdqsort(lastPos, bin_cache[u], offset_char_less_than<Data_type,
  447. Get_char, Get_length>(char_offset + 1));
  448. else
  449. string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char,
  450. Get_length>(lastPos, bin_cache[u], char_offset + 1, bin_cache,
  451. cache_end, bin_sizes, get_character, length);
  452. }
  453. }
  454. //String sorting recursive implementation
  455. template <class RandomAccessIter, class Unsigned_char_type, class Get_char,
  456. class Get_length, class Compare>
  457. inline void
  458. string_sort_rec(RandomAccessIter first, RandomAccessIter last,
  459. size_t char_offset, std::vector<RandomAccessIter> &bin_cache,
  460. unsigned cache_offset, size_t *bin_sizes,
  461. Get_char get_character, Get_length length, Compare comp)
  462. {
  463. //This section makes handling of long identical substrings much faster
  464. //with a mild average performance impact.
  465. //Iterate to the end of the empties. If all empty, return
  466. while (length(*first) <= char_offset) {
  467. if (++first == last)
  468. return;
  469. }
  470. RandomAccessIter finish = last - 1;
  471. //Getting the last non-empty
  472. for (;length(*finish) <= char_offset; --finish);
  473. ++finish;
  474. update_offset(first, finish, char_offset, get_character, length);
  475. const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
  476. //Equal worst-case of radix and comparison is when bin_count = n*log(n).
  477. const unsigned max_size = bin_count;
  478. const unsigned membin_count = bin_count + 1;
  479. unsigned cache_end;
  480. RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
  481. cache_end, membin_count) + 1;
  482. //Calculating the size of each bin; this takes roughly 10% of runtime
  483. for (RandomAccessIter current = first; current != last; ++current) {
  484. if (length(*current) <= char_offset) {
  485. bin_sizes[0]++;
  486. }
  487. else
  488. bin_sizes[get_character((*current), char_offset) + 1]++;
  489. }
  490. //Assign the bin positions
  491. bin_cache[cache_offset] = first;
  492. for (unsigned u = 0; u < membin_count - 1; u++)
  493. bin_cache[cache_offset + u + 1] =
  494. bin_cache[cache_offset + u] + bin_sizes[u];
  495. //Swap into place
  496. RandomAccessIter next_bin_start = first;
  497. //handling empty bins
  498. RandomAccessIter * local_bin = &(bin_cache[cache_offset]);
  499. next_bin_start += bin_sizes[0];
  500. RandomAccessIter * target_bin;
  501. //Iterating over each element in the bin of empties
  502. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  503. ++current) {
  504. //empties belong in this bin
  505. while (length(*current) > char_offset) {
  506. target_bin = bins + get_character((*current), char_offset);
  507. iter_swap(current, (*target_bin)++);
  508. }
  509. }
  510. *local_bin = next_bin_start;
  511. //iterate backwards to find the last bin with elements in it
  512. //this saves iterations in multiple loops
  513. unsigned last_bin = bin_count - 1;
  514. for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin);
  515. //This dominates runtime, mostly in the swap and bin lookups
  516. for (unsigned u = 0; u < last_bin; ++u) {
  517. local_bin = bins + u;
  518. next_bin_start += bin_sizes[u + 1];
  519. //Iterating over each element in this bin
  520. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  521. ++current) {
  522. //Swapping into place until the correct element has been swapped in
  523. for (target_bin = bins + get_character((*current), char_offset);
  524. target_bin != local_bin;
  525. target_bin = bins + get_character((*current), char_offset))
  526. iter_swap(current, (*target_bin)++);
  527. }
  528. *local_bin = next_bin_start;
  529. }
  530. bins[last_bin] = last;
  531. //Recursing
  532. RandomAccessIter lastPos = bin_cache[cache_offset];
  533. //Skip this loop for empties
  534. for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2;
  535. lastPos = bin_cache[u], ++u) {
  536. size_t count = bin_cache[u] - lastPos;
  537. //don't sort unless there are at least two items to Compare
  538. if (count < 2)
  539. continue;
  540. //using boost::sort::pdqsort if its worst-case is better
  541. if (count < max_size)
  542. boost::sort::pdqsort(lastPos, bin_cache[u], comp);
  543. else
  544. string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char,
  545. Get_length, Compare>
  546. (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end,
  547. bin_sizes, get_character, length, comp);
  548. }
  549. }
  550. //Sorts strings in reverse order, with empties at the end
  551. template <class RandomAccessIter, class Unsigned_char_type, class Get_char,
  552. class Get_length, class Compare>
  553. inline void
  554. reverse_string_sort_rec(RandomAccessIter first, RandomAccessIter last,
  555. size_t char_offset, std::vector<RandomAccessIter> &bin_cache,
  556. unsigned cache_offset, size_t *bin_sizes,
  557. Get_char get_character, Get_length length, Compare comp)
  558. {
  559. //This section makes handling of long identical substrings much faster
  560. //with a mild average performance impact.
  561. RandomAccessIter curr = first;
  562. //Iterate to the end of the empties. If all empty, return
  563. while (length(*curr) <= char_offset) {
  564. if (++curr == last)
  565. return;
  566. }
  567. //Getting the last non-empty
  568. while (length(*(--last)) <= char_offset);
  569. ++last;
  570. //Offsetting on identical characters. This section works
  571. //a character at a time for optimal worst-case performance.
  572. update_offset(curr, last, char_offset, get_character, length);
  573. const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
  574. //Equal worst-case of radix and comparison is when bin_count = n*log(n).
  575. const unsigned max_size = bin_count;
  576. const unsigned membin_count = bin_count + 1;
  577. const unsigned max_bin = bin_count - 1;
  578. unsigned cache_end;
  579. RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
  580. cache_end, membin_count);
  581. RandomAccessIter *end_bin = &(bin_cache[cache_offset + max_bin]);
  582. //Calculating the size of each bin; this takes roughly 10% of runtime
  583. for (RandomAccessIter current = first; current != last; ++current) {
  584. if (length(*current) <= char_offset) {
  585. bin_sizes[bin_count]++;
  586. }
  587. else
  588. bin_sizes[max_bin - get_character((*current), char_offset)]++;
  589. }
  590. //Assign the bin positions
  591. bin_cache[cache_offset] = first;
  592. for (unsigned u = 0; u < membin_count - 1; u++)
  593. bin_cache[cache_offset + u + 1] =
  594. bin_cache[cache_offset + u] + bin_sizes[u];
  595. //Swap into place
  596. RandomAccessIter next_bin_start = last;
  597. //handling empty bins
  598. RandomAccessIter * local_bin = &(bin_cache[cache_offset + bin_count]);
  599. RandomAccessIter lastFull = *local_bin;
  600. RandomAccessIter * target_bin;
  601. //Iterating over each element in the bin of empties
  602. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  603. ++current) {
  604. //empties belong in this bin
  605. while (length(*current) > char_offset) {
  606. target_bin = end_bin - get_character((*current), char_offset);
  607. iter_swap(current, (*target_bin)++);
  608. }
  609. }
  610. *local_bin = next_bin_start;
  611. next_bin_start = first;
  612. //iterate backwards to find the last bin with elements in it
  613. //this saves iterations in multiple loops
  614. unsigned last_bin = max_bin;
  615. for (; last_bin && !bin_sizes[last_bin]; --last_bin);
  616. //This dominates runtime, mostly in the swap and bin lookups
  617. for (unsigned u = 0; u < last_bin; ++u) {
  618. local_bin = bins + u;
  619. next_bin_start += bin_sizes[u];
  620. //Iterating over each element in this bin
  621. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  622. ++current) {
  623. //Swapping into place until the correct element has been swapped in
  624. for (target_bin = end_bin - get_character((*current), char_offset);
  625. target_bin != local_bin;
  626. target_bin = end_bin - get_character((*current), char_offset))
  627. iter_swap(current, (*target_bin)++);
  628. }
  629. *local_bin = next_bin_start;
  630. }
  631. bins[last_bin] = lastFull;
  632. //Recursing
  633. RandomAccessIter lastPos = first;
  634. //Skip this loop for empties
  635. for (unsigned u = cache_offset; u <= cache_offset + last_bin;
  636. lastPos = bin_cache[u], ++u) {
  637. size_t count = bin_cache[u] - lastPos;
  638. //don't sort unless there are at least two items to Compare
  639. if (count < 2)
  640. continue;
  641. //using boost::sort::pdqsort if its worst-case is better
  642. if (count < max_size)
  643. boost::sort::pdqsort(lastPos, bin_cache[u], comp);
  644. else
  645. reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type,
  646. Get_char, Get_length, Compare>
  647. (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end,
  648. bin_sizes, get_character, length, comp);
  649. }
  650. }
  651. //Holds the bin vector and makes the initial recursive call
  652. template <class RandomAccessIter, class Unsigned_char_type>
  653. inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
  654. >::type
  655. string_sort(RandomAccessIter first, RandomAccessIter last,
  656. Unsigned_char_type)
  657. {
  658. size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
  659. std::vector<RandomAccessIter> bin_cache;
  660. string_sort_rec<RandomAccessIter, Unsigned_char_type>
  661. (first, last, 0, bin_cache, 0, bin_sizes);
  662. }
  663. template <class RandomAccessIter, class Unsigned_char_type>
  664. inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void
  665. >::type
  666. string_sort(RandomAccessIter first, RandomAccessIter last,
  667. Unsigned_char_type)
  668. {
  669. //Warning that we're using boost::sort::pdqsort, even though string_sort was called
  670. BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
  671. boost::sort::pdqsort(first, last);
  672. }
  673. //Holds the bin vector and makes the initial recursive call
  674. template <class RandomAccessIter, class Unsigned_char_type>
  675. inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
  676. >::type
  677. reverse_string_sort(RandomAccessIter first, RandomAccessIter last,
  678. Unsigned_char_type)
  679. {
  680. size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
  681. std::vector<RandomAccessIter> bin_cache;
  682. reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type>
  683. (first, last, 0, bin_cache, 0, bin_sizes);
  684. }
  685. template <class RandomAccessIter, class Unsigned_char_type>
  686. inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void
  687. >::type
  688. reverse_string_sort(RandomAccessIter first, RandomAccessIter last,
  689. Unsigned_char_type)
  690. {
  691. typedef typename std::iterator_traits<RandomAccessIter>::value_type
  692. Data_type;
  693. //Warning that we're using boost::sort::pdqsort, even though string_sort was called
  694. BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
  695. boost::sort::pdqsort(first, last, std::greater<Data_type>());
  696. }
  697. //Holds the bin vector and makes the initial recursive call
  698. template <class RandomAccessIter, class Get_char, class Get_length,
  699. class Unsigned_char_type>
  700. inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
  701. >::type
  702. string_sort(RandomAccessIter first, RandomAccessIter last,
  703. Get_char get_character, Get_length length, Unsigned_char_type)
  704. {
  705. size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
  706. std::vector<RandomAccessIter> bin_cache;
  707. string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char,
  708. Get_length>(first, last, 0, bin_cache, 0, bin_sizes, get_character, length);
  709. }
  710. template <class RandomAccessIter, class Get_char, class Get_length,
  711. class Unsigned_char_type>
  712. inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void
  713. >::type
  714. string_sort(RandomAccessIter first, RandomAccessIter last,
  715. Get_char get_character, Get_length length, Unsigned_char_type)
  716. {
  717. //Warning that we're using boost::sort::pdqsort, even though string_sort was called
  718. BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
  719. boost::sort::pdqsort(first, last);
  720. }
  721. //Holds the bin vector and makes the initial recursive call
  722. template <class RandomAccessIter, class Get_char, class Get_length,
  723. class Compare, class Unsigned_char_type>
  724. inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
  725. >::type
  726. string_sort(RandomAccessIter first, RandomAccessIter last,
  727. Get_char get_character, Get_length length, Compare comp, Unsigned_char_type)
  728. {
  729. size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
  730. std::vector<RandomAccessIter> bin_cache;
  731. string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char
  732. , Get_length, Compare>
  733. (first, last, 0, bin_cache, 0, bin_sizes, get_character, length, comp);
  734. }
  735. //disable_if_c was refusing to compile, so rewrote to use enable_if_c
  736. template <class RandomAccessIter, class Get_char, class Get_length,
  737. class Compare, class Unsigned_char_type>
  738. inline typename boost::enable_if_c< (sizeof(Unsigned_char_type) > 2), void
  739. >::type
  740. string_sort(RandomAccessIter first, RandomAccessIter last,
  741. Get_char get_character, Get_length length, Compare comp, Unsigned_char_type)
  742. {
  743. //Warning that we're using boost::sort::pdqsort, even though string_sort was called
  744. BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
  745. boost::sort::pdqsort(first, last, comp);
  746. }
  747. //Holds the bin vector and makes the initial recursive call
  748. template <class RandomAccessIter, class Get_char, class Get_length,
  749. class Compare, class Unsigned_char_type>
  750. inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
  751. >::type
  752. reverse_string_sort(RandomAccessIter first, RandomAccessIter last,
  753. Get_char get_character, Get_length length, Compare comp, Unsigned_char_type)
  754. {
  755. size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
  756. std::vector<RandomAccessIter> bin_cache;
  757. reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char,
  758. Get_length, Compare>
  759. (first, last, 0, bin_cache, 0, bin_sizes, get_character, length, comp);
  760. }
  761. template <class RandomAccessIter, class Get_char, class Get_length,
  762. class Compare, class Unsigned_char_type>
  763. inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void
  764. >::type
  765. reverse_string_sort(RandomAccessIter first, RandomAccessIter last,
  766. Get_char get_character, Get_length length, Compare comp, Unsigned_char_type)
  767. {
  768. //Warning that we're using boost::sort::pdqsort, even though string_sort was called
  769. BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
  770. boost::sort::pdqsort(first, last, comp);
  771. }
  772. }
  773. }
  774. }
  775. }
  776. #endif