├── LICENSE.md ├── README.md ├── plf_indiesort.h └── plf_indiesort_test_suite.cpp /LICENSE.md: -------------------------------------------------------------------------------- 1 | zlib License 2 | 3 | (C) 2019 mattreecebentley 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plf_indiesort 2 | A sort wrapper enabling use of random-access (eg. std::sort) sorting on non-random access containers, and increased performance for the sorting of large types in random-access containers. 3 | 4 | It has a temporary memory cost of N * (sizeof(pointer) + sizeof(size_t)) for sorting non-random-access iterators/containers,
5 | and a N * sizeof(U) cost for random-access iterators/containers, where U = the smallest unsigned integer able to store N. For example if the size of the range being sorted is <= 255, U will be unsigned char. 6 | 7 | Indiesort should be used when: 8 | 9 | * The temporary memory cost mentioned is non-problematic, 10 | * The container or iterators are not random_access and therefore std::sort cannot be used, and/or 11 | * The element type is large or non-trivially-movable/copyable. 12 | 13 | 14 | It is, on average across all numbers of sorted elements: 15 | 16 | * +146% faster than std::sort when used on vectors or arrays of large structs (496 bytes). Crossover point for increased performance over std::sort is any type larger than 152 bytes. 17 | * +28% faster than std::list's internal sort, on types smaller than 272 bytes. 18 | 19 | std::list's internal sort is faster for types larger than 272 bytes (as it only writes previous and next pointers) and std::sort is faster on vectors and arrays for types smaller than 152 bytes. 20 | C++98/03/11/14/etc-compatible. 21 | -------------------------------------------------------------------------------- /plf_indiesort.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Matthew Bentley (mattreecebentley@gmail.com) www.plflib.org 2 | 3 | // zLib license (https://www.zlib.net/zlib_license.html): 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // 8 | // Permission is granted to anyone to use this software for any purpose, 9 | // including commercial applications, and to alter it and redistribute it 10 | // freely, subject to the following restrictions: 11 | // 12 | // 1. The origin of this software must not be misrepresented; you must not 13 | // claim that you wrote the original software. If you use this software 14 | // in a product, an acknowledgement in the product documentation would be 15 | // appreciated but is not required. 16 | // 2. Altered source versions must be plainly marked as such, and must not be 17 | // misrepresented as being the original software. 18 | // 3. This notice may not be removed or altered from any source distribution. 19 | 20 | #ifndef PLF_INDIESORT_H 21 | #define PLF_INDIESORT_H 22 | 23 | 24 | // Compiler-specific defines used by indiesort: 25 | 26 | // Define default cases before possibly redefining: 27 | #define PLF_CONSTFUNC 28 | #define PLF_NOEXCEPT throw() 29 | #define PLF_CONSTEXPR 30 | 31 | #if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) 32 | // Suppress incorrect (unfixed MSVC bug) warnings re: constant expressions in constexpr-if statements 33 | #pragma warning ( push ) 34 | #pragma warning ( disable : 4127 ) 35 | 36 | #if _MSC_VER >= 1600 37 | #define PLF_DECLTYPE_SUPPORT 38 | #define PLF_MOVE_SEMANTICS_SUPPORT 39 | #endif 40 | #if _MSC_VER >= 1700 41 | #define PLF_TYPE_TRAITS_SUPPORT 42 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 43 | #endif 44 | #if _MSC_VER >= 1800 45 | #define PLF_VARIADICS_SUPPORT // Variadics, in this context, means both variadic templates and variadic macros are supported 46 | #endif 47 | #if _MSC_VER >= 1900 48 | #undef PLF_NOEXCEPT 49 | #define PLF_NOEXCEPT noexcept 50 | #endif 51 | 52 | #if defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L) 53 | #undef PLF_CONSTEXPR 54 | #define PLF_CONSTEXPR constexpr 55 | #endif 56 | 57 | #if defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L) && _MSC_VER >= 1929 58 | #undef PLF_CONSTFUNC 59 | #define PLF_CONSTFUNC constexpr 60 | #endif 61 | 62 | #elif defined(__cplusplus) && __cplusplus >= 201103L // C++11 support, at least 63 | #define PLF_MOVE_SEMANTICS_SUPPORT 64 | 65 | #if defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__clang__) // If compiler is GCC/G++ 66 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4 // 4.2 and below do not support variadic templates or decltype 67 | #define PLF_VARIADICS_SUPPORT 68 | #define PLF_DECLTYPE_SUPPORT 69 | #endif 70 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 71 | #undef PLF_NOEXCEPT 72 | #define PLF_NOEXCEPT noexcept 73 | #endif 74 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4 75 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 76 | #endif 77 | #if __GNUC__ >= 5 // GCC v4.9 and below do not support std::is_trivially_copyable 78 | #define PLF_TYPE_TRAITS_SUPPORT 79 | #endif 80 | #elif defined(__clang__) && !defined(__GLIBCXX__) && !defined(_LIBCPP_CXX03_LANG) 81 | #if __clang_major__ >= 3 // clang versions < 3 don't support __has_feature() or traits 82 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 83 | #define PLF_TYPE_TRAITS_SUPPORT 84 | 85 | #if __has_feature(cxx_decltype) 86 | #define PLF_DECLTYPE_SUPPORT 87 | #endif 88 | #if __has_feature(cxx_noexcept) 89 | #undef PLF_NOEXCEPT 90 | #define PLF_NOEXCEPT noexcept 91 | #endif 92 | #if __has_feature(cxx_rvalue_references) && !defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) 93 | #define PLF_MOVE_SEMANTICS_SUPPORT 94 | #endif 95 | #if __has_feature(cxx_variadic_templates) && !defined(_LIBCPP_HAS_NO_VARIADICS) 96 | #define PLF_VARIADICS_SUPPORT 97 | #endif 98 | #endif 99 | #elif defined(__GLIBCXX__) // Using another compiler type with libstdc++ - we are assuming full c++11 compliance for compiler - which may not be true 100 | #define PLF_DECLTYPE_SUPPORT 101 | 102 | #if __GLIBCXX__ >= 20080606 // libstdc++ 4.2 and below do not support variadic templates 103 | #define PLF_VARIADICS_SUPPORT 104 | #endif 105 | #if __GLIBCXX__ >= 20120322 106 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 107 | #undef PLF_NOEXCEPT 108 | #define PLF_NOEXCEPT noexcept 109 | #endif 110 | #if __GLIBCXX__ >= 20150422 // libstdc++ v4.9 and below do not support std::is_trivially_copyable 111 | #define PLF_TYPE_TRAITS_SUPPORT 112 | #endif 113 | #elif defined(_LIBCPP_CXX03_LANG) // Special case for checking C++11 support with libcpp 114 | #if !defined(_LIBCPP_HAS_NO_VARIADICS) 115 | #define PLF_VARIADICS_SUPPORT 116 | #endif 117 | #else // Assume full support for other compilers and standard libraries 118 | #define PLF_DECLTYPE_SUPPORT 119 | #define PLF_INITIALIZER_LIST_SUPPORT 120 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 121 | #define PLF_VARIADICS_SUPPORT 122 | #define PLF_TYPE_TRAITS_SUPPORT 123 | #undef PLF_NOEXCEPT 124 | #define PLF_NOEXCEPT noexcept 125 | #endif 126 | 127 | #if __cplusplus >= 201703L && ((defined(__clang__) && ((__clang_major__ == 3 && __clang_minor__ == 9) || __clang_major__ > 3)) || (defined(__GNUC__) && __GNUC__ >= 7) || (!defined(__clang__) && !defined(__GNUC__))) 128 | #undef PLF_CONSTEXPR 129 | #define PLF_CONSTEXPR constexpr 130 | #endif 131 | 132 | #if __cplusplus > 201704L && ((((defined(__clang__) && !defined(__APPLE_CC__) && __clang_major__ >= 14) || (defined(__GNUC__) && (__GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ > 0)))) && ((defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 14) || (defined(__GLIBCXX__) && __GLIBCXX__ >= 201806L))) || (!defined(__clang__) && !defined(__GNUC__))) 133 | #define PLF_CPP20_SUPPORT 134 | #undef PLF_CONSTFUNC 135 | #define PLF_CONSTFUNC constexpr 136 | #endif 137 | #endif 138 | 139 | 140 | 141 | 142 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT 143 | #ifdef PLF_VARIADICS_SUPPORT 144 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, ...) std::allocator_traits::construct(allocator_instance, location, __VA_ARGS__) 145 | #else 146 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, data) std::allocator_traits::construct(allocator_instance, location, data) 147 | #endif 148 | 149 | #define PLF_DESTROY(the_allocator, allocator_instance, location) std::allocator_traits::destroy(allocator_instance, location) 150 | #define PLF_ALLOCATE(the_allocator, allocator_instance, size, hint) std::allocator_traits::allocate(allocator_instance, size, hint) 151 | #define PLF_DEALLOCATE(the_allocator, allocator_instance, location, size) std::allocator_traits::deallocate(allocator_instance, location, size) 152 | #else 153 | #ifdef PLF_VARIADICS_SUPPORT 154 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, ...) (allocator_instance).construct(location, __VA_ARGS__) 155 | #else 156 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, data) (allocator_instance).construct(location, data) 157 | #endif 158 | 159 | #define PLF_DESTROY(the_allocator, allocator_instance, location) (allocator_instance).destroy(location) 160 | #define PLF_ALLOCATE(the_allocator, allocator_instance, size, hint) (allocator_instance).allocate(size, hint) 161 | #define PLF_DEALLOCATE(the_allocator, allocator_instance, location, size) (allocator_instance).deallocate(location, size) 162 | #endif 163 | 164 | 165 | 166 | #ifndef PLF_SORT_FUNCTION 167 | #include // std::sort 168 | #define PLF_SORT_FUNCTION std::sort 169 | #define PLF_SORT_FUNCTION_DEFINED 170 | #endif 171 | 172 | #include // assert 173 | #include // std::size_t 174 | #include // std::allocator, std::to_address 175 | #include // std::numeric_limits 176 | #include // std::iterator_traits, std::move_iterator, std::distance 177 | 178 | 179 | #ifdef PLF_TYPE_TRAITS_SUPPORT 180 | #include // is_same 181 | #endif 182 | 183 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 184 | #include // std::move 185 | #endif 186 | 187 | 188 | 189 | namespace plf 190 | { 191 | // std:: tool replacements for C++03/98/11 support: 192 | 193 | #ifndef PLF_TOOLS 194 | #define PLF_TOOLS 195 | 196 | template 197 | struct enable_if 198 | { 199 | typedef T type; 200 | }; 201 | 202 | template 203 | struct enable_if 204 | {}; 205 | 206 | 207 | 208 | template struct conditional; 209 | 210 | template struct conditional 211 | { 212 | typedef is_true type; 213 | }; 214 | 215 | template struct conditional 216 | { 217 | typedef is_false type; 218 | }; 219 | 220 | 221 | 222 | template 223 | struct less 224 | { 225 | bool operator() (const element_type &a, const element_type &b) const PLF_NOEXCEPT 226 | { 227 | return a < b; 228 | } 229 | }; 230 | 231 | 232 | 233 | template 234 | struct equal_to 235 | { 236 | const element_type value; 237 | 238 | explicit equal_to(const element_type store_value): // no noexcept as element may allocate and potentially throw when copied 239 | value(store_value) 240 | {} 241 | 242 | bool operator() (const element_type compare_value) const PLF_NOEXCEPT 243 | { 244 | return value == compare_value; 245 | } 246 | }; 247 | 248 | 249 | 250 | // To enable conversion to void * when allocator supplies non-raw pointers: 251 | template 252 | static PLF_CONSTFUNC void * void_cast(const source_pointer_type source_pointer) PLF_NOEXCEPT 253 | { 254 | #if defined(PLF_CPP20_SUPPORT) 255 | return static_cast(std::to_address(source_pointer)); 256 | #else 257 | return static_cast(&*source_pointer); 258 | #endif 259 | } 260 | 261 | 262 | 263 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 264 | template 265 | PLF_CONSTFUNC std::move_iterator make_move_iterator(iterator_type it) 266 | { 267 | return std::move_iterator(std::move(it)); 268 | } 269 | #endif 270 | 271 | 272 | 273 | enum priority { performance = 1, memory_use = 4}; 274 | #endif 275 | 276 | 277 | template 278 | struct is_pointer 279 | { 280 | static const bool value = false; 281 | }; 282 | 283 | template 284 | struct is_pointer 285 | { 286 | static const bool value = true; 287 | }; 288 | 289 | 290 | 291 | template< class T > struct remove_pointer {typedef T type;}; 292 | template< class T > struct remove_pointer {typedef T type;}; 293 | template< class T > struct remove_pointer {typedef T type;}; 294 | template< class T > struct remove_pointer {typedef T type;}; 295 | template< class T > struct remove_pointer {typedef T type;}; 296 | 297 | 298 | 299 | template struct derive_type; 300 | 301 | template struct derive_type 302 | { 303 | typedef typename remove_pointer::type type; 304 | }; 305 | 306 | template struct derive_type 307 | { 308 | typedef typename the_type::value_type type; 309 | }; 310 | 311 | 312 | 313 | // Function-object, used to redirect the sort function to compare element pointers by the elements they point to, and sort the element pointers instead of the elements: 314 | 315 | template 316 | struct random_access_sort_dereferencer 317 | { 318 | comparison_function stored_instance; 319 | const iterator_type stored_first_iterator; 320 | 321 | PLF_CONSTFUNC explicit random_access_sort_dereferencer(const comparison_function &function_instance, const iterator_type first): 322 | stored_instance(function_instance), 323 | stored_first_iterator(first) 324 | {} 325 | 326 | PLF_CONSTFUNC bool operator() (const size_type index1, const size_type index2) 327 | { 328 | return stored_instance(*(stored_first_iterator + index1), *(stored_first_iterator + index2)); 329 | } 330 | }; 331 | 332 | 333 | 334 | template 335 | PLF_CONSTFUNC void random_access_sort(const iterator_type first, comparison_function compare, const size_type size) 336 | { 337 | typedef typename derive_type::value, iterator_type>::type element_type; 338 | typedef typename std::allocator size_type_allocator_type; 339 | 340 | size_type_allocator_type size_type_allocator; 341 | size_type * const sort_array = PLF_ALLOCATE(size_type_allocator_type, size_type_allocator, size, NULL); 342 | size_type *size_type_pointer = sort_array; 343 | 344 | // Construct pointers to all elements in the sequence: 345 | for (size_type index = 0; index != size; ++index, ++size_type_pointer) 346 | { 347 | PLF_CONSTRUCT(size_type_allocator_type, size_type_allocator, size_type_pointer, index); 348 | } 349 | 350 | // Now, sort the pointers by the values they point to (std::sort is default sort function if the macro below is not defined prior to header inclusion): 351 | PLF_SORT_FUNCTION(sort_array, size_type_pointer, random_access_sort_dereferencer(compare, first)); 352 | 353 | // Sort the actual elements via the tuple array: 354 | size_type index = 0; 355 | 356 | for (size_type *current_index = sort_array; current_index != size_type_pointer; ++current_index, ++index) 357 | { 358 | if (*current_index != index) 359 | { 360 | size_type destination_index = index; 361 | 362 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 363 | element_type end_value = std::move(*(first + destination_index)); 364 | #else 365 | element_type end_value = *(first + destination_index); 366 | #endif 367 | 368 | size_type source_index = *current_index; 369 | 370 | do 371 | { 372 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 373 | *(first + destination_index) = std::move(*(first + source_index)); 374 | #else 375 | *(first + destination_index) = *(first + source_index); 376 | #endif 377 | 378 | destination_index = source_index; 379 | source_index = sort_array[destination_index]; 380 | sort_array[destination_index] = destination_index; 381 | } while (source_index != index); 382 | 383 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 384 | *(first + destination_index) = std::move(end_value); 385 | #else 386 | *(first + destination_index) = end_value; 387 | #endif 388 | } 389 | } 390 | 391 | PLF_DEALLOCATE(size_type_allocator_type, size_type_allocator, sort_array, size); 392 | } 393 | 394 | 395 | 396 | template 397 | PLF_CONSTFUNC void call_random_access_sort(const iterator_type first, const iterator_type last, comparison_function compare) 398 | { 399 | assert(first <= last); 400 | typedef typename derive_type::value, iterator_type>::type element_type; 401 | 402 | #ifdef PLF_TYPE_TRAITS_SUPPORT 403 | if PLF_CONSTEXPR ((std::is_trivially_copyable::value || std::is_move_assignable::value) && sizeof(element_type) < 152) // ie. below benchmarked threshold for improved performance over direct std::sort 404 | #else 405 | if PLF_CONSTEXPR (sizeof(element_type) < 152) 406 | #endif 407 | { 408 | PLF_SORT_FUNCTION(first, last, compare); 409 | return; 410 | } 411 | 412 | 413 | const std::size_t size = static_cast(last - first); 414 | 415 | if (size < 2) 416 | { 417 | return; 418 | } 419 | else if (size <= std::numeric_limits::max()) 420 | { 421 | random_access_sort(first, compare, static_cast(size)); 422 | } 423 | else if (size <= std::numeric_limits::max()) 424 | { 425 | random_access_sort(first, compare, static_cast(size)); 426 | } 427 | else if (size <= std::numeric_limits::max()) 428 | { 429 | random_access_sort(first, compare, static_cast(size)); 430 | } 431 | else 432 | { 433 | random_access_sort(first, compare, size); 434 | } 435 | } 436 | 437 | 438 | 439 | template 440 | struct sort_dereferencer 441 | { 442 | comparison_function stored_instance; 443 | 444 | PLF_CONSTFUNC explicit sort_dereferencer(const comparison_function &function_instance): 445 | stored_instance(function_instance) 446 | {} 447 | 448 | PLF_CONSTFUNC bool operator() (const element_type first, const element_type second) 449 | { 450 | return stored_instance(*(first.original_location), *(second.original_location)); 451 | } 452 | }; 453 | 454 | 455 | 456 | // This struct must be non-local to the function below for C++03 compatibility: 457 | 458 | template 459 | struct pointer_index_tuple 460 | { 461 | pointer original_location; 462 | size_type original_index; 463 | 464 | PLF_CONSTFUNC pointer_index_tuple(const pointer _item, const size_type _index) PLF_NOEXCEPT: 465 | original_location(_item), 466 | original_index(_index) 467 | {} 468 | }; 469 | 470 | 471 | 472 | template 473 | PLF_CONSTFUNC void non_random_access_sort(const iterator_type first, const iterator_type last, comparison_function compare, const std::size_t size) 474 | { 475 | if (size < 2) 476 | { 477 | return; 478 | } 479 | 480 | typedef typename derive_type::value, iterator_type>::type element_type; 481 | 482 | #ifdef PLF_TYPE_TRAITS_SUPPORT 483 | if PLF_CONSTEXPR ((std::is_trivially_copyable::value || std::is_move_assignable::value) && sizeof(element_type) <= sizeof(element_type *) * 2) // If element is <= 2 pointers, just copy to an array and sort that then copy back - consumes less memory and may be faster 484 | #else 485 | if PLF_CONSTEXPR (sizeof(element_type) <= sizeof(element_type *) * 2) 486 | #endif 487 | { 488 | typedef typename std::allocator allocator_type; 489 | allocator_type alloc; 490 | element_type * const sort_array = PLF_ALLOCATE(allocator_type, alloc, size, NULL), * const end = sort_array + size; 491 | 492 | #ifdef PLF_TYPE_TRAITS_SUPPORT 493 | if PLF_CONSTEXPR (!std::is_trivially_copyable::value && std::is_move_assignable::value) 494 | { 495 | std::uninitialized_copy(plf::make_move_iterator(first), plf::make_move_iterator(last), sort_array); 496 | } 497 | else 498 | #endif 499 | { 500 | std::uninitialized_copy(first, last, sort_array); 501 | } 502 | 503 | PLF_SORT_FUNCTION(sort_array, end, compare); 504 | 505 | #ifdef PLF_TYPE_TRAITS_SUPPORT 506 | if PLF_CONSTEXPR (!std::is_trivially_copyable::value && std::is_move_assignable::value) 507 | { 508 | std::copy(plf::make_move_iterator(sort_array), plf::make_move_iterator(end), first); 509 | } 510 | else 511 | #endif 512 | { 513 | std::copy(sort_array, end, first); 514 | 515 | #ifdef PLF_TYPE_TRAITS_SUPPORT 516 | if (!std::is_trivially_destructible::value) 517 | #endif 518 | { 519 | for (element_type *current = sort_array; current != end; ++current) 520 | { 521 | PLF_DESTROY(allocator_type, alloc, current); 522 | } 523 | } 524 | } 525 | 526 | PLF_DEALLOCATE(allocator_type, alloc, sort_array, size); 527 | return; 528 | } 529 | else 530 | { 531 | typedef typename std::size_t size_type; 532 | typedef pointer_index_tuple item_index_tuple; 533 | 534 | typedef typename std::allocator tuple_allocator_type; 535 | tuple_allocator_type tuple_allocator; 536 | 537 | item_index_tuple * const sort_array = PLF_ALLOCATE(tuple_allocator_type, tuple_allocator, size, NULL); 538 | item_index_tuple *tuple_pointer = sort_array; 539 | 540 | // Construct pointers to all elements in the sequence: 541 | size_type index = 0; 542 | 543 | for (iterator_type current_element = first; current_element != last; ++current_element, ++tuple_pointer, ++index) 544 | { 545 | #ifdef PLF_VARIADICS_SUPPORT 546 | PLF_CONSTRUCT(tuple_allocator_type, tuple_allocator, tuple_pointer, &*current_element, index); 547 | #else 548 | PLF_CONSTRUCT(tuple_allocator_type, tuple_allocator, tuple_pointer, item_index_tuple(&*current_element, index)); 549 | #endif 550 | } 551 | 552 | PLF_SORT_FUNCTION(sort_array, sort_array + size, sort_dereferencer(compare)); 553 | 554 | // Sort the actual elements via the tuple array: 555 | index = 0; 556 | 557 | for (item_index_tuple *current_tuple = sort_array; current_tuple != tuple_pointer; ++current_tuple, ++index) 558 | { 559 | if (current_tuple->original_index != index) 560 | { 561 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 562 | element_type end_value = std::move(*(current_tuple->original_location)); 563 | #else 564 | element_type end_value = *(current_tuple->original_location); 565 | #endif 566 | 567 | size_type destination_index = index; 568 | size_type source_index = current_tuple->original_index; 569 | 570 | do 571 | { 572 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 573 | *(sort_array[destination_index].original_location) = std::move(*(sort_array[source_index].original_location)); 574 | #else 575 | *(sort_array[destination_index].original_location) = *(sort_array[source_index].original_location); 576 | #endif 577 | 578 | destination_index = source_index; 579 | source_index = sort_array[destination_index].original_index; 580 | sort_array[destination_index].original_index = destination_index; 581 | } while (source_index != index); 582 | 583 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 584 | *(sort_array[destination_index].original_location) = std::move(end_value); 585 | #else 586 | *(sort_array[destination_index].original_location) = end_value; 587 | #endif 588 | } 589 | } 590 | 591 | PLF_DEALLOCATE(tuple_allocator_type, tuple_allocator, sort_array, size); 592 | } 593 | } 594 | 595 | 596 | 597 | // Range templates: 598 | template 599 | PLF_CONSTFUNC void indiesort(const iterator_type first, const iterator_type last, comparison_function compare, const std::size_t size) 600 | { 601 | non_random_access_sort(first, last, compare, size); 602 | } 603 | 604 | 605 | 606 | template 607 | #ifdef PLF_TYPE_TRAITS_SUPPORT 608 | PLF_CONSTFUNC void indiesort(const typename plf::enable_if::value || std::is_same::iterator_category, std::random_access_iterator_tag>::value), iterator_type>::type first, const iterator_type last, comparison_function compare) 609 | #else 610 | PLF_CONSTFUNC void indiesort(const typename plf::enable_if::value, iterator_type>::type first, const iterator_type last, comparison_function compare) 611 | #endif 612 | { 613 | non_random_access_sort(first, last, compare, std::distance(first, last)); 614 | } 615 | 616 | 617 | 618 | template 619 | #ifdef PLF_TYPE_TRAITS_SUPPORT 620 | PLF_CONSTFUNC void indiesort(const typename plf::enable_if<(is_pointer::value || std::is_same::iterator_category, std::random_access_iterator_tag>::value), iterator_type>::type first, const iterator_type last, comparison_function compare) 621 | #else 622 | PLF_CONSTFUNC void indiesort(const typename plf::enable_if::value, iterator_type>::type first, const iterator_type last, comparison_function compare) 623 | #endif 624 | { 625 | call_random_access_sort(first, last, compare); 626 | } 627 | 628 | 629 | 630 | template 631 | PLF_CONSTFUNC void indiesort(const iterator_type first, const iterator_type last) 632 | { 633 | indiesort(first, last, plf::less::value, iterator_type>::type>()); 634 | } 635 | 636 | 637 | 638 | // Container-based templates: 639 | 640 | #ifdef PLF_TYPE_TRAITS_SUPPORT 641 | template ::iterator_category, std::random_access_iterator_tag>::value, container_type>::type * = nullptr> 642 | PLF_CONSTFUNC void indiesort(container_type &container, comparison_function compare) 643 | { 644 | call_random_access_sort(container.begin(), container.end(), compare); 645 | } 646 | #endif 647 | 648 | 649 | 650 | #ifdef PLF_DECLTYPE_SUPPORT 651 | template 652 | class has_size_function 653 | { 654 | private: 655 | typedef char one; 656 | struct two { char x[2]; }; 657 | 658 | template PLF_CONSTFUNC static one test( decltype(&C::size) ) ; 659 | template PLF_CONSTFUNC static two test(...); 660 | 661 | public: 662 | enum { value = sizeof(test(0)) == sizeof(char) }; 663 | }; 664 | #endif 665 | 666 | 667 | 668 | #ifdef PLF_TYPE_TRAITS_SUPPORT 669 | template ::iterator_category, std::random_access_iterator_tag>::value, container_type>::type * = nullptr> 670 | #else 671 | template 672 | #endif 673 | PLF_CONSTFUNC void indiesort(container_type &container, comparison_function compare) 674 | { 675 | #ifdef PLF_DECLTYPE_SUPPORT 676 | if PLF_CONSTEXPR (has_size_function::value) 677 | { 678 | non_random_access_sort(container.begin(), container.end(), compare, static_cast(container.size())); 679 | } 680 | else 681 | #endif 682 | { // If no decltype support, assume container doesn't have size() 683 | indiesort(container.begin(), container.end(), compare); // call range indiesort 684 | } 685 | } 686 | 687 | 688 | 689 | template 690 | PLF_CONSTFUNC void indiesort(container_type &container) 691 | { 692 | indiesort(container, plf::less()); 693 | } 694 | 695 | } 696 | 697 | 698 | #undef PLF_DECLTYPE_SUPPORT 699 | #undef PLF_TYPE_TRAITS_SUPPORT 700 | #undef PLF_ALLOCATOR_TRAITS_SUPPORT 701 | #undef PLF_VARIADICS_SUPPORT 702 | #undef PLF_MOVE_SEMANTICS_SUPPORT 703 | #undef PLF_NOEXCEPT 704 | #undef PLF_CONSTEXPR 705 | #undef PLF_CONSTFUNC 706 | 707 | #undef PLF_CONSTRUCT 708 | #undef PLF_ALLOCATE 709 | #undef PLF_DEALLOCATE 710 | 711 | #if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) 712 | #pragma warning ( pop ) 713 | #endif 714 | 715 | #ifdef PLF_SORT_FUNCTION_DEFINED 716 | #undef PLF_SORT_FUNCTION 717 | #undef PLF_SORT_FUNCTION_DEFINED 718 | #endif 719 | 720 | 721 | #endif // PLF_INDIESORT_H 722 | -------------------------------------------------------------------------------- /plf_indiesort_test_suite.cpp: -------------------------------------------------------------------------------- 1 | #if defined(_MSC_VER) 2 | #if _MSC_VER >= 1600 3 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT 4 | #endif 5 | #elif defined(__cplusplus) && __cplusplus >= 201103L // C++11 support, at least 6 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT 7 | #endif 8 | 9 | 10 | #include // std::greater 11 | #include 12 | #include 13 | #include 14 | 15 | #include "plf_indiesort.h" 16 | 17 | 18 | 19 | void title1(const char *title_text) 20 | { 21 | std::cout << std::endl << std::endl << std::endl << "*** " << title_text << " ***" << std::endl; 22 | std::cout << "===========================================" << std::endl << std::endl << std::endl; 23 | } 24 | 25 | 26 | 27 | void title2(const char *title_text) 28 | { 29 | std::cout << std::endl << std::endl << "--- " << title_text << " ---" << std::endl << std::endl; 30 | } 31 | 32 | 33 | 34 | void breakfail(const char *error_message) 35 | { 36 | std::cout << error_message << std::endl; 37 | std::cin.get(); 38 | abort(); 39 | } 40 | 41 | 42 | 43 | struct small_struct 44 | { 45 | double *empty_field_1; 46 | double unused_number; 47 | unsigned int empty_field2; 48 | double *empty_field_3; 49 | int number; 50 | unsigned int empty_field4; 51 | 52 | small_struct() : number(0) {}; 53 | small_struct(const int num) : number(num) {}; 54 | int operator * () const { return number; }; 55 | bool operator == (const small_struct &source) const { return source.number == number; }; 56 | bool operator != (const small_struct &source) const { return source.number != number; }; 57 | bool operator > (const small_struct &source) const { return number > source.number; }; 58 | bool operator < (const small_struct &source) const { return number < source.number; }; 59 | bool operator >= (const small_struct &source) const { return number >= source.number; }; 60 | bool operator <= (const small_struct &source) const { return number <= source.number; }; 61 | }; 62 | 63 | 64 | 65 | int global_counter = 0; 66 | 67 | struct small_struct_non_trivial 68 | { 69 | double *empty_field_1; 70 | double unused_number; 71 | unsigned int empty_field2; 72 | double *empty_field_3; 73 | int number; 74 | unsigned int empty_field4; 75 | 76 | small_struct_non_trivial() : number(0) {}; 77 | small_struct_non_trivial(const int num) : number(num) {}; 78 | small_struct_non_trivial(const small_struct_non_trivial &source) : number(source.number) {}; 79 | small_struct_non_trivial operator = (small_struct_non_trivial &source) { number = source.number; return *this; }; 80 | 81 | #ifdef PLF_TEST_MOVE_SEMANTICS_SUPPORT 82 | small_struct_non_trivial(small_struct_non_trivial &&source) : number(std::move(source.number)) {}; 83 | small_struct_non_trivial operator = (small_struct_non_trivial &&source) { number = std::move(source.number); return *this; }; 84 | #endif 85 | 86 | int operator * () const { return number; }; 87 | bool operator == (const small_struct_non_trivial &source) const { return source.number == number; }; 88 | bool operator != (const small_struct_non_trivial &source) const { return source.number != number; }; 89 | bool operator > (const small_struct_non_trivial &source) const { return number > source.number; }; 90 | bool operator < (const small_struct_non_trivial &source) const { return number < source.number; }; 91 | bool operator >= (const small_struct_non_trivial &source) const { return number >= source.number; }; 92 | bool operator <= (const small_struct_non_trivial &source) const { return number <= source.number; }; 93 | }; 94 | 95 | 96 | 97 | struct large_struct 98 | { 99 | int numbers[100]; 100 | char a_string[50]; 101 | double unused_number; 102 | int number; 103 | double *empty_field_1; 104 | double *empty_field_2; 105 | unsigned int empty_field3; 106 | unsigned int empty_field4; 107 | 108 | large_struct() : number(0) {}; 109 | large_struct(const int num) : number(num) {}; 110 | int operator * () const { return number; }; 111 | bool operator == (const large_struct &source) const { return source.number == number; }; 112 | bool operator != (const large_struct &source) const { return source.number != number; }; 113 | bool operator > (const large_struct &source) const { return number > source.number; }; 114 | bool operator < (const large_struct &source) const { return number < source.number; }; 115 | bool operator >= (const large_struct &source) const { return number >= source.number; }; 116 | bool operator <= (const large_struct &source) const { return number <= source.number; }; 117 | }; 118 | 119 | 120 | 121 | 122 | int main() 123 | { 124 | freopen("error.log","w", stderr); // For catching assertion failure info when run outside of a command line prompt 125 | 126 | unsigned int looper = 0; 127 | 128 | while (++looper != 600) 129 | { 130 | title1("Indiesort"); 131 | 132 | { 133 | title2("Test Basics Int"); 134 | 135 | std::vector vec; 136 | 137 | for (int i = 60; i != -1; --i) 138 | { 139 | vec.push_back(i); 140 | } 141 | 142 | for (int i = 0; i != 60; ++i) 143 | { 144 | vec.push_back(i); 145 | } 146 | 147 | plf::indiesort(vec); 148 | 149 | int last = 0; 150 | 151 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 152 | { 153 | if (last > *it) 154 | { 155 | breakfail("Sequential int last > *it!."); 156 | } 157 | 158 | last = *it; 159 | } 160 | 161 | std::cout << "Sequential int passed\n"; 162 | 163 | vec.clear(); 164 | 165 | 166 | for (int i = 0; i != 70000; ++i) 167 | { 168 | vec.push_back(rand() & 65535); 169 | } 170 | 171 | plf::indiesort(vec, std::greater()); 172 | 173 | last = 65535; 174 | 175 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 176 | { 177 | if (last < *it) 178 | { 179 | breakfail("Random int last < *it!."); 180 | } 181 | 182 | last = *it; 183 | } 184 | 185 | std::cout << "Random int passed\n"; 186 | } 187 | 188 | 189 | { 190 | title2("Test Basics small_struct"); 191 | 192 | std::vector vec; 193 | 194 | for (int i = 60; i != -1; --i) 195 | { 196 | vec.push_back(i); 197 | } 198 | 199 | for (int i = 0; i != 60; ++i) 200 | { 201 | vec.push_back(i); 202 | } 203 | 204 | plf::indiesort(vec); 205 | 206 | small_struct last = 0; 207 | 208 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 209 | { 210 | if (last > **it) 211 | { 212 | breakfail("Sequential small_struct last > *it!."); 213 | } 214 | 215 | last = **it; 216 | } 217 | 218 | std::cout << "Sequential Small Struct passed\n"; 219 | 220 | vec.clear(); 221 | 222 | 223 | for (int i = 0; i != 6000; ++i) 224 | { 225 | vec.push_back(rand() & 65535); 226 | } 227 | 228 | plf::indiesort(vec, std::greater()); 229 | 230 | last = 65535; 231 | 232 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 233 | { 234 | if (last < *it) 235 | { 236 | breakfail("Random small_struct last < *it!."); 237 | } 238 | 239 | last = *it; 240 | } 241 | 242 | std::cout << "Random Small Struct passed\n"; 243 | } 244 | 245 | 246 | 247 | { 248 | title2("Test Basics C array"); 249 | 250 | large_struct *structs = new large_struct[500]; 251 | 252 | for (int i = 0; i != 500; ++i) 253 | { 254 | structs[i] = rand() & 65535; 255 | } 256 | 257 | plf::indiesort(&structs[0], &structs[500]); 258 | 259 | large_struct last = 0; 260 | 261 | for (int i = 0; i != 500; ++i) 262 | { 263 | if (last > structs[i]) 264 | { 265 | breakfail("Random large_struct last > *it!."); 266 | } 267 | 268 | last = structs[i]; 269 | } 270 | 271 | std::cout << "Random large_struct C array passed\n"; 272 | } 273 | 274 | 275 | 276 | { 277 | title2("Test Basics small_struct_non_trivial"); 278 | 279 | std::vector vec; 280 | 281 | for (int i = 60; i != -1; --i) 282 | { 283 | vec.push_back(i); 284 | } 285 | 286 | for (int i = 0; i != 60; ++i) 287 | { 288 | vec.push_back(i); 289 | } 290 | 291 | plf::indiesort(vec); 292 | 293 | int last = 0; 294 | 295 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 296 | { 297 | if (last > **it) 298 | { 299 | breakfail("Sequential small_struct_non_trivial last > *it!."); 300 | } 301 | 302 | last = **it; 303 | } 304 | 305 | std::cout << "Sequential small_struct_non_trivial passed\n"; 306 | 307 | vec.clear(); 308 | 309 | 310 | for (int i = 0; i != 6000; ++i) 311 | { 312 | vec.push_back(rand() & 65535); 313 | } 314 | 315 | plf::indiesort(vec, std::greater()); 316 | 317 | last = 65535; 318 | 319 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 320 | { 321 | if (last < **it) 322 | { 323 | breakfail("Random small_struct_non_trivial last < *it!."); 324 | } 325 | 326 | last = **it; 327 | } 328 | 329 | std::cout << "Random small_struct_non_trivial passed\n"; 330 | } 331 | 332 | 333 | 334 | { 335 | title2("Test Basics large_struct"); 336 | 337 | std::vector vec; 338 | 339 | for (int i = 60; i != -1; --i) 340 | { 341 | vec.push_back(i); 342 | } 343 | 344 | for (int i = 0; i != 60; ++i) 345 | { 346 | vec.push_back(i); 347 | } 348 | 349 | plf::indiesort(vec); 350 | 351 | large_struct last = 0; 352 | 353 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 354 | { 355 | if (last > **it) 356 | { 357 | breakfail("Sequential large_struct last > *it!."); 358 | } 359 | 360 | last = **it; 361 | } 362 | 363 | std::cout << "Sequential large_struct passed\n"; 364 | 365 | vec.clear(); 366 | 367 | 368 | for (int i = 0; i != 6000; ++i) 369 | { 370 | vec.push_back(rand() & 65535); 371 | } 372 | 373 | plf::indiesort(vec, std::greater()); 374 | 375 | last = 65535; 376 | 377 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 378 | { 379 | if (last < *it) 380 | { 381 | breakfail("Random large_struct last < *it!."); 382 | } 383 | 384 | last = *it; 385 | } 386 | 387 | std::cout << "Random large_struct passed\n"; 388 | } 389 | 390 | 391 | 392 | 393 | { 394 | title2("std::list int range tests"); 395 | 396 | { 397 | std::list ilist; 398 | 399 | for (int i = 0; i != 2000; ++i) 400 | { 401 | ilist.push_back(rand() & 65535); 402 | } 403 | 404 | 405 | std::list::iterator start = ilist.begin(), end = ilist.end(); 406 | plf::indiesort(start, end); 407 | 408 | 409 | int last = 0; 410 | 411 | do 412 | { 413 | if (last > *start) 414 | { 415 | breakfail("std::list sort test > *it!."); 416 | } 417 | 418 | last = *start; 419 | } while (++start != end); 420 | 421 | std::cout << "std::list sort test passed\n"; 422 | } 423 | 424 | 425 | 426 | { 427 | title2("std::list small_struct range tests"); 428 | 429 | std::list sslist; 430 | 431 | for (int i = 0; i != 5000; ++i) 432 | { 433 | sslist.push_back(rand() & 65535); 434 | } 435 | 436 | 437 | std::list::iterator start = sslist.begin(), end = sslist.end(); 438 | std::advance(start, 150); 439 | std::advance(end, -300); 440 | 441 | plf::indiesort(start, end); 442 | 443 | 444 | int last = 0; 445 | 446 | do 447 | { 448 | if (last > **start) 449 | { 450 | breakfail("std::list small_struct sort test > *it!."); 451 | } 452 | 453 | last = **start; 454 | } while (++start != end); 455 | 456 | std::cout << "std::list small_struct sort test passed\n"; 457 | } 458 | 459 | 460 | 461 | { 462 | title2("std::list large_struct range tests"); 463 | 464 | std::list lslist; 465 | 466 | for (int i = 0; i != 6000; ++i) 467 | { 468 | lslist.push_back(rand() & 65535); 469 | } 470 | 471 | 472 | plf::indiesort(lslist); 473 | 474 | int last = 0; 475 | 476 | std::list::iterator start = lslist.begin(), end = lslist.end(); 477 | 478 | do 479 | { 480 | if (last > **start) 481 | { 482 | breakfail("std::list large_struct sort test > *it!."); 483 | } 484 | 485 | last = **start; 486 | } while (++start != end); 487 | 488 | std::cout << "std::list large_struct sort test passed\n"; 489 | } 490 | 491 | 492 | } 493 | } 494 | 495 | 496 | title1("All tests passed! Press Enter to Exit."); 497 | std::cin.get(); 498 | 499 | return 0; 500 | } 501 | --------------------------------------------------------------------------------