├── LICENSE.md ├── README.md ├── plf_list.h └── plf_list_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::list 2 | A drop-in replacement for std::list with (on average): 3 | 4 | * 293% faster insertion 5 | * 57% faster erasure 6 | * 17% faster iteration 7 | * 77% faster sorting 8 | * 70% faster reversal 9 | * 91% faster remove/remove_if 10 | * 63% faster unique 11 | * 811% faster clear (1147900% for trivially-destructible types) 12 | * 1248% faster destruction (6350% for trivially-destructible types) 13 | * 20-24% faster performance overall in ordered use-case benchmarking(insertion, erasure and iteration on the fly and over time) 14 | 15 | (Benchmarks performed on a haswell-based CPU under GCC 8.1: http://www.plflib.org/benchmarks_haswell_gcc.htm 16 | Insertion, erasure, and iteration percentages obtained as average of performance across 5 types from char to very large struct) 17 | 18 | Documentation and function descriptions are here: https://plflib.org/list.htm 19 | plf::list is C++98/03/11/14/17/20/23/etc compatible. 20 | -------------------------------------------------------------------------------- /plf_list.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, 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 | 21 | #ifndef PLF_LIST_H 22 | #define PLF_LIST_H 23 | 24 | 25 | // Compiler-specific defines: 26 | 27 | // Define default cases before possibly redefining: 28 | #define PLF_NOEXCEPT throw() 29 | #define PLF_NOEXCEPT_ALLOCATOR 30 | #define PLF_CONSTEXPR 31 | #define PLF_CONSTFUNC 32 | 33 | #define PLF_EXCEPTIONS_SUPPORT 34 | 35 | #if ((defined(__clang__) || defined(__GNUC__)) && !defined(__EXCEPTIONS)) || (defined(_MSC_VER) && !defined(_CPPUNWIND)) 36 | #undef PLF_EXCEPTIONS_SUPPORT 37 | #include // std::terminate 38 | #endif 39 | 40 | 41 | #if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) 42 | // Suppress incorrect (unfixed MSVC bug) warnings re: constant expressions in constexpr-if statements 43 | #pragma warning ( push ) 44 | #pragma warning ( disable : 4127 ) 45 | 46 | #if _MSC_VER >= 1600 47 | #define PLF_MOVE_SEMANTICS_SUPPORT 48 | #endif 49 | #if _MSC_VER >= 1700 50 | #define PLF_TYPE_TRAITS_SUPPORT 51 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 52 | #endif 53 | #if _MSC_VER >= 1800 54 | #define PLF_VARIADICS_SUPPORT // Variadics, in this context, means both variadic templates and variadic macros are supported 55 | #define PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 56 | #define PLF_INITIALIZER_LIST_SUPPORT 57 | #endif 58 | #if _MSC_VER >= 1900 59 | #define PLF_ALIGNMENT_SUPPORT 60 | #undef PLF_NOEXCEPT 61 | #undef PLF_NOEXCEPT_ALLOCATOR 62 | #define PLF_NOEXCEPT noexcept 63 | #define PLF_NOEXCEPT_ALLOCATOR noexcept(noexcept(allocator_type())) 64 | #define PLF_IS_ALWAYS_EQUAL_SUPPORT 65 | #endif 66 | 67 | #if defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L) 68 | #undef PLF_CONSTEXPR 69 | #define PLF_CONSTEXPR constexpr 70 | #endif 71 | 72 | #if defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L) && _MSC_VER >= 1929 73 | #define PLF_CPP20_SUPPORT 74 | #undef PLF_CONSTFUNC 75 | #define PLF_CONSTFUNC constexpr 76 | #endif 77 | #elif defined(__cplusplus) && __cplusplus >= 201103L // C++11 support, at least 78 | #if defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__clang__) // If compiler is GCC/G++ 79 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4 80 | #define PLF_MOVE_SEMANTICS_SUPPORT 81 | #define PLF_VARIADICS_SUPPORT 82 | #endif 83 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4 84 | #define PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 85 | #define PLF_INITIALIZER_LIST_SUPPORT 86 | #endif 87 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 88 | #undef PLF_NOEXCEPT 89 | #undef PLF_NOEXCEPT_ALLOCATOR 90 | #define PLF_NOEXCEPT noexcept 91 | #define PLF_NOEXCEPT_ALLOCATOR noexcept(noexcept(allocator_type())) 92 | #endif 93 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4 94 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 95 | #endif 96 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4 97 | #define PLF_ALIGNMENT_SUPPORT 98 | #endif 99 | #if __GNUC__ >= 5 // GCC v4.9 and below do not support std::is_trivially_copyable 100 | #define PLF_TYPE_TRAITS_SUPPORT 101 | #endif 102 | #if __GNUC__ > 6 103 | #define PLF_IS_ALWAYS_EQUAL_SUPPORT 104 | #endif 105 | #elif defined(__clang__) && !defined(__GLIBCXX__) && !defined(_LIBCPP_CXX03_LANG) && __clang_major__ >= 3 106 | #define PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 107 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 108 | #define PLF_TYPE_TRAITS_SUPPORT 109 | 110 | #if __has_feature(cxx_alignas) && __has_feature(cxx_alignof) 111 | #define PLF_ALIGNMENT_SUPPORT 112 | #endif 113 | #if __has_feature(cxx_noexcept) 114 | #undef PLF_NOEXCEPT 115 | #undef PLF_NOEXCEPT_ALLOCATOR 116 | #define PLF_NOEXCEPT noexcept 117 | #define PLF_NOEXCEPT_ALLOCATOR noexcept(noexcept(allocator_type())) 118 | #define PLF_IS_ALWAYS_EQUAL_SUPPORT 119 | #endif 120 | #if __has_feature(cxx_rvalue_references) && !defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) 121 | #define PLF_MOVE_SEMANTICS_SUPPORT 122 | #endif 123 | #if __has_feature(cxx_variadic_templates) && !defined(_LIBCPP_HAS_NO_VARIADICS) 124 | #define PLF_VARIADICS_SUPPORT 125 | #endif 126 | #if (__clang_major__ == 3 && __clang_minor__ >= 1) || __clang_major__ > 3 127 | #define PLF_INITIALIZER_LIST_SUPPORT 128 | #endif 129 | #elif defined(__GLIBCXX__) // Using another compiler type with libstdc++ - we are assuming full c++11 compliance for compiler - which may not be true 130 | #define PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 131 | 132 | #if __GLIBCXX__ >= 20080606 133 | #define PLF_MOVE_SEMANTICS_SUPPORT 134 | #define PLF_VARIADICS_SUPPORT 135 | #endif 136 | #if __GLIBCXX__ >= 20090421 137 | #define PLF_INITIALIZER_LIST_SUPPORT 138 | #endif 139 | #if __GLIBCXX__ >= 20120322 140 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 141 | #undef PLF_NOEXCEPT 142 | #undef PLF_NOEXCEPT_ALLOCATOR 143 | #define PLF_NOEXCEPT noexcept 144 | #define PLF_NOEXCEPT_ALLOCATOR noexcept(noexcept(allocator_type())) 145 | #endif 146 | #if __GLIBCXX__ >= 20130322 147 | #define PLF_ALIGNMENT_SUPPORT 148 | #endif 149 | #if __GLIBCXX__ >= 20150422 // libstdc++ v4.9 and below do not support std::is_trivially_copyable 150 | #define PLF_TYPE_TRAITS_SUPPORT 151 | #endif 152 | #if __GLIBCXX__ >= 20160111 153 | #define PLF_IS_ALWAYS_EQUAL_SUPPORT 154 | #endif 155 | #elif defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) // Special case for checking C++11 support with libCPP 156 | #if !defined(_LIBCPP_HAS_NO_VARIADICS) 157 | #define PLF_VARIADICS_SUPPORT 158 | #endif 159 | #else // Assume type traits and initializer support for other compilers and standard library implementations 160 | #define PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 161 | #define PLF_MOVE_SEMANTICS_SUPPORT 162 | #define PLF_VARIADICS_SUPPORT 163 | #define PLF_TYPE_TRAITS_SUPPORT 164 | #define PLF_ALLOCATOR_TRAITS_SUPPORT 165 | #define PLF_ALIGNMENT_SUPPORT 166 | #define PLF_INITIALIZER_LIST_SUPPORT 167 | #undef PLF_NOEXCEPT 168 | #undef PLF_NOEXCEPT_ALLOCATOR 169 | #define PLF_NOEXCEPT noexcept 170 | #define PLF_NOEXCEPT_ALLOCATOR noexcept(noexcept(allocator_type())) 171 | #define PLF_IS_ALWAYS_EQUAL_SUPPORT 172 | #endif 173 | 174 | #if __cplusplus >= 201703L && ((defined(__clang__) && ((__clang_major__ == 3 && __clang_minor__ == 9) || __clang_major__ > 3)) || (defined(__GNUC__) && __GNUC__ >= 7) || (!defined(__clang__) && !defined(__GNUC__))) // assume correct C++17 implementation for non-gcc/clang compilers 175 | #undef PLF_CONSTEXPR 176 | #define PLF_CONSTEXPR constexpr 177 | #endif 178 | 179 | #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__))) 180 | #define PLF_CPP20_SUPPORT 181 | #undef PLF_CONSTFUNC 182 | #define PLF_CONSTFUNC constexpr 183 | #endif 184 | #endif 185 | 186 | #if defined(PLF_IS_ALWAYS_EQUAL_SUPPORT) && defined(PLF_MOVE_SEMANTICS_SUPPORT) && defined(PLF_ALLOCATOR_TRAITS_SUPPORT) && (__cplusplus >= 201703L || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L))) 187 | #define PLF_NOEXCEPT_MOVE_ASSIGN(the_allocator) noexcept(std::allocator_traits::propagate_on_container_move_assignment::value || std::allocator_traits::is_always_equal::value) 188 | #define PLF_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits::propagate_on_container_swap::value || std::allocator_traits::is_always_equal::value) 189 | #else 190 | #define PLF_NOEXCEPT_MOVE_ASSIGN(the_allocator) 191 | #define PLF_NOEXCEPT_SWAP(the_allocator) 192 | #endif 193 | 194 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT 195 | #ifdef PLF_VARIADICS_SUPPORT 196 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, ...) std::allocator_traits::construct(allocator_instance, location, __VA_ARGS__) 197 | #else 198 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, data) std::allocator_traits::construct(allocator_instance, location, data) 199 | #endif 200 | 201 | #define PLF_DESTROY(the_allocator, allocator_instance, location) std::allocator_traits::destroy(allocator_instance, location) 202 | #define PLF_ALLOCATE(the_allocator, allocator_instance, size, hint) std::allocator_traits::allocate(allocator_instance, size, hint) 203 | #define PLF_DEALLOCATE(the_allocator, allocator_instance, location, size) std::allocator_traits::deallocate(allocator_instance, location, size) 204 | #else 205 | #ifdef PLF_VARIADICS_SUPPORT 206 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, ...) (allocator_instance).construct(location, __VA_ARGS__) 207 | #else 208 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, data) (allocator_instance).construct(location, data) 209 | #endif 210 | 211 | #define PLF_DESTROY(the_allocator, allocator_instance, location) (allocator_instance).destroy(location) 212 | #define PLF_ALLOCATE(the_allocator, allocator_instance, size, hint) (allocator_instance).allocate(size, hint) 213 | #define PLF_DEALLOCATE(the_allocator, allocator_instance, location, size) (allocator_instance).deallocate(location, size) 214 | #endif 215 | 216 | 217 | #ifdef PLF_VARIADICS_SUPPORT 218 | #define PLF_CONSTRUCT_NODE(location, next, prev, element) PLF_CONSTRUCT(node_allocator_type, node_allocator_pair, location, next, prev, element) 219 | #else 220 | #define PLF_CONSTRUCT_NODE(location, next, prev, element) PLF_CONSTRUCT(node_allocator_type, node_allocator_pair, location, node(next, prev, element)) 221 | #endif 222 | 223 | 224 | 225 | #include // memmove, memcpy 226 | #include // assert 227 | #include // std::numeric_limits 228 | #include // std::uninitialized_copy, std::allocator, std::to_address 229 | #include // std::bidirectional_iterator_tag, iterator_traits, std::move_iterator, std::distance 230 | #include // std::length_error 231 | #include // std::move, std::swap 232 | 233 | 234 | #if !defined(PLF_SORT_FUNCTION) || defined(PLF_CPP20_SUPPORT) 235 | #include // std::sort, lexicographical_three_way_compare (C++20) 236 | #endif 237 | 238 | #ifdef PLF_TYPE_TRAITS_SUPPORT 239 | #include // std::is_trivially_destructible, etc 240 | #endif 241 | 242 | 243 | #ifdef PLF_INITIALIZER_LIST_SUPPORT 244 | #include 245 | #endif 246 | 247 | #ifdef PLF_CPP20_SUPPORT 248 | #include 249 | #include // std::strong_ordering 250 | #include 251 | 252 | namespace plf 253 | { 254 | // For getting std:: overload for reverse_iterator to match plf::list iterators specifically (see bottom of header): 255 | template 256 | concept list_iterator_concept = requires { typename T::list_iterator_tag; }; 257 | 258 | #ifndef PLF_FROM_RANGE 259 | #define PLF_FROM_RANGE 260 | 261 | // Until such point as standard libraries include std::ranges::from_range_t, including this so the rangesv3 constructor overloads will work unambiguously: 262 | namespace ranges 263 | { 264 | struct from_range_t {}; 265 | constexpr from_range_t from_range; 266 | } 267 | #endif 268 | } 269 | #endif 270 | 271 | 272 | 273 | namespace plf 274 | { 275 | 276 | 277 | #ifndef PLF_TOOLS 278 | #define PLF_TOOLS 279 | 280 | // std:: tool replacements for C++03/98/11 support: 281 | template 282 | struct enable_if 283 | { 284 | typedef T type; 285 | }; 286 | 287 | template 288 | struct enable_if 289 | {}; 290 | 291 | 292 | 293 | template struct conditional; 294 | 295 | template struct conditional 296 | { 297 | typedef is_true type; 298 | }; 299 | 300 | template struct conditional 301 | { 302 | typedef is_false type; 303 | }; 304 | 305 | 306 | 307 | template 308 | struct less 309 | { 310 | bool operator() (const element_type &a, const element_type &b) const PLF_NOEXCEPT 311 | { 312 | return a < b; 313 | } 314 | }; 315 | 316 | 317 | 318 | template 319 | struct equal_to 320 | { 321 | const element_type &value; 322 | 323 | explicit equal_to(const element_type &store_value) PLF_NOEXCEPT: 324 | value(store_value) 325 | {} 326 | 327 | bool operator() (const element_type &compare_value) const PLF_NOEXCEPT 328 | { 329 | return value == compare_value; 330 | } 331 | }; 332 | 333 | 334 | 335 | // To enable conversion to void * when allocator supplies non-raw pointers: 336 | template 337 | static PLF_CONSTFUNC void * void_cast(const source_pointer_type source_pointer) PLF_NOEXCEPT 338 | { 339 | #ifdef PLF_CPP20_SUPPORT 340 | return static_cast(std::to_address(source_pointer)); 341 | #else 342 | return static_cast(&*source_pointer); 343 | #endif 344 | } 345 | 346 | 347 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 348 | template 349 | static PLF_CONSTFUNC std::move_iterator make_move_iterator(iterator_type it) 350 | { 351 | return std::move_iterator(std::move(it)); 352 | } 353 | #endif 354 | 355 | 356 | 357 | enum priority { performance = 1, memory_use = 4}; 358 | 359 | #endif 360 | 361 | 362 | 363 | template > class list : private allocator_type 364 | { 365 | public: 366 | // Standard container typedefs: 367 | typedef element_type value_type; 368 | typedef unsigned short group_size_type; 369 | 370 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT 371 | typedef typename std::allocator_traits::size_type size_type; 372 | typedef typename std::allocator_traits::difference_type difference_type; 373 | typedef element_type & reference; 374 | typedef const element_type & const_reference; 375 | typedef typename std::allocator_traits::pointer pointer; 376 | typedef typename std::allocator_traits::const_pointer const_pointer; 377 | #else 378 | typedef typename allocator_type::size_type size_type; 379 | typedef typename allocator_type::difference_type difference_type; 380 | typedef typename allocator_type::reference reference; 381 | typedef typename allocator_type::const_reference const_reference; 382 | typedef typename allocator_type::pointer pointer; 383 | typedef typename allocator_type::const_pointer const_pointer; 384 | #endif 385 | 386 | 387 | // Iterator declarations: 388 | template class list_iterator; 389 | typedef list_iterator iterator; 390 | typedef list_iterator const_iterator; 391 | friend class list_iterator; // Using 'iterator' typedef name here is illegal under C++03 392 | friend class list_iterator; 393 | 394 | template class list_reverse_iterator; 395 | typedef list_reverse_iterator reverse_iterator; 396 | typedef list_reverse_iterator const_reverse_iterator; 397 | friend class list_reverse_iterator; 398 | friend class list_reverse_iterator; 399 | 400 | 401 | private: 402 | struct group; // forward declarations for typedefs below 403 | struct node; 404 | 405 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT // >= C++11 406 | typedef typename std::allocator_traits::template rebind_alloc group_allocator_type; 407 | typedef typename std::allocator_traits::template rebind_alloc node_allocator_type; 408 | typedef typename std::allocator_traits::pointer group_pointer_type; 409 | typedef typename std::allocator_traits::pointer node_pointer_type; 410 | typedef typename std::allocator_traits::template rebind_alloc node_pointer_allocator_type; 411 | #else 412 | typedef typename allocator_type::template rebind::other group_allocator_type; 413 | typedef typename allocator_type::template rebind::other node_allocator_type; 414 | typedef typename group_allocator_type::pointer group_pointer_type; 415 | typedef typename node_allocator_type::pointer node_pointer_type; 416 | typedef typename allocator_type::template rebind::other node_pointer_allocator_type; 417 | #endif 418 | 419 | 420 | 421 | struct node_base 422 | { 423 | node_pointer_type next, previous; 424 | 425 | node_base() PLF_NOEXCEPT 426 | {} 427 | 428 | node_base(const node_pointer_type &n, const node_pointer_type &p) PLF_NOEXCEPT: 429 | next(n), 430 | previous(p) 431 | {} 432 | 433 | 434 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 435 | node_base(node_pointer_type &&n, node_pointer_type &&p) PLF_NOEXCEPT: 436 | next(std::move(n)), 437 | previous(std::move(p)) 438 | {} 439 | #endif 440 | }; 441 | 442 | 443 | 444 | struct node : public node_base 445 | { 446 | element_type element; 447 | 448 | node(const node_pointer_type next, const node_pointer_type previous, const element_type &source): 449 | node_base(next, previous), 450 | element(source) 451 | {} 452 | 453 | 454 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 455 | node(node_pointer_type &&next, node_pointer_type &&previous, element_type &&source) PLF_NOEXCEPT: 456 | node_base(std::move(next), std::move(previous)), 457 | element(std::move(source)) 458 | {} 459 | #endif 460 | 461 | 462 | #ifdef PLF_VARIADICS_SUPPORT 463 | template 464 | node(const node_pointer_type next, const node_pointer_type previous, arguments&&... parameters): 465 | node_base(next, previous), 466 | element(std::forward(parameters) ...) 467 | {} 468 | #endif 469 | }; 470 | 471 | 472 | 473 | struct group : public node_allocator_type // Node memory block + metadata 474 | { 475 | node_pointer_type nodes; 476 | node_pointer_type free_list_head; 477 | node_pointer_type beyond_end; 478 | group_size_type number_of_elements; 479 | 480 | 481 | group() PLF_NOEXCEPT: 482 | nodes(NULL), 483 | free_list_head(NULL), 484 | beyond_end(NULL), 485 | number_of_elements(0) 486 | {} 487 | 488 | 489 | #if defined(PLF_VARIADICS_SUPPORT) || defined(PLF_MOVE_SEMANTICS_SUPPORT) 490 | group(const group_size_type group_size, const node_pointer_type previous = NULL): 491 | nodes(PLF_ALLOCATE(node_allocator_type, *this, group_size, previous)), 492 | free_list_head(NULL), 493 | beyond_end(nodes + group_size), 494 | number_of_elements(0) 495 | {} 496 | #else 497 | // This is a hack around the fact that allocator_type::construct only supports copy construction in C++03 and copy elision does not occur on the vast majority of compilers in this circumstance. And to avoid running out of memory (and performance loss) from allocating the same block twice, we're allocating in this constructor and moving data in the copy constructor. 498 | group(const group_size_type group_size, const node_pointer_type previous = NULL) PLF_NOEXCEPT: 499 | nodes(NULL), 500 | free_list_head(previous), 501 | beyond_end(NULL), 502 | number_of_elements(group_size) 503 | {} 504 | 505 | // Not a real copy constructor ie. actually a move constructor. Only used for allocator.construct in C++03 for reasons stated above: 506 | group(const group &source): 507 | node_allocator_type(source), 508 | nodes(PLF_ALLOCATE(node_allocator_type, *this, source.number_of_elements, source.free_list_head)), 509 | free_list_head(NULL), 510 | beyond_end(nodes + source.number_of_elements), 511 | number_of_elements(0) 512 | {} 513 | #endif 514 | 515 | 516 | group & operator = (const group &source) PLF_NOEXCEPT // Actually a move operator, used by c++03 in group_vector's remove, expand_capacity and append functions 517 | { 518 | nodes = source.nodes; 519 | free_list_head = source.free_list_head; 520 | beyond_end = source.beyond_end; 521 | number_of_elements = source.number_of_elements; 522 | return *this; 523 | } 524 | 525 | 526 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 527 | group(group &&source) PLF_NOEXCEPT: 528 | node_allocator_type(source), 529 | nodes(std::move(source.nodes)), 530 | free_list_head(std::move(source.free_list_head)), 531 | beyond_end(std::move(source.beyond_end)), 532 | number_of_elements(source.number_of_elements) 533 | { 534 | source.nodes = NULL; 535 | source.beyond_end = NULL; 536 | } 537 | 538 | 539 | group & operator = (group &&source) PLF_NOEXCEPT 540 | { 541 | nodes = std::move(source.nodes); 542 | free_list_head = std::move(source.free_list_head); 543 | beyond_end = std::move(source.beyond_end); 544 | number_of_elements = std::move(source.number_of_elements); 545 | source.nodes = NULL; 546 | source.beyond_end = NULL; 547 | return *this; 548 | } 549 | #endif 550 | 551 | 552 | ~group() PLF_NOEXCEPT 553 | { 554 | PLF_DEALLOCATE(node_allocator_type, *this, nodes, static_cast(beyond_end - nodes)); 555 | } 556 | }; 557 | 558 | 559 | 560 | 561 | class group_vector : private allocator_type // Simple vector of groups + associated functions 562 | { 563 | public: 564 | group_pointer_type last_endpoint_group, block_pointer, last_searched_group; // last_endpoint_group is the last -active- group in the block. Other -inactive- (previously used, now empty of elements) groups may be stored after this group for future usage (to reduce deallocation/reallocation of nodes). block_pointer + size - 1 == the last group in the block, regardless of whether or not the group is active. 565 | size_type size; 566 | 567 | struct ebco_pair2 : node_pointer_allocator_type // empty-base-class optimisation 568 | { 569 | size_type capacity; // Total element capacity of all initialized groups 570 | ebco_pair2(const size_type number_of_elements, const allocator_type &alloc) PLF_NOEXCEPT: 571 | node_pointer_allocator_type(alloc), 572 | capacity(number_of_elements) 573 | {}; 574 | } node_pointer_allocator_pair; 575 | 576 | struct ebco_pair : group_allocator_type 577 | { 578 | size_type capacity; // Total number of groups 579 | ebco_pair(const size_type number_of_groups, const allocator_type &alloc) PLF_NOEXCEPT: 580 | group_allocator_type(alloc), 581 | capacity(number_of_groups) 582 | {}; 583 | } group_allocator_pair; 584 | 585 | 586 | 587 | group_vector(const allocator_type &alloc) PLF_NOEXCEPT: 588 | allocator_type(alloc), 589 | last_endpoint_group(NULL), 590 | block_pointer(NULL), 591 | last_searched_group(NULL), 592 | size(0), 593 | node_pointer_allocator_pair(0, alloc), 594 | group_allocator_pair(0, alloc) 595 | {} 596 | 597 | 598 | 599 | void blank() PLF_NOEXCEPT 600 | { 601 | #ifdef PLF_IS_ALWAYS_EQUAL_SUPPORT // allocator_traits and type_traits always available when is_always_equal is available 602 | if PLF_CONSTEXPR (std::is_standard_layout::value && std::allocator_traits::is_always_equal::value && std::is_trivial::value) 603 | { 604 | std::memset(static_cast(this), 0, sizeof(group_vector)); 605 | } 606 | else 607 | #endif 608 | { 609 | last_endpoint_group = NULL; 610 | block_pointer = NULL; 611 | last_searched_group = NULL; 612 | size = 0; 613 | node_pointer_allocator_pair.capacity = 0; 614 | group_allocator_pair.capacity = 0; 615 | } 616 | } 617 | 618 | 619 | 620 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 621 | group_vector(group_vector &&source) PLF_NOEXCEPT: 622 | allocator_type(source), 623 | last_endpoint_group(std::move(source.last_endpoint_group)), 624 | block_pointer(std::move(source.block_pointer)), 625 | last_searched_group(std::move(source.last_searched_group)), 626 | size(source.size), 627 | node_pointer_allocator_pair(source.node_pointer_allocator_pair.capacity, source), 628 | group_allocator_pair(source.group_allocator_pair.capacity, source) 629 | { 630 | source.blank(); 631 | } 632 | 633 | 634 | 635 | group_vector & operator = (group_vector &&source) PLF_NOEXCEPT 636 | { 637 | #ifdef PLF_IS_ALWAYS_EQUAL_SUPPORT 638 | if PLF_CONSTEXPR ((std::is_trivially_copyable::value || std::allocator_traits::is_always_equal::value) && std::is_trivial::value) 639 | { 640 | std::memcpy(static_cast(this), &source, sizeof(group_vector)); 641 | } 642 | else 643 | #endif 644 | { 645 | last_endpoint_group = std::move(source.last_endpoint_group); 646 | block_pointer = std::move(source.block_pointer); 647 | last_searched_group = std::move(source.last_searched_group); 648 | size = source.size; 649 | node_pointer_allocator_pair.capacity = source.node_pointer_allocator_pair.capacity; 650 | group_allocator_pair.capacity = source.group_allocator_pair.capacity; 651 | 652 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT 653 | if PLF_CONSTEXPR(std::allocator_traits::propagate_on_container_move_assignment::value) 654 | #endif 655 | { 656 | static_cast(*this) = static_cast(source); 657 | // Reconstruct rebinds: 658 | static_cast(node_pointer_allocator_pair) = node_allocator_type(*this); 659 | static_cast(group_allocator_pair) = group_allocator_type(*this); 660 | } 661 | } 662 | 663 | source.blank(); 664 | return *this; 665 | } 666 | #endif 667 | 668 | 669 | 670 | void destroy_all_data(const node_pointer_type last_endpoint_node) PLF_NOEXCEPT 671 | { 672 | if (block_pointer == NULL) return; 673 | 674 | #ifdef PLF_TYPE_TRAITS_SUPPORT 675 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value || !std::is_trivially_destructible::value) 676 | #endif 677 | { 678 | if (last_endpoint_node != NULL) clear(last_endpoint_node); 679 | } 680 | 681 | const group_pointer_type end = block_pointer + size; 682 | for (group_pointer_type current_group = block_pointer; current_group != end; ++current_group) 683 | { 684 | PLF_DESTROY(group_allocator_type, group_allocator_pair, current_group); 685 | } 686 | 687 | PLF_DEALLOCATE(group_allocator_type, group_allocator_pair, block_pointer, group_allocator_pair.capacity); 688 | blank(); 689 | } 690 | 691 | 692 | 693 | void destroy_group(const group_pointer_type current_group, const node_pointer_type end_node) PLF_NOEXCEPT 694 | { 695 | #ifdef PLF_TYPE_TRAITS_SUPPORT 696 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value || !std::is_trivially_destructible::value) 697 | #endif 698 | { 699 | if ((end_node - current_group->nodes) != current_group->number_of_elements) // If there are erased nodes present in the group 700 | { 701 | for (node_pointer_type current_node = current_group->nodes; current_node != end_node; ++current_node) 702 | { 703 | #ifdef PLF_TYPE_TRAITS_SUPPORT 704 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 705 | #endif 706 | { 707 | if (current_node->next != NULL) // is not part of free list ie. element has not already had it's destructor called 708 | { 709 | PLF_DESTROY(allocator_type, *this, &(current_node->element)); 710 | } 711 | } 712 | 713 | #ifdef PLF_TYPE_TRAITS_SUPPORT 714 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 715 | #endif 716 | { 717 | PLF_DESTROY(node_pointer_allocator_type, node_pointer_allocator_pair, &(current_node->next)); 718 | PLF_DESTROY(node_pointer_allocator_type, node_pointer_allocator_pair, &(current_node->previous)); 719 | } 720 | } 721 | } 722 | else 723 | { 724 | for (node_pointer_type current_node = current_group->nodes; current_node != end_node; ++current_node) 725 | { 726 | #ifdef PLF_TYPE_TRAITS_SUPPORT 727 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 728 | #endif 729 | { 730 | PLF_DESTROY(allocator_type, *this, &(current_node->element)); 731 | } 732 | 733 | #ifdef PLF_TYPE_TRAITS_SUPPORT 734 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 735 | #endif 736 | { 737 | PLF_DESTROY(node_pointer_allocator_type, node_pointer_allocator_pair, &(current_node->next)); 738 | PLF_DESTROY(node_pointer_allocator_type, node_pointer_allocator_pair, &(current_node->previous)); 739 | } 740 | } 741 | } 742 | } 743 | 744 | current_group->free_list_head = NULL; 745 | current_group->number_of_elements = 0; 746 | } 747 | 748 | 749 | 750 | void clear(const node_pointer_type last_endpoint_node) PLF_NOEXCEPT 751 | { 752 | for (group_pointer_type current_group = block_pointer; current_group != last_endpoint_group; ++current_group) 753 | { 754 | destroy_group(current_group, current_group->beyond_end); 755 | } 756 | 757 | destroy_group(last_endpoint_group, last_endpoint_node); 758 | last_searched_group = last_endpoint_group = block_pointer; 759 | } 760 | 761 | 762 | 763 | void expand_capacity(const size_type new_capacity) // used by add_new and append 764 | { 765 | const group_pointer_type old_block = block_pointer; 766 | block_pointer = PLF_ALLOCATE(group_allocator_type, group_allocator_pair, new_capacity, 0); 767 | 768 | #ifdef PLF_TYPE_TRAITS_SUPPORT 769 | if PLF_CONSTEXPR (std::is_trivially_copyable::value && std::is_trivially_destructible::value) 770 | { 771 | std::memcpy(plf::void_cast(block_pointer), plf::void_cast(old_block), sizeof(group) * size); 772 | } 773 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 774 | else if PLF_CONSTEXPR (std::is_move_constructible::value) 775 | { 776 | std::uninitialized_copy(plf::make_move_iterator(old_block), plf::make_move_iterator(old_block + size), block_pointer); 777 | } 778 | #endif 779 | else 780 | #endif 781 | { 782 | // If allocator supplies non-trivial pointers it becomes necessary to destroy the group. uninitialized_copy will not work in this context as the copy constructor for "group" is overriden in C++03/98. The = operator for "group" has been overriden to make the following work: 783 | const group_pointer_type end = old_block + size; 784 | 785 | for (group_pointer_type current_group = old_block, current_new_group = block_pointer; current_group != end; ++current_group) 786 | { 787 | *current_new_group++ = *current_group; 788 | 789 | current_group->nodes = NULL; 790 | current_group->beyond_end = NULL; 791 | PLF_DESTROY(group_allocator_type, group_allocator_pair, current_group); 792 | } 793 | } 794 | 795 | last_searched_group = block_pointer + (last_searched_group - old_block); // correct pointer post-reallocation 796 | PLF_DEALLOCATE(group_allocator_type, group_allocator_pair, old_block, group_allocator_pair.capacity); 797 | group_allocator_pair.capacity = new_capacity; 798 | } 799 | 800 | 801 | 802 | void add_new(const group_size_type group_size) 803 | { 804 | if (group_allocator_pair.capacity == size) expand_capacity(group_allocator_pair.capacity * 2); 805 | 806 | last_endpoint_group = block_pointer + size - 1; 807 | 808 | #ifdef PLF_VARIADICS_SUPPORT 809 | PLF_CONSTRUCT(group_allocator_type, group_allocator_pair, last_endpoint_group + 1, group_size, last_endpoint_group->nodes); 810 | #else 811 | PLF_CONSTRUCT(group_allocator_type, group_allocator_pair, last_endpoint_group + 1, group(group_size, last_endpoint_group->nodes)); 812 | #endif 813 | 814 | ++last_endpoint_group; // Doing this here instead of pre-construct to avoid need for a try-catch block 815 | node_pointer_allocator_pair.capacity += group_size; 816 | ++size; 817 | } 818 | 819 | 820 | 821 | void initialize(const group_size_type first_group_capacity) // For adding first group *only* when group vector is completely empty and block_pointer is NULL 822 | { 823 | last_endpoint_group = block_pointer = last_searched_group = PLF_ALLOCATE(group_allocator_type, group_allocator_pair, 1, 0); 824 | group_allocator_pair.capacity = 1; 825 | 826 | #ifdef PLF_VARIADICS_SUPPORT 827 | PLF_CONSTRUCT(group_allocator_type, group_allocator_pair, last_endpoint_group, first_group_capacity); 828 | #else 829 | PLF_CONSTRUCT(group_allocator_type, group_allocator_pair, last_endpoint_group, group(first_group_capacity)); 830 | #endif 831 | 832 | size = 1; // Doing these here instead of pre-construct to avoid need for a try-catch block 833 | node_pointer_allocator_pair.capacity = first_group_capacity; 834 | } 835 | 836 | 837 | 838 | #ifdef PLF_CPP20_SUPPORT 839 | #define PLF_TO_ADDRESS(pointer) std::to_address(pointer) 840 | #else 841 | #define PLF_TO_ADDRESS(pointer) &*(pointer) 842 | #endif 843 | 844 | 845 | 846 | void remove(const group_pointer_type group_to_erase) PLF_NOEXCEPT 847 | { 848 | if (last_searched_group >= group_to_erase && last_searched_group != block_pointer) --last_searched_group; 849 | 850 | node_pointer_allocator_pair.capacity -= static_cast(group_to_erase->beyond_end - group_to_erase->nodes); 851 | 852 | PLF_DESTROY(group_allocator_type, group_allocator_pair, group_to_erase); 853 | 854 | #ifdef PLF_TYPE_TRAITS_SUPPORT 855 | if PLF_CONSTEXPR (std::is_trivially_copyable::value && std::is_trivially_destructible::value) 856 | { 857 | std::memmove(plf::void_cast(group_to_erase), plf::void_cast(group_to_erase + 1), sizeof(group) * (--size - static_cast(PLF_TO_ADDRESS(group_to_erase) - PLF_TO_ADDRESS(block_pointer)))); 858 | } 859 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 860 | else if PLF_CONSTEXPR (std::is_move_constructible::value) 861 | { 862 | std::move(group_to_erase + 1, block_pointer + size--, group_to_erase); 863 | } 864 | #endif 865 | else 866 | #endif 867 | { 868 | group_pointer_type back = block_pointer + size--; 869 | std::copy(group_to_erase + 1, back--, group_to_erase); 870 | 871 | back->nodes = NULL; 872 | back->beyond_end = NULL; 873 | PLF_DESTROY(group_allocator_type, group_allocator_pair, back); 874 | } 875 | } 876 | 877 | 878 | 879 | void move_to_back(const group_pointer_type group_to_erase) 880 | { 881 | if (last_searched_group >= group_to_erase && last_searched_group != block_pointer) --last_searched_group; 882 | 883 | group *temp_group = PLF_ALLOCATE(group_allocator_type, group_allocator_pair, 1, NULL); 884 | 885 | #ifdef PLF_TYPE_TRAITS_SUPPORT 886 | if PLF_CONSTEXPR (std::is_trivially_copyable::value && std::is_trivially_destructible::value) 887 | { 888 | std::memcpy(plf::void_cast(temp_group), plf::void_cast(group_to_erase), sizeof(group)); 889 | std::memmove(plf::void_cast(group_to_erase), plf::void_cast(group_to_erase + 1), sizeof(group) * ((size - 1) - static_cast(PLF_TO_ADDRESS(group_to_erase) - PLF_TO_ADDRESS(block_pointer)))); 890 | std::memcpy(plf::void_cast(block_pointer + size - 1), plf::void_cast(temp_group), sizeof(group)); 891 | } 892 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 893 | else if PLF_CONSTEXPR (std::is_move_constructible::value) 894 | { 895 | PLF_CONSTRUCT(group_allocator_type, group_allocator_pair, temp_group, std::move(*group_to_erase)); 896 | std::move(group_to_erase + 1, block_pointer + size, group_to_erase); 897 | *(block_pointer + size - 1) = std::move(*temp_group); 898 | 899 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 900 | { 901 | PLF_DESTROY(group_allocator_type, group_allocator_pair, temp_group); 902 | } 903 | } 904 | #endif 905 | else 906 | #endif 907 | { 908 | PLF_CONSTRUCT(group_allocator_type, group_allocator_pair, temp_group, group()); 909 | 910 | *temp_group = *group_to_erase; 911 | std::copy(group_to_erase + 1, block_pointer + size, group_to_erase); 912 | *(block_pointer + --size) = *temp_group; 913 | 914 | temp_group->nodes = NULL; 915 | PLF_DESTROY(group_allocator_type, group_allocator_pair, temp_group); 916 | } 917 | 918 | PLF_DEALLOCATE(group_allocator_type, group_allocator_pair, temp_group, 1); 919 | } 920 | 921 | 922 | 923 | group_pointer_type get_nearest_freelist_group(const node_pointer_type location_node) PLF_NOEXCEPT // In working implementation this cannot throw 924 | { 925 | const group_pointer_type beyond_end_group = last_endpoint_group + 1; 926 | group_pointer_type left = last_searched_group - 1, right = last_searched_group + 1, freelist_group = NULL; 927 | bool right_not_beyond_back = right < beyond_end_group; 928 | bool left_not_beyond_front = left >= block_pointer; 929 | 930 | 931 | if (location_node >= last_searched_group->nodes && location_node < last_searched_group->beyond_end) // ie. location is within last_search_group 932 | { 933 | if (last_searched_group->free_list_head != NULL) return last_searched_group; // if last_searched_group has previously-erased nodes 934 | // Else: search outwards using loop below this if/else block 935 | } 936 | else // search for the node group which location_node is located within, using last_searched_group as a starting point and searching left and right. Try and find the closest node group with reusable erased-element locations along the way: 937 | { 938 | group_pointer_type closest_freelist_left = (last_searched_group->free_list_head == NULL) ? NULL : last_searched_group; 939 | group_pointer_type closest_freelist_right = closest_freelist_left; 940 | 941 | while (true) 942 | { 943 | if (right_not_beyond_back) 944 | { 945 | if (location_node < right->beyond_end && location_node >= right->nodes) // location_node's group is found 946 | { 947 | last_searched_group = right; 948 | if (right->free_list_head != NULL) return right; // group has erased nodes, reuse them: 949 | difference_type left_distance; 950 | 951 | if (closest_freelist_right != NULL) 952 | { 953 | left_distance = right - closest_freelist_right; 954 | if (left_distance <= 2) return closest_freelist_right; // ie. this group is close enough to location_node's group 955 | freelist_group = closest_freelist_right; 956 | } 957 | else 958 | { 959 | left_distance = right - left; 960 | } 961 | 962 | 963 | // Otherwise find closest group with freelist - check an equal distance on the right to the distance we've checked on the left: 964 | const group_pointer_type right_plus_distance = right + left_distance; 965 | const group_pointer_type end_group = (right_plus_distance > beyond_end_group) ? beyond_end_group : right_plus_distance - 1; 966 | 967 | while (++right != end_group) 968 | { 969 | if (right->free_list_head != NULL) return right; 970 | } 971 | 972 | if (freelist_group != NULL) return freelist_group; 973 | 974 | right_not_beyond_back = right < beyond_end_group; 975 | break; // group with reusable erased nodes not found yet, continue searching in loop below 976 | } 977 | 978 | if (right->free_list_head != NULL) // location_node's group not found, but a reusable location found 979 | { 980 | if ((closest_freelist_right == NULL) & (closest_freelist_left == NULL)) closest_freelist_left = right; 981 | closest_freelist_right = right; 982 | } 983 | 984 | right_not_beyond_back = ++right < beyond_end_group; 985 | } 986 | 987 | 988 | if (left_not_beyond_front) 989 | { 990 | if (location_node >= left->nodes && location_node < left->beyond_end) 991 | { 992 | last_searched_group = left; 993 | if (left->free_list_head != NULL) return left; 994 | difference_type right_distance; 995 | 996 | if (closest_freelist_left != NULL) 997 | { 998 | right_distance = closest_freelist_left - left; 999 | if (right_distance <= 2) return closest_freelist_left; 1000 | freelist_group = closest_freelist_left; 1001 | } 1002 | else 1003 | { 1004 | right_distance = right - left; 1005 | } 1006 | 1007 | // Otherwise find closest group with freelist: 1008 | const group_pointer_type left_minus_distance = left - right_distance; 1009 | const group_pointer_type end_group = (left_minus_distance < block_pointer) ? block_pointer - 1 : left_minus_distance + 1; 1010 | 1011 | while (--left != end_group) 1012 | { 1013 | if (left->free_list_head != NULL) return left; 1014 | } 1015 | 1016 | if (freelist_group != NULL) return freelist_group; 1017 | left_not_beyond_front = left >= block_pointer; 1018 | break; 1019 | } 1020 | 1021 | if (left->free_list_head != NULL) 1022 | { 1023 | if ((closest_freelist_left == NULL) & (closest_freelist_right == NULL)) closest_freelist_right = left; 1024 | closest_freelist_left = left; 1025 | } 1026 | 1027 | left_not_beyond_front = --left >= block_pointer; 1028 | } 1029 | } 1030 | } 1031 | 1032 | 1033 | // The node group which location_node is located within, is known at this point. Continue searching outwards from this group until a group is found with a reusable location: 1034 | while (true) 1035 | { 1036 | if (right_not_beyond_back) 1037 | { 1038 | if (right->free_list_head != NULL) return right; 1039 | right_not_beyond_back = ++right < beyond_end_group; 1040 | } 1041 | 1042 | if (left_not_beyond_front) 1043 | { 1044 | if (left->free_list_head != NULL) return left; 1045 | left_not_beyond_front = --left >= block_pointer; 1046 | } 1047 | } 1048 | 1049 | // Will never reach here 1050 | } 1051 | 1052 | 1053 | 1054 | void swap(group_vector &source) PLF_NOEXCEPT_SWAP(group_allocator_type) 1055 | { 1056 | #ifdef PLF_IS_ALWAYS_EQUAL_SUPPORT 1057 | if PLF_CONSTEXPR (std::allocator_traits::is_always_equal::value && std::is_trivial::value) // if all pointer types are trivial we can just copy using memcpy - avoids constructors/destructors etc and is faster 1058 | { 1059 | char temp[sizeof(group_vector)]; 1060 | std::memcpy(static_cast(&temp), static_cast(this), sizeof(group_vector)); 1061 | std::memcpy(static_cast(this), static_cast(&source), sizeof(group_vector)); 1062 | std::memcpy(static_cast(&source), static_cast(&temp), sizeof(group_vector)); 1063 | } 1064 | else 1065 | #endif 1066 | { 1067 | // Otherwise, make the reads/writes as contiguous in memory as-possible (yes, it is faster than using std::swap with the individual variables): 1068 | const group_pointer_type swap_last_endpoint_group = last_endpoint_group, swap_block_pointer = block_pointer, swap_last_searched_group = last_searched_group; 1069 | const size_type swap_size = size, swap_element_capacity = node_pointer_allocator_pair.capacity, swap_capacity = group_allocator_pair.capacity; 1070 | 1071 | last_endpoint_group = source.last_endpoint_group; 1072 | block_pointer = source.block_pointer; 1073 | last_searched_group = source.last_searched_group; 1074 | size = source.size; 1075 | node_pointer_allocator_pair.capacity = source.node_pointer_allocator_pair.capacity; 1076 | group_allocator_pair.capacity = source.group_allocator_pair.capacity; 1077 | 1078 | source.last_endpoint_group = swap_last_endpoint_group; 1079 | source.block_pointer = swap_block_pointer; 1080 | source.last_searched_group = swap_last_searched_group; 1081 | source.size = swap_size; 1082 | source.node_pointer_allocator_pair.capacity = swap_element_capacity; 1083 | source.group_allocator_pair.capacity = swap_capacity; 1084 | 1085 | #ifdef PLF_IS_ALWAYS_EQUAL_SUPPORT 1086 | if PLF_CONSTEXPR (std::allocator_traits::propagate_on_container_swap::value && !std::allocator_traits::is_always_equal::value) 1087 | #endif 1088 | { 1089 | std::swap(static_cast(source), static_cast(*this)); 1090 | 1091 | // Reconstruct rebinds for swapped allocators: 1092 | static_cast(node_pointer_allocator_pair) = node_pointer_allocator_type(*this); 1093 | static_cast(group_allocator_pair) = group_allocator_type(*this); 1094 | static_cast(source.node_pointer_allocator_pair) = node_pointer_allocator_type(source); 1095 | static_cast(source.group_allocator_pair) = group_allocator_type(source); 1096 | } // else: undefined behaviour, as per standard 1097 | } 1098 | } 1099 | 1100 | 1101 | 1102 | void trim_unused_groups() PLF_NOEXCEPT // trim trailing groups previously allocated by reserve() or retained via erase() 1103 | { 1104 | const group_pointer_type end = block_pointer + size; 1105 | 1106 | for (group_pointer_type current_group = last_endpoint_group + 1; current_group != end; ++current_group) 1107 | { 1108 | node_pointer_allocator_pair.capacity -= static_cast(current_group->beyond_end - current_group->nodes); 1109 | PLF_DESTROY(group_allocator_type, group_allocator_pair, current_group); 1110 | } 1111 | 1112 | size -= static_cast(end - (last_endpoint_group + 1)); 1113 | } 1114 | 1115 | 1116 | 1117 | void append(group_vector &source) 1118 | { 1119 | source.trim_unused_groups(); 1120 | trim_unused_groups(); 1121 | 1122 | if (size + source.size > group_allocator_pair.capacity) expand_capacity(size + source.size); 1123 | 1124 | #ifdef PLF_TYPE_TRAITS_SUPPORT 1125 | if PLF_CONSTEXPR (std::is_trivially_copyable::value && std::is_trivially_destructible::value) 1126 | { 1127 | std::memcpy(plf::void_cast(block_pointer + size), plf::void_cast(source.block_pointer), sizeof(group) * source.size); 1128 | } 1129 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 1130 | else if PLF_CONSTEXPR (std::is_move_constructible::value) 1131 | { 1132 | std::uninitialized_copy(plf::make_move_iterator(source.block_pointer), plf::make_move_iterator(source.block_pointer + source.size), block_pointer + size); 1133 | } 1134 | #endif 1135 | else 1136 | #endif 1137 | { 1138 | const group_pointer_type end = source.block_pointer + source.size; 1139 | 1140 | for (group_pointer_type current_group = source.block_pointer, current_new_group = block_pointer + size; current_group != end; ++current_group) 1141 | { 1142 | *current_new_group++ = *current_group; 1143 | 1144 | current_group->nodes = NULL; 1145 | current_group->beyond_end = NULL; 1146 | PLF_DESTROY(group_allocator_type, source.group_allocator_pair, current_group); 1147 | } 1148 | } 1149 | 1150 | PLF_DEALLOCATE(group_allocator_type, source.group_allocator_pair, source.block_pointer, source.group_allocator_pair.capacity); 1151 | size += source.size; 1152 | last_endpoint_group = block_pointer + size - 1; 1153 | node_pointer_allocator_pair.capacity += source.node_pointer_allocator_pair.capacity; 1154 | source.blank(); 1155 | } 1156 | }; 1157 | 1158 | 1159 | 1160 | private: 1161 | 1162 | group_vector groups; // Structure which contains all groups (structures containing node memory blocks + block metadata) 1163 | node_base end_node; // The independent, content-less node which is returned by end() 1164 | // When the list is empty, the previous and next pointers of end_node both point to end_node. 1165 | node_pointer_type last_endpoint; // The node location which is one-past the last inserted element in the last group of the list. Is not affected by erasures to prior elements (these are handled using the group's freelist). 1166 | // If last_endpoint is beyond the end of a memory block it means a new group must be created upon the next insertion if prior erased nodes are not available for re-use. 1167 | // last_endpoint == NULL means total_size is zero, but there may still be groups available due to calling clear(), reserve() on an empty list, or having erased all elements in the list 1168 | // groups.block_pointer == NULL means an uninitialized container ie. no groups or elements yet 1169 | iterator end_iterator, begin_iterator; // Returned by begin() and end(). 1170 | // end_iterator always points to end_node. It is a convenience/optimization variable to save generating many temporary iterators from end_node during functions and during end(). 1171 | // When the list is empty of elements, begin_iterator == end_iterator so that program loops iterating from begin() to end() will function as expected. 1172 | size_type total_size; 1173 | 1174 | struct ebco_pair3 : node_allocator_type 1175 | { 1176 | size_type number_of_erased_nodes; 1177 | ebco_pair3(const size_type num_nodes, const allocator_type &alloc) PLF_NOEXCEPT: 1178 | node_allocator_type(alloc), 1179 | number_of_erased_nodes(num_nodes) 1180 | {}; 1181 | } node_allocator_pair; 1182 | 1183 | 1184 | 1185 | public: 1186 | 1187 | // Default constructor: 1188 | 1189 | PLF_CONSTFUNC list() PLF_NOEXCEPT_ALLOCATOR: 1190 | groups(*this), 1191 | end_node(static_cast(&end_node), static_cast(&end_node)), 1192 | last_endpoint(NULL), 1193 | end_iterator(static_cast(&end_node)), 1194 | begin_iterator(static_cast(&end_node)), 1195 | total_size(0), 1196 | node_allocator_pair(0, *this) 1197 | {} 1198 | 1199 | 1200 | 1201 | // Allocator-extended constructor: 1202 | 1203 | explicit list(const allocator_type &alloc): 1204 | allocator_type(alloc), 1205 | groups(alloc), 1206 | end_node(static_cast(&end_node), static_cast(&end_node)), 1207 | last_endpoint(NULL), 1208 | end_iterator(static_cast(&end_node)), 1209 | begin_iterator(static_cast(&end_node)), 1210 | total_size(0), 1211 | node_allocator_pair(0, alloc) 1212 | {} 1213 | 1214 | 1215 | 1216 | // Copy constructor: 1217 | 1218 | list(const list &source): 1219 | #if (defined(__cplusplus) && __cplusplus >= 201103L) || _MSC_VER >= 1700 1220 | allocator_type(std::allocator_traits::select_on_container_copy_construction(source)), 1221 | #else 1222 | allocator_type(source), 1223 | #endif 1224 | groups(*this), 1225 | end_node(static_cast(&end_node), static_cast(&end_node)), 1226 | last_endpoint(NULL), 1227 | end_iterator(static_cast(&end_node)), 1228 | begin_iterator(static_cast(&end_node)), 1229 | total_size(0), 1230 | node_allocator_pair(0, *this) 1231 | { 1232 | range_insert(end_iterator, source.total_size, source.begin_iterator); 1233 | } 1234 | 1235 | 1236 | 1237 | // Allocator-extended copy constructor: 1238 | 1239 | #ifdef PLF_CPP20_SUPPORT 1240 | list(const list &source, const std::type_identity_t &alloc): 1241 | #else 1242 | list(const list &source, const allocator_type &alloc): 1243 | #endif 1244 | allocator_type(alloc), 1245 | groups(alloc), 1246 | end_node(static_cast(&end_node), static_cast(&end_node)), 1247 | last_endpoint(NULL), 1248 | end_iterator(static_cast(&end_node)), 1249 | begin_iterator(static_cast(&end_node)), 1250 | total_size(0), 1251 | node_allocator_pair(0, alloc) 1252 | { 1253 | range_insert(end_iterator, source.total_size, source.begin_iterator); 1254 | } 1255 | 1256 | 1257 | 1258 | private: 1259 | 1260 | void blank() PLF_NOEXCEPT 1261 | { 1262 | end_node.next = static_cast(&end_node); 1263 | end_node.previous = static_cast(&end_node); 1264 | last_endpoint = NULL; 1265 | begin_iterator.node_pointer = end_iterator.node_pointer; 1266 | total_size = 0; 1267 | node_allocator_pair.number_of_erased_nodes = 0; 1268 | } 1269 | 1270 | 1271 | void reset() PLF_NOEXCEPT 1272 | { 1273 | groups.destroy_all_data(last_endpoint); 1274 | blank(); 1275 | } 1276 | 1277 | 1278 | 1279 | public: 1280 | 1281 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 1282 | // Move constructor: 1283 | 1284 | list(list &&source) PLF_NOEXCEPT: 1285 | allocator_type(static_cast(source)), 1286 | groups(std::move(source.groups)), 1287 | end_node(std::move(source.end_node)), 1288 | last_endpoint(std::move(source.last_endpoint)), 1289 | end_iterator(static_cast(&end_node)), 1290 | begin_iterator((source.begin_iterator.node_pointer == source.end_iterator.node_pointer) ? static_cast(&end_node) : std::move(source.begin_iterator)), 1291 | total_size(source.total_size), 1292 | node_allocator_pair(source.node_allocator_pair.number_of_erased_nodes, source) 1293 | { 1294 | end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer; 1295 | source.groups.blank(); 1296 | source.blank(); 1297 | } 1298 | 1299 | 1300 | 1301 | // Allocator-extended move constructor: 1302 | 1303 | #ifdef PLF_CPP20_SUPPORT 1304 | list(list &&source, const std::type_identity_t &alloc): 1305 | #else 1306 | list(list &&source, const allocator_type &alloc): 1307 | #endif 1308 | allocator_type(alloc), 1309 | groups(std::move(source.groups)), 1310 | end_node(std::move(source.end_node)), 1311 | last_endpoint(std::move(source.last_endpoint)), 1312 | end_iterator(static_cast(&end_node)), 1313 | begin_iterator((source.begin_iterator.node_pointer == source.end_iterator.node_pointer) ? static_cast(&end_node) : std::move(source.begin_iterator)), 1314 | total_size(source.total_size), 1315 | node_allocator_pair(source.node_allocator_pair.number_of_erased_nodes, alloc) 1316 | { 1317 | end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer; 1318 | source.groups.blank(); 1319 | source.blank(); 1320 | } 1321 | #endif 1322 | 1323 | 1324 | 1325 | // Fill constructor: 1326 | 1327 | list(const size_type fill_number, const element_type &element, const allocator_type &alloc = allocator_type()): 1328 | allocator_type(alloc), 1329 | groups(alloc), 1330 | end_node(static_cast(&end_node), static_cast(&end_node)), 1331 | last_endpoint(NULL), 1332 | end_iterator(static_cast(&end_node)), 1333 | begin_iterator(static_cast(&end_node)), 1334 | total_size(0), 1335 | node_allocator_pair(0, alloc) 1336 | { 1337 | insert(end_iterator, fill_number, element); 1338 | } 1339 | 1340 | 1341 | 1342 | // Default element value fill constructor: 1343 | 1344 | list(const size_type fill_number, const allocator_type &alloc = allocator_type()): 1345 | allocator_type(alloc), 1346 | groups(alloc), 1347 | end_node(static_cast(&end_node), static_cast(&end_node)), 1348 | last_endpoint(NULL), 1349 | end_iterator(static_cast(&end_node)), 1350 | begin_iterator(static_cast(&end_node)), 1351 | total_size(0), 1352 | node_allocator_pair(0, alloc) 1353 | { 1354 | insert(end_iterator, fill_number, element_type()); 1355 | } 1356 | 1357 | 1358 | 1359 | // Range constructor: 1360 | 1361 | template 1362 | list(const typename plf::enable_if::is_integer, iterator_type>::type &first, const iterator_type &last, const allocator_type &alloc = allocator_type()): 1363 | allocator_type(alloc), 1364 | groups(alloc), 1365 | end_node(static_cast(&end_node), static_cast(&end_node)), 1366 | last_endpoint(NULL), 1367 | end_iterator(static_cast(&end_node)), 1368 | begin_iterator(static_cast(&end_node)), 1369 | total_size(0), 1370 | node_allocator_pair(0, alloc) 1371 | { 1372 | insert(end_iterator, first, last); 1373 | } 1374 | 1375 | 1376 | 1377 | // Initializer-list constructor: 1378 | 1379 | #ifdef PLF_INITIALIZER_LIST_SUPPORT 1380 | list(const std::initializer_list &element_list, const allocator_type &alloc = allocator_type()): 1381 | allocator_type(alloc), 1382 | groups(alloc), 1383 | end_node(static_cast(&end_node), static_cast(&end_node)), 1384 | last_endpoint(NULL), 1385 | end_iterator(static_cast(&end_node)), 1386 | begin_iterator(static_cast(&end_node)), 1387 | total_size(0), 1388 | node_allocator_pair(0, alloc) 1389 | { 1390 | range_insert(end_iterator, static_cast(element_list.size()), element_list.begin()); 1391 | } 1392 | 1393 | #endif 1394 | 1395 | 1396 | 1397 | #ifdef PLF_CPP20_SUPPORT 1398 | // Ranges v3 constructor: 1399 | 1400 | template 1401 | requires std::ranges::range 1402 | list(plf::ranges::from_range_t, range_type &&rg, const allocator_type &alloc = allocator_type()): 1403 | allocator_type(alloc), 1404 | end_node(static_cast(&end_node), static_cast(&end_node)), 1405 | last_endpoint(NULL), 1406 | end_iterator(static_cast(&end_node)), 1407 | begin_iterator(static_cast(&end_node)), 1408 | total_size(0), 1409 | node_allocator_pair(0, alloc) 1410 | { 1411 | range_insert(end_iterator, static_cast(std::ranges::distance(rg)), std::ranges::begin(rg)); 1412 | } 1413 | #endif 1414 | 1415 | 1416 | 1417 | ~list() PLF_NOEXCEPT 1418 | { 1419 | groups.destroy_all_data(last_endpoint); 1420 | } 1421 | 1422 | 1423 | 1424 | iterator begin() PLF_NOEXCEPT 1425 | { 1426 | return begin_iterator; 1427 | } 1428 | 1429 | 1430 | 1431 | const_iterator begin() const PLF_NOEXCEPT 1432 | { 1433 | return begin_iterator; 1434 | } 1435 | 1436 | 1437 | 1438 | iterator end() PLF_NOEXCEPT 1439 | { 1440 | return end_iterator; 1441 | } 1442 | 1443 | 1444 | 1445 | const_iterator end() const PLF_NOEXCEPT 1446 | { 1447 | return end_iterator; 1448 | } 1449 | 1450 | 1451 | 1452 | const_iterator cbegin() const PLF_NOEXCEPT 1453 | { 1454 | return const_iterator(begin_iterator.node_pointer); 1455 | } 1456 | 1457 | 1458 | 1459 | const_iterator cend() const PLF_NOEXCEPT 1460 | { 1461 | return const_iterator(end_iterator.node_pointer); 1462 | } 1463 | 1464 | 1465 | 1466 | reverse_iterator rbegin() PLF_NOEXCEPT 1467 | { 1468 | return reverse_iterator(end_node.previous); 1469 | } 1470 | 1471 | 1472 | 1473 | const_reverse_iterator rbegin() const PLF_NOEXCEPT 1474 | { 1475 | return const_reverse_iterator(end_node.previous); 1476 | } 1477 | 1478 | 1479 | 1480 | reverse_iterator rend() PLF_NOEXCEPT 1481 | { 1482 | return reverse_iterator(end_iterator.node_pointer); 1483 | } 1484 | 1485 | 1486 | 1487 | const_reverse_iterator rend() const PLF_NOEXCEPT 1488 | { 1489 | return const_reverse_iterator(end_iterator.node_pointer); 1490 | } 1491 | 1492 | 1493 | 1494 | const_reverse_iterator crbegin() const PLF_NOEXCEPT 1495 | { 1496 | return const_reverse_iterator(end_node.previous); 1497 | } 1498 | 1499 | 1500 | 1501 | const_reverse_iterator crend() const PLF_NOEXCEPT 1502 | { 1503 | return const_reverse_iterator(end_iterator.node_pointer); 1504 | } 1505 | 1506 | 1507 | 1508 | reference front() 1509 | { 1510 | assert(total_size != 0); 1511 | return begin_iterator.node_pointer->element; 1512 | } 1513 | 1514 | 1515 | 1516 | const_reference front() const 1517 | { 1518 | assert(total_size != 0); 1519 | return begin_iterator.node_pointer->element; 1520 | } 1521 | 1522 | 1523 | 1524 | reference back() 1525 | { 1526 | assert(total_size != 0); 1527 | return end_node.previous->element; 1528 | } 1529 | 1530 | 1531 | 1532 | const_reference back() const 1533 | { 1534 | assert(total_size != 0); 1535 | return end_node.previous->element; 1536 | } 1537 | 1538 | 1539 | 1540 | void clear() PLF_NOEXCEPT 1541 | { 1542 | if (last_endpoint == NULL) return; // already clear'ed or uninitialized 1543 | if (total_size != 0) groups.clear(last_endpoint); 1544 | blank(); 1545 | } 1546 | 1547 | 1548 | 1549 | private: 1550 | 1551 | 1552 | 1553 | void add_group_if_necessary() 1554 | { 1555 | if (last_endpoint == groups.last_endpoint_group->beyond_end) // last_endpoint is beyond the end of a group 1556 | { 1557 | if (static_cast(groups.last_endpoint_group - groups.block_pointer) == groups.size - 1) // ie. there are no reusable groups available at the back of group vector 1558 | { 1559 | groups.add_new((total_size < list_max_block_capacity()) ? static_cast(total_size) : list_max_block_capacity()); 1560 | } 1561 | else 1562 | { 1563 | ++groups.last_endpoint_group; 1564 | } 1565 | 1566 | last_endpoint = groups.last_endpoint_group->nodes; 1567 | } 1568 | } 1569 | 1570 | 1571 | 1572 | void update_sizes_and_iterators(const const_iterator it) 1573 | { 1574 | ++(groups.last_endpoint_group->number_of_elements); 1575 | ++total_size; 1576 | if (it.node_pointer == begin_iterator.node_pointer) begin_iterator.node_pointer = last_endpoint; 1577 | it.node_pointer->previous->next = last_endpoint; 1578 | it.node_pointer->previous = last_endpoint; 1579 | } 1580 | 1581 | 1582 | 1583 | static PLF_CONSTFUNC group_size_type list_min_block_capacity() PLF_NOEXCEPT 1584 | { 1585 | return static_cast((sizeof(node) * 8 > (sizeof(list) + sizeof(group)) * 2) ? 8 : (((sizeof(list) + sizeof(group)) * 2) / sizeof(node)) + 1); 1586 | } 1587 | 1588 | 1589 | 1590 | static PLF_CONSTFUNC group_size_type list_max_block_capacity() PLF_NOEXCEPT 1591 | { 1592 | return 2048u; 1593 | } 1594 | 1595 | 1596 | 1597 | void insert_initialize() 1598 | { 1599 | if (groups.block_pointer == NULL) groups.initialize(list_min_block_capacity()); // In case of prior reserve/clear call as opposed to being uninitialized 1600 | 1601 | groups.last_endpoint_group->number_of_elements = 1; 1602 | end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = groups.last_endpoint_group->nodes; 1603 | total_size = 1; 1604 | } 1605 | 1606 | 1607 | 1608 | public: 1609 | 1610 | 1611 | iterator insert(const const_iterator it, const element_type &element) 1612 | { 1613 | if (last_endpoint != NULL) // ie. list is not empty 1614 | { 1615 | if (node_allocator_pair.number_of_erased_nodes == 0) // No erased nodes available for reuse 1616 | { 1617 | add_group_if_necessary(); 1618 | PLF_CONSTRUCT_NODE(last_endpoint, it.node_pointer, it.node_pointer->previous, element); 1619 | update_sizes_and_iterators(it); 1620 | return iterator(last_endpoint++); 1621 | } 1622 | else 1623 | { 1624 | const group_pointer_type node_group = groups.get_nearest_freelist_group((it.node_pointer != end_iterator.node_pointer) ? it.node_pointer : end_node.previous); 1625 | const node_pointer_type selected_node = node_group->free_list_head; 1626 | const node_pointer_type previous = node_group->free_list_head->previous; 1627 | PLF_CONSTRUCT_NODE(selected_node, it.node_pointer, it.node_pointer->previous, element); 1628 | 1629 | node_group->free_list_head = previous; 1630 | ++(node_group->number_of_elements); 1631 | ++total_size; 1632 | --node_allocator_pair.number_of_erased_nodes; 1633 | 1634 | it.node_pointer->previous->next = selected_node; 1635 | it.node_pointer->previous = selected_node; 1636 | 1637 | if (it.node_pointer == begin_iterator.node_pointer) begin_iterator.node_pointer = selected_node; 1638 | return iterator(selected_node); 1639 | } 1640 | } 1641 | else // list is empty 1642 | { 1643 | insert_initialize(); 1644 | 1645 | #ifndef PLF_EXCEPTIONS_SUPPORT 1646 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, element); 1647 | #else 1648 | #ifdef PLF_TYPE_TRAITS_SUPPORT 1649 | if PLF_CONSTEXPR (std::is_nothrow_copy_constructible::value) // Avoid try-catch code generation 1650 | { 1651 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, element); 1652 | } 1653 | else 1654 | #endif 1655 | { 1656 | try 1657 | { 1658 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, element); 1659 | } 1660 | catch (...) 1661 | { 1662 | reset(); 1663 | throw; 1664 | } 1665 | } 1666 | #endif 1667 | 1668 | return begin_iterator; 1669 | } 1670 | } 1671 | 1672 | 1673 | 1674 | void push_back(const element_type &element) 1675 | { 1676 | insert(end_iterator, element); 1677 | } 1678 | 1679 | 1680 | 1681 | void push_front(const element_type &element) 1682 | { 1683 | insert(begin_iterator, element); 1684 | } 1685 | 1686 | 1687 | 1688 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 1689 | iterator insert(const const_iterator it, element_type &&element) // This is almost identical to the insert implementation above with the only change being std::move of the element and the is_nothrow test 1690 | { 1691 | if (last_endpoint != NULL) 1692 | { 1693 | if (node_allocator_pair.number_of_erased_nodes == 0) 1694 | { 1695 | add_group_if_necessary(); 1696 | PLF_CONSTRUCT_NODE(last_endpoint, it.node_pointer, it.node_pointer->previous, std::move(element)); 1697 | update_sizes_and_iterators(it); 1698 | return iterator(last_endpoint++); 1699 | } 1700 | else 1701 | { 1702 | const group_pointer_type node_group = groups.get_nearest_freelist_group((it.node_pointer != end_iterator.node_pointer) ? it.node_pointer : end_node.previous); 1703 | const node_pointer_type selected_node = node_group->free_list_head; 1704 | const node_pointer_type previous = node_group->free_list_head->previous; 1705 | PLF_CONSTRUCT_NODE(selected_node, it.node_pointer, it.node_pointer->previous, std::move(element)); 1706 | 1707 | node_group->free_list_head = previous; 1708 | ++(node_group->number_of_elements); 1709 | ++total_size; 1710 | --node_allocator_pair.number_of_erased_nodes; 1711 | 1712 | it.node_pointer->previous->next = selected_node; 1713 | it.node_pointer->previous = selected_node; 1714 | 1715 | if (it.node_pointer == begin_iterator.node_pointer) begin_iterator.node_pointer = selected_node; 1716 | return iterator(selected_node); 1717 | } 1718 | } 1719 | else 1720 | { 1721 | insert_initialize(); 1722 | 1723 | #ifndef PLF_EXCEPTIONS_SUPPORT 1724 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::move(element)); 1725 | #else 1726 | #ifdef PLF_TYPE_TRAITS_SUPPORT 1727 | if PLF_CONSTEXPR (std::is_nothrow_move_constructible::value) 1728 | { 1729 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::move(element)); 1730 | } 1731 | else 1732 | #endif 1733 | { 1734 | try 1735 | { 1736 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::move(element)); 1737 | } 1738 | catch (...) 1739 | { 1740 | reset(); 1741 | throw; 1742 | } 1743 | } 1744 | #endif 1745 | 1746 | return begin_iterator; 1747 | } 1748 | } 1749 | 1750 | 1751 | 1752 | void push_back(element_type &&element) 1753 | { 1754 | insert(end_iterator, std::move(element)); 1755 | } 1756 | 1757 | 1758 | 1759 | void push_front(element_type &&element) 1760 | { 1761 | insert(begin_iterator, std::move(element)); 1762 | } 1763 | #endif 1764 | 1765 | 1766 | 1767 | 1768 | #ifdef PLF_VARIADICS_SUPPORT 1769 | template 1770 | iterator emplace(const const_iterator it, arguments &&... parameters) // This is almost identical to the insert implementations above with the only changes being std::forward of element parameters, removal of VARIADICS support checking, and is_nothrow_contructible 1771 | { 1772 | if (last_endpoint != NULL) 1773 | { 1774 | if (node_allocator_pair.number_of_erased_nodes == 0) 1775 | { 1776 | add_group_if_necessary(); 1777 | PLF_CONSTRUCT_NODE(last_endpoint, it.node_pointer, it.node_pointer->previous, std::forward(parameters)...); 1778 | update_sizes_and_iterators(it); 1779 | return iterator(last_endpoint++); 1780 | } 1781 | else 1782 | { 1783 | const group_pointer_type node_group = groups.get_nearest_freelist_group((it.node_pointer != end_iterator.node_pointer) ? it.node_pointer : end_node.previous); 1784 | const node_pointer_type selected_node = node_group->free_list_head; 1785 | const node_pointer_type previous = node_group->free_list_head->previous; 1786 | PLF_CONSTRUCT_NODE(selected_node, it.node_pointer, it.node_pointer->previous, std::forward(parameters)...); 1787 | 1788 | node_group->free_list_head = previous; 1789 | ++(node_group->number_of_elements); 1790 | ++total_size; 1791 | --node_allocator_pair.number_of_erased_nodes; 1792 | 1793 | it.node_pointer->previous->next = selected_node; 1794 | it.node_pointer->previous = selected_node; 1795 | 1796 | if (it.node_pointer == begin_iterator.node_pointer) begin_iterator.node_pointer = selected_node; 1797 | return iterator(selected_node); 1798 | } 1799 | } 1800 | else 1801 | { 1802 | insert_initialize(); 1803 | 1804 | #ifndef PLF_EXCEPTIONS_SUPPORT 1805 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::forward(parameters)...); 1806 | #else 1807 | #ifdef PLF_TYPE_TRAITS_SUPPORT 1808 | if PLF_CONSTEXPR (std::is_nothrow_constructible::value) 1809 | { 1810 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::forward(parameters)...); 1811 | } 1812 | else 1813 | #endif 1814 | { 1815 | try 1816 | { 1817 | PLF_CONSTRUCT_NODE(last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::forward(parameters)...); 1818 | } 1819 | catch (...) 1820 | { 1821 | reset(); 1822 | throw; 1823 | } 1824 | } 1825 | #endif 1826 | 1827 | return begin_iterator; 1828 | } 1829 | } 1830 | 1831 | 1832 | 1833 | template 1834 | reference emplace_back(arguments &&... parameters) 1835 | { 1836 | return (emplace(end_iterator, std::forward(parameters)...)).node_pointer->element; 1837 | } 1838 | 1839 | 1840 | 1841 | template 1842 | reference emplace_front(arguments &&... parameters) 1843 | { 1844 | return (emplace(begin_iterator, std::forward(parameters)...)).node_pointer->element; 1845 | } 1846 | 1847 | 1848 | #endif 1849 | 1850 | 1851 | 1852 | 1853 | private: 1854 | 1855 | void fill(const element_type &element, group_size_type number_of_elements, const node_pointer_type position) 1856 | { 1857 | position->previous->next = last_endpoint; 1858 | groups.last_endpoint_group->number_of_elements = static_cast(groups.last_endpoint_group->number_of_elements + number_of_elements); 1859 | node_pointer_type previous = position->previous; 1860 | 1861 | do 1862 | { 1863 | #ifdef PLF_TYPE_TRAITS_SUPPORT 1864 | if PLF_CONSTEXPR (std::is_nothrow_copy_constructible::value) 1865 | { 1866 | PLF_CONSTRUCT_NODE(last_endpoint, last_endpoint + 1, previous, element); 1867 | } 1868 | else 1869 | #endif 1870 | { 1871 | #ifdef PLF_EXCEPTIONS_SUPPORT 1872 | try 1873 | { 1874 | PLF_CONSTRUCT_NODE(last_endpoint, last_endpoint + 1, previous, element); 1875 | } 1876 | catch (...) 1877 | { 1878 | previous->next = position; 1879 | position->previous = --previous; 1880 | groups.last_endpoint_group->number_of_elements = static_cast(groups.last_endpoint_group->number_of_elements - (number_of_elements - (last_endpoint - position))); 1881 | throw; 1882 | } 1883 | #else 1884 | PLF_CONSTRUCT_NODE(last_endpoint, last_endpoint + 1, previous, element); 1885 | #endif 1886 | } 1887 | 1888 | previous = last_endpoint++; 1889 | } while (--number_of_elements != 0); 1890 | 1891 | previous->next = position; 1892 | position->previous = previous; 1893 | } 1894 | 1895 | 1896 | 1897 | 1898 | template 1899 | void range_fill(iterator_type &it, group_size_type number_of_elements, const node_pointer_type position) 1900 | { 1901 | position->previous->next = last_endpoint; 1902 | groups.last_endpoint_group->number_of_elements = static_cast(groups.last_endpoint_group->number_of_elements + number_of_elements); 1903 | node_pointer_type previous = position->previous; 1904 | 1905 | do 1906 | { 1907 | #ifdef PLF_TYPE_TRAITS_SUPPORT 1908 | if PLF_CONSTEXPR (std::is_nothrow_copy_constructible::value) 1909 | { 1910 | PLF_CONSTRUCT_NODE(last_endpoint, last_endpoint + 1, previous, *it++); 1911 | } 1912 | else 1913 | #endif 1914 | { 1915 | #ifdef PLF_EXCEPTIONS_SUPPORT 1916 | try 1917 | { 1918 | PLF_CONSTRUCT_NODE(last_endpoint, last_endpoint + 1, previous, *it++); 1919 | } 1920 | catch (...) 1921 | { 1922 | previous->next = position; 1923 | position->previous = --previous; 1924 | groups.last_endpoint_group->number_of_elements = static_cast(groups.last_endpoint_group->number_of_elements - (number_of_elements - (last_endpoint - position))); 1925 | throw; 1926 | } 1927 | #else 1928 | PLF_CONSTRUCT_NODE(last_endpoint, last_endpoint + 1, previous, *it++); 1929 | #endif 1930 | } 1931 | 1932 | previous = last_endpoint++; 1933 | } while (--number_of_elements != 0); 1934 | 1935 | previous->next = position; 1936 | position->previous = previous; 1937 | } 1938 | 1939 | 1940 | 1941 | // This function is near-identical to fill-insert, the only difference is it uses and iterates over an iterator: 1942 | template 1943 | iterator range_insert(const const_iterator position, const size_type number_of_elements, iterator_type it) 1944 | { 1945 | if (number_of_elements == 0) 1946 | { 1947 | return end_iterator; 1948 | } 1949 | else if (number_of_elements == 1) 1950 | { 1951 | return insert(position, *it); 1952 | } 1953 | 1954 | reserve(total_size + number_of_elements); 1955 | 1956 | // Insert first element, then use up any erased nodes: 1957 | size_type remainder = number_of_elements - 1; 1958 | const iterator return_iterator = insert(position, *it++); 1959 | 1960 | while (node_allocator_pair.number_of_erased_nodes != 0) 1961 | { 1962 | insert(position, *it++); 1963 | if (--remainder == 0) return return_iterator; 1964 | } 1965 | 1966 | total_size += remainder; 1967 | 1968 | // then use up remainder of last_endpoint_group: 1969 | const group_size_type remaining_nodes_in_group = static_cast(groups.last_endpoint_group->beyond_end - last_endpoint); 1970 | 1971 | if (remaining_nodes_in_group != 0) 1972 | { 1973 | if (remaining_nodes_in_group < remainder) 1974 | { 1975 | range_fill(it, remaining_nodes_in_group, position.node_pointer); 1976 | remainder -= remaining_nodes_in_group; 1977 | } 1978 | else 1979 | { 1980 | range_fill(it, static_cast(remainder), position.node_pointer); 1981 | return return_iterator; 1982 | } 1983 | } 1984 | 1985 | 1986 | // Then start using trailing (reserved) groups: 1987 | while (true) 1988 | { 1989 | last_endpoint = (++groups.last_endpoint_group)->nodes; 1990 | const group_size_type group_size = static_cast(groups.last_endpoint_group->beyond_end - groups.last_endpoint_group->nodes); 1991 | 1992 | if (group_size < remainder) 1993 | { 1994 | range_fill(it, group_size, position.node_pointer); 1995 | remainder -= group_size; 1996 | } 1997 | else 1998 | { 1999 | range_fill(it, static_cast(remainder), position.node_pointer); 2000 | break; 2001 | } 2002 | } 2003 | 2004 | return return_iterator; 2005 | } 2006 | 2007 | 2008 | 2009 | 2010 | public: 2011 | 2012 | // Fill insert 2013 | 2014 | iterator insert(const const_iterator position, const size_type number_of_elements, const element_type &element) 2015 | { 2016 | if (number_of_elements == 0) 2017 | { 2018 | return end_iterator; 2019 | } 2020 | else if (number_of_elements == 1) 2021 | { 2022 | return insert(position, element); 2023 | } 2024 | 2025 | reserve(total_size + number_of_elements); 2026 | 2027 | // Insert first element, then use up any erased nodes: 2028 | size_type remainder = number_of_elements - 1; 2029 | const iterator return_iterator = insert(position, element); 2030 | 2031 | while (node_allocator_pair.number_of_erased_nodes != 0) 2032 | { 2033 | insert(position, element); 2034 | if (--remainder == 0) return return_iterator; 2035 | } 2036 | 2037 | total_size += remainder; 2038 | 2039 | // then use up remainder of last_endpoint_group: 2040 | const group_size_type remaining_nodes_in_group = static_cast(groups.last_endpoint_group->beyond_end - last_endpoint); 2041 | 2042 | if (remaining_nodes_in_group != 0) 2043 | { 2044 | if (remaining_nodes_in_group < remainder) 2045 | { 2046 | fill(element, remaining_nodes_in_group, position.node_pointer); 2047 | remainder -= remaining_nodes_in_group; 2048 | } 2049 | else 2050 | { 2051 | fill(element, static_cast(remainder), position.node_pointer); 2052 | return return_iterator; 2053 | } 2054 | } 2055 | 2056 | 2057 | // Then start using trailing (reserved) groups: 2058 | while (true) 2059 | { 2060 | last_endpoint = (++groups.last_endpoint_group)->nodes; 2061 | const group_size_type group_size = static_cast(groups.last_endpoint_group->beyond_end - groups.last_endpoint_group->nodes); 2062 | 2063 | if (group_size < remainder) 2064 | { 2065 | fill(element, group_size, position.node_pointer); 2066 | remainder -= group_size; 2067 | } 2068 | else 2069 | { 2070 | fill(element, static_cast(remainder), position.node_pointer); 2071 | break; 2072 | } 2073 | } 2074 | 2075 | return return_iterator; 2076 | } 2077 | 2078 | 2079 | 2080 | // Range insert 2081 | 2082 | template 2083 | iterator insert(const const_iterator position, typename plf::enable_if::is_integer, iterator_type>::type first, const iterator_type last) 2084 | { 2085 | return range_insert(position, static_cast(std::distance(first, last)), first); 2086 | } 2087 | 2088 | 2089 | 2090 | // Range insert, move_iterator overload 2091 | 2092 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 2093 | template 2094 | iterator insert (const const_iterator position, const std::move_iterator first, const std::move_iterator last) 2095 | { 2096 | return range_insert(position, static_cast(std::distance(first.base(),last.base())), first); 2097 | } 2098 | #endif 2099 | 2100 | 2101 | 2102 | // Initializer-list insert 2103 | 2104 | #ifdef PLF_INITIALIZER_LIST_SUPPORT 2105 | iterator insert(const const_iterator it, const std::initializer_list &element_list) 2106 | { 2107 | return range_insert(it, static_cast(element_list.size()), element_list.begin()); 2108 | } 2109 | #endif 2110 | 2111 | 2112 | 2113 | #ifdef PLF_CPP20_SUPPORT 2114 | template 2115 | requires std::ranges::range 2116 | iterator insert_range(const const_iterator it, range_type &&the_range) 2117 | { 2118 | return range_insert(it, static_cast(std::ranges::distance(the_range)), std::ranges::begin(the_range)); 2119 | } 2120 | #endif 2121 | 2122 | 2123 | 2124 | private: 2125 | 2126 | void destroy_all_node_pointers(const group_pointer_type group_to_process, const node_pointer_type beyond_end_node) PLF_NOEXCEPT 2127 | { 2128 | for (node_pointer_type current_node = group_to_process->nodes; current_node != beyond_end_node; ++current_node) 2129 | { 2130 | PLF_DESTROY(node_pointer_allocator_type, groups.node_pointer_allocator_pair, &(current_node->next)); 2131 | PLF_DESTROY(node_pointer_allocator_type, groups.node_pointer_allocator_pair, &(current_node->previous)); 2132 | } 2133 | } 2134 | 2135 | 2136 | 2137 | public: 2138 | 2139 | 2140 | // Single erase: 2141 | 2142 | iterator erase(const const_iterator it) // if uninitialized/invalid iterator supplied, function could generate an exception, hence no noexcept 2143 | { 2144 | assert(total_size != 0); 2145 | assert(it.node_pointer != NULL); // uninitialized iterator 2146 | assert(it.node_pointer != end_iterator.node_pointer); // iterator points to end() 2147 | 2148 | #ifdef PLF_TYPE_TRAITS_SUPPORT 2149 | if PLF_CONSTEXPR (!(std::is_trivially_destructible::value)) 2150 | #endif 2151 | { 2152 | PLF_DESTROY(allocator_type, *this, &(it.node_pointer->element)); // Destruct element 2153 | } 2154 | 2155 | --total_size; 2156 | ++node_allocator_pair.number_of_erased_nodes; 2157 | 2158 | 2159 | // find the group this element is in, starting from the last group an element to-be-erased was found in (as erasures are, for most programs, likely to be closer in proximity to previous erasures): 2160 | group_pointer_type node_group = groups.last_searched_group; 2161 | 2162 | if (it.node_pointer < node_group->nodes || it.node_pointer >= node_group->beyond_end) 2163 | { 2164 | // Search groups to the left and right of the last searched group, in the group vector: 2165 | const group_pointer_type beyond_end_group = groups.last_endpoint_group + 1; 2166 | group_pointer_type left = node_group - 1; 2167 | bool right_not_beyond_back = ++node_group < beyond_end_group; 2168 | bool left_not_beyond_front = left >= groups.block_pointer; 2169 | 2170 | while (true) 2171 | { 2172 | if (right_not_beyond_back) 2173 | { 2174 | if (it.node_pointer < node_group->beyond_end && it.node_pointer >= node_group->nodes) break; // element location found 2175 | right_not_beyond_back = ++node_group < beyond_end_group; 2176 | } 2177 | 2178 | if (left_not_beyond_front) 2179 | { 2180 | if (it.node_pointer >= left->nodes && it.node_pointer < left->beyond_end) // element location found 2181 | { 2182 | node_group = left; 2183 | break; 2184 | } 2185 | 2186 | left_not_beyond_front = --left >= groups.block_pointer; 2187 | } 2188 | } 2189 | 2190 | groups.last_searched_group = node_group; 2191 | } 2192 | 2193 | // To avoid pointer aliasing and increase performance: 2194 | const node_pointer_type previous = it.node_pointer->previous; 2195 | const node_pointer_type next = it.node_pointer->next; 2196 | next->previous = previous; 2197 | previous->next = next; 2198 | 2199 | if (it.node_pointer == begin_iterator.node_pointer) begin_iterator.node_pointer = next; 2200 | 2201 | 2202 | const iterator return_iterator(next); 2203 | 2204 | if (--(node_group->number_of_elements) != 0) // ie. group is not empty yet, add node to free list 2205 | { 2206 | it.node_pointer->next = NULL; // next == NULL so that destructor and other functions which linearly iterate over node memory chunks can detect this as a free list node, ie an erased node 2207 | it.node_pointer->previous = node_group->free_list_head; 2208 | node_group->free_list_head = it.node_pointer; 2209 | return return_iterator; 2210 | } 2211 | else if (node_group != groups.last_endpoint_group--) // remove group (and decrement active back group) 2212 | { 2213 | const group_size_type group_size = static_cast(node_group->beyond_end - node_group->nodes); 2214 | node_allocator_pair.number_of_erased_nodes -= group_size; 2215 | 2216 | #ifdef PLF_TYPE_TRAITS_SUPPORT 2217 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 2218 | #endif 2219 | { 2220 | destroy_all_node_pointers(node_group, node_group->beyond_end); 2221 | } 2222 | 2223 | node_group->free_list_head = NULL; 2224 | 2225 | if ((group_size == list_max_block_capacity()) | (node_group >= groups.last_endpoint_group - 1)) // Preserve only groups which are at the maximum possible size, or first/second/third-to-last active groups - seems to be best for performance under high-modification benchmarks 2226 | { 2227 | groups.move_to_back(node_group); 2228 | } 2229 | else 2230 | { 2231 | groups.remove(node_group); 2232 | } 2233 | 2234 | return return_iterator; 2235 | } 2236 | else // clear back group, leave trailing 2237 | { 2238 | #ifdef PLF_TYPE_TRAITS_SUPPORT 2239 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 2240 | #endif 2241 | { 2242 | destroy_all_node_pointers(node_group, last_endpoint); 2243 | } 2244 | 2245 | node_group->free_list_head = NULL; 2246 | 2247 | if (total_size != 0) 2248 | { 2249 | node_allocator_pair.number_of_erased_nodes -= static_cast(last_endpoint - node_group->nodes); 2250 | last_endpoint = groups.last_endpoint_group->beyond_end; 2251 | } 2252 | else 2253 | { 2254 | groups.last_endpoint_group = groups.block_pointer; // If number of elements is zero, it indicates that this was the only group in the vector. In which case the last_endpoint_group would be invalid at this point due to the decrement in the above else-if statement. So it needs to be reset, as it will not be reset in the function call below. 2255 | blank(); 2256 | } 2257 | 2258 | return return_iterator; 2259 | } 2260 | } 2261 | 2262 | 2263 | 2264 | // Range-erase: 2265 | 2266 | iterator erase(const_iterator iterator1, const const_iterator iterator2) 2267 | { 2268 | while (iterator1 != iterator2) 2269 | { 2270 | iterator1 = erase(iterator1); 2271 | } 2272 | 2273 | return iterator(iterator2.node_pointer); 2274 | } 2275 | 2276 | 2277 | 2278 | void pop_back() // Exception will occur on empty list 2279 | { 2280 | erase(iterator(end_node.previous)); 2281 | } 2282 | 2283 | 2284 | 2285 | void pop_front() 2286 | { 2287 | erase(begin_iterator); 2288 | } 2289 | 2290 | 2291 | 2292 | list & operator = (const list &source) 2293 | { 2294 | assert (&source != this); 2295 | 2296 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT 2297 | if PLF_CONSTEXPR (std::allocator_traits::propagate_on_container_copy_assignment::value) 2298 | #endif 2299 | { 2300 | #ifdef PLF_IS_ALWAYS_EQUAL_SUPPORT 2301 | if PLF_CONSTEXPR (!std::allocator_traits::is_always_equal::value) 2302 | #endif 2303 | { 2304 | if(static_cast(*this) != static_cast(source)) 2305 | { // Deallocate existing blocks as source allocator is not necessarily able to do so 2306 | reset(); 2307 | } 2308 | } 2309 | 2310 | static_cast(*this) = static_cast(source); 2311 | 2312 | // Reconstruct rebinds: 2313 | static_cast(node_allocator_pair) = node_allocator_type(*this); 2314 | } 2315 | 2316 | range_assign(source.begin_iterator, source.total_size); 2317 | return *this; 2318 | } 2319 | 2320 | 2321 | 2322 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 2323 | private: 2324 | 2325 | void move_assign(list &&source) PLF_NOEXCEPT 2326 | { 2327 | groups.destroy_all_data(last_endpoint); 2328 | 2329 | groups = std::move(source.groups); 2330 | end_node = std::move(source.end_node); 2331 | last_endpoint = std::move(source.last_endpoint); 2332 | begin_iterator.node_pointer = (source.begin_iterator.node_pointer == source.end_iterator.node_pointer) ? end_iterator.node_pointer : std::move(source.begin_iterator.node_pointer); 2333 | total_size = source.total_size; 2334 | node_allocator_pair.number_of_erased_nodes = source.node_allocator_pair.number_of_erased_nodes; 2335 | 2336 | end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer; 2337 | source.groups.blank(); 2338 | 2339 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT 2340 | if PLF_CONSTEXPR(std::allocator_traits::propagate_on_container_move_assignment::value) 2341 | #endif 2342 | { 2343 | static_cast(*this) = source; 2344 | static_cast(node_allocator_pair) = node_allocator_type(*this); 2345 | } 2346 | 2347 | source.blank(); 2348 | } 2349 | 2350 | 2351 | 2352 | public: 2353 | 2354 | // Move assignment 2355 | list & operator = (list &&source) PLF_NOEXCEPT_MOVE_ASSIGN(allocator_type) 2356 | { 2357 | assert (&source != this); 2358 | 2359 | // Move source values across: 2360 | #ifdef PLF_IS_ALWAYS_EQUAL_SUPPORT 2361 | if PLF_CONSTEXPR (std::allocator_traits::propagate_on_container_move_assignment::value || std::allocator_traits::is_always_equal::value) 2362 | { 2363 | move_assign(std::move(source)); 2364 | } 2365 | else 2366 | #endif 2367 | if (static_cast(*this) == static_cast(source)) 2368 | { 2369 | move_assign(std::move(source)); 2370 | } 2371 | else // Allocator isn't movable so move/copy elements from source and deallocate the source's blocks: 2372 | { 2373 | reset(); 2374 | 2375 | #ifdef PLF_TYPE_TRAITS_SUPPORT 2376 | if PLF_CONSTEXPR (!std::is_move_constructible::value) 2377 | { 2378 | #ifdef PLF_EXCEPTIONS_SUPPORT 2379 | if PLF_CONSTEXPR (!std::is_copy_constructible::value) 2380 | { 2381 | throw std::domain_error("Cannot perform move assignment, allocators are not equal and type is not copy/move-constructible."); 2382 | } 2383 | #endif 2384 | 2385 | range_insert(end_iterator, source.total_size, source.begin_iterator); 2386 | } 2387 | else 2388 | #endif 2389 | { 2390 | range_insert(end_iterator, source.total_size, plf::make_move_iterator(source.begin_iterator)); 2391 | } 2392 | 2393 | source.reset(); 2394 | } 2395 | 2396 | return *this; 2397 | } 2398 | #endif 2399 | 2400 | 2401 | 2402 | #ifdef PLF_INITIALIZER_LIST_SUPPORT 2403 | list & operator = (const std::initializer_list &element_list) 2404 | { 2405 | range_assign(element_list.begin(), static_cast(element_list.size())); 2406 | return *this; 2407 | } 2408 | #endif 2409 | 2410 | 2411 | 2412 | friend bool operator == (const list &lh, const list &rh) PLF_NOEXCEPT 2413 | { 2414 | if (lh.total_size != rh.total_size) return false; 2415 | 2416 | for (const_iterator lh_iterator = lh.begin_iterator, rh_iterator = rh.begin_iterator; lh_iterator != lh.end_iterator; ++lh_iterator, ++rh_iterator) 2417 | { 2418 | if (*lh_iterator != *rh_iterator) return false; 2419 | } 2420 | 2421 | return true; 2422 | } 2423 | 2424 | 2425 | 2426 | friend bool operator != (const list &lh, const list &rh) PLF_NOEXCEPT 2427 | { 2428 | return !(lh == rh); 2429 | } 2430 | 2431 | 2432 | 2433 | #ifdef PLF_CPP20_SUPPORT 2434 | friend constexpr std::strong_ordering operator <=> (const list &lh, const list &rh) 2435 | { 2436 | return std::lexicographical_compare_three_way(lh.begin(), lh.end(), rh.begin(), rh.end()); 2437 | } 2438 | 2439 | 2440 | 2441 | [[nodiscard]] 2442 | #endif 2443 | bool empty() const PLF_NOEXCEPT 2444 | { 2445 | return total_size == 0; 2446 | } 2447 | 2448 | 2449 | 2450 | size_type size() const PLF_NOEXCEPT 2451 | { 2452 | return total_size; 2453 | } 2454 | 2455 | 2456 | 2457 | size_type max_size() const PLF_NOEXCEPT 2458 | { 2459 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT 2460 | return std::allocator_traits::max_size(*this); 2461 | #else 2462 | return allocator_type::max_size(); 2463 | #endif 2464 | } 2465 | 2466 | 2467 | 2468 | size_type capacity() const PLF_NOEXCEPT 2469 | { 2470 | return groups.node_pointer_allocator_pair.capacity; 2471 | } 2472 | 2473 | 2474 | 2475 | size_type memory() const PLF_NOEXCEPT 2476 | { 2477 | return sizeof(*this) + (groups.node_pointer_allocator_pair.capacity * sizeof(node)) + (groups.group_allocator_pair.capacity * sizeof(group)); 2478 | } 2479 | 2480 | 2481 | 2482 | private: 2483 | 2484 | 2485 | // Function-object to redirect the sort function to sort pointers by the elements they point to, not the pointer value 2486 | template 2487 | struct sort_dereferencer 2488 | { 2489 | comparison_function stored_instance; 2490 | 2491 | explicit sort_dereferencer(const comparison_function &function_instance) PLF_NOEXCEPT: 2492 | stored_instance(function_instance) 2493 | {} 2494 | 2495 | bool operator() (const node_pointer_type first, const node_pointer_type second) 2496 | { 2497 | return stored_instance(first->element, second->element); 2498 | } 2499 | }; 2500 | 2501 | 2502 | 2503 | public: 2504 | 2505 | 2506 | template 2507 | void sort(comparison_function compare) 2508 | { 2509 | if (total_size < 2) return; 2510 | 2511 | node_pointer_type * const node_pointers = PLF_ALLOCATE(node_pointer_allocator_type, groups.node_pointer_allocator_pair, total_size, NULL); 2512 | node_pointer_type *node_pointer = node_pointers; 2513 | 2514 | // According to the C++ standard, construction of a pointer (of any type) may not trigger an exception - hence, no try-catch blocks are necessary for constructing the pointers: 2515 | for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group) 2516 | { 2517 | const node_pointer_type end = current_group->beyond_end; 2518 | 2519 | if (end - current_group->nodes != current_group->number_of_elements) // If there are erased nodes present in the group 2520 | { 2521 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 2522 | { 2523 | if (current_node->next != NULL) // is not free list node 2524 | { 2525 | PLF_CONSTRUCT(node_pointer_allocator_type, groups.node_pointer_allocator_pair, node_pointer++, current_node); 2526 | } 2527 | } 2528 | } 2529 | else // If no erased nodes present we can avoid the per-node testing 2530 | { 2531 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 2532 | { 2533 | PLF_CONSTRUCT(node_pointer_allocator_type, groups.node_pointer_allocator_pair, node_pointer++, current_node); 2534 | } 2535 | } 2536 | } 2537 | 2538 | if (last_endpoint - groups.last_endpoint_group->nodes != groups.last_endpoint_group->number_of_elements) // If there are erased nodes present in the group 2539 | { 2540 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 2541 | { 2542 | if (current_node->next != NULL) 2543 | { 2544 | PLF_CONSTRUCT(node_pointer_allocator_type, groups.node_pointer_allocator_pair, node_pointer++, current_node); 2545 | } 2546 | } 2547 | } 2548 | else 2549 | { 2550 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 2551 | { 2552 | PLF_CONSTRUCT(node_pointer_allocator_type, groups.node_pointer_allocator_pair, node_pointer++, current_node); 2553 | } 2554 | } 2555 | 2556 | 2557 | #ifndef PLF_SORT_FUNCTION 2558 | std::sort(node_pointers, node_pointer, sort_dereferencer(compare)); 2559 | #else 2560 | PLF_SORT_FUNCTION(node_pointers, node_pointer, sort_dereferencer(compare)); 2561 | #endif 2562 | 2563 | 2564 | begin_iterator.node_pointer = node_pointers[0]; 2565 | begin_iterator.node_pointer->next = node_pointers[1]; 2566 | begin_iterator.node_pointer->previous = end_iterator.node_pointer; 2567 | 2568 | end_node.next = node_pointers[0]; 2569 | end_node.previous = node_pointers[total_size - 1]; 2570 | end_node.previous->next = end_iterator.node_pointer; 2571 | end_node.previous->previous = node_pointers[total_size - 2]; 2572 | 2573 | node_pointer_type * const back = node_pointers + total_size - 1; 2574 | 2575 | for(node_pointer = node_pointers + 1; node_pointer != back; ++node_pointer) 2576 | { 2577 | (*node_pointer)->next = *(node_pointer + 1); 2578 | (*node_pointer)->previous = *(node_pointer - 1); 2579 | 2580 | #ifdef PLF_TYPE_TRAITS_SUPPORT 2581 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 2582 | #endif 2583 | { 2584 | PLF_DESTROY(node_pointer_allocator_type, groups.node_pointer_allocator_pair, node_pointer - 1); 2585 | } 2586 | } 2587 | 2588 | #ifdef PLF_TYPE_TRAITS_SUPPORT 2589 | if PLF_CONSTEXPR (!std::is_trivially_destructible::value) 2590 | #endif 2591 | { 2592 | PLF_DESTROY(node_pointer_allocator_type, groups.node_pointer_allocator_pair, back); 2593 | } 2594 | 2595 | PLF_DEALLOCATE(node_pointer_allocator_type, groups.node_pointer_allocator_pair, node_pointers, total_size); 2596 | } 2597 | 2598 | 2599 | 2600 | void sort() 2601 | { 2602 | sort(plf::less()); 2603 | } 2604 | 2605 | 2606 | 2607 | void splice(const const_iterator position, const const_iterator first, const const_iterator last) PLF_NOEXCEPT // intra-list only splice functions - will crash if first & list are not from *this 2608 | { 2609 | if (position == last) return; 2610 | if (begin_iterator == first) begin_iterator.node_pointer = last.node_pointer; 2611 | 2612 | // To avoid pointer aliasing and subsequently increase performance via simultaneous assignments: 2613 | const node_pointer_type first_previous = first.node_pointer->previous; 2614 | const node_pointer_type last_previous = last.node_pointer->previous; 2615 | const node_pointer_type position_previous = position.node_pointer->previous; 2616 | 2617 | last.node_pointer->previous = first_previous; 2618 | first.node_pointer->previous->next = last.node_pointer; 2619 | 2620 | last_previous->next = position.node_pointer; 2621 | first.node_pointer->previous = position_previous; 2622 | 2623 | position_previous->next = first.node_pointer; 2624 | position.node_pointer->previous = last_previous; 2625 | 2626 | if (begin_iterator == position) begin_iterator.node_pointer = first.node_pointer; 2627 | } 2628 | 2629 | 2630 | 2631 | void splice(const const_iterator position, const const_iterator location) PLF_NOEXCEPT 2632 | { 2633 | splice(position, location, const_iterator(location.node_pointer->next)); 2634 | } 2635 | 2636 | 2637 | 2638 | private: 2639 | 2640 | void append_process(list &source) // used by merge and splice 2641 | { 2642 | node_allocator_pair.number_of_erased_nodes += source.node_allocator_pair.number_of_erased_nodes; 2643 | 2644 | if (last_endpoint != groups.last_endpoint_group->beyond_end) 2645 | { // Add unused nodes to group's free list 2646 | const node_pointer_type back_node = last_endpoint - 1; 2647 | for (node_pointer_type current_node = groups.last_endpoint_group->beyond_end - 1; current_node != back_node; --current_node) 2648 | { 2649 | current_node->next = NULL; 2650 | current_node->previous = groups.last_endpoint_group->free_list_head; 2651 | groups.last_endpoint_group->free_list_head = current_node; 2652 | } 2653 | 2654 | node_allocator_pair.number_of_erased_nodes += static_cast(groups.last_endpoint_group->beyond_end - last_endpoint); 2655 | } 2656 | 2657 | groups.append(source.groups); 2658 | last_endpoint = source.last_endpoint; 2659 | total_size += source.total_size; 2660 | source.blank(); 2661 | } 2662 | 2663 | 2664 | 2665 | 2666 | public: 2667 | 2668 | void splice(iterator position, list &source) 2669 | { 2670 | assert(&source != this); 2671 | 2672 | if (source.total_size == 0) 2673 | { 2674 | return; 2675 | } 2676 | else if (total_size == 0) 2677 | { 2678 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 2679 | *this = std::move(source); 2680 | #else 2681 | reset(); 2682 | swap(source); 2683 | #endif 2684 | 2685 | return; 2686 | } 2687 | 2688 | if (position.node_pointer == begin_iterator.node_pointer) // put source groups at front rather than back 2689 | { 2690 | swap(source); 2691 | position.node_pointer = end_iterator.node_pointer; 2692 | } 2693 | 2694 | position.node_pointer->previous->next = source.begin_iterator.node_pointer; 2695 | source.begin_iterator.node_pointer->previous = position.node_pointer->previous; 2696 | position.node_pointer->previous = source.end_node.previous; 2697 | source.end_node.previous->next = position.node_pointer; 2698 | 2699 | append_process(source); 2700 | } 2701 | 2702 | 2703 | 2704 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 2705 | void splice(iterator position, list &&source) 2706 | { 2707 | splice(position, source); 2708 | } 2709 | #endif 2710 | 2711 | 2712 | 2713 | template 2714 | void merge(list &source, comparison_function compare) 2715 | { 2716 | splice((source.total_size >= total_size) ? end_iterator : begin_iterator, source); 2717 | sort(compare); 2718 | } 2719 | 2720 | 2721 | 2722 | void merge(list &source) 2723 | { 2724 | assert(&source != this); 2725 | 2726 | if (source.total_size == 0) 2727 | { 2728 | return; 2729 | } 2730 | else if (total_size == 0) 2731 | { 2732 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 2733 | *this = std::move(source); 2734 | #else 2735 | reset(); 2736 | swap(source); 2737 | #endif 2738 | 2739 | return; 2740 | } 2741 | 2742 | node_pointer_type current1 = begin_iterator.node_pointer->next, current2 = source.begin_iterator.node_pointer->next; 2743 | node_pointer_type previous = source.begin_iterator.node_pointer; 2744 | const node_pointer_type source_end = source.end_iterator.node_pointer, this_end = end_iterator.node_pointer; 2745 | 2746 | begin_iterator.node_pointer->next = source.begin_iterator.node_pointer; 2747 | source.begin_iterator.node_pointer->previous = begin_iterator.node_pointer; 2748 | 2749 | 2750 | while ((current1 != this_end) & (current2 != source_end)) 2751 | { 2752 | previous->next = current1; 2753 | current1->previous = previous; 2754 | previous = current1; 2755 | current1 = current1->next; 2756 | 2757 | previous->next = current2; 2758 | current2->previous = previous; 2759 | previous = current2; 2760 | current2 = current2->next; 2761 | } 2762 | 2763 | if (current1 != this_end) 2764 | { 2765 | previous->next = current1; 2766 | current1->previous = previous; 2767 | } 2768 | else 2769 | { 2770 | end_node.previous = source.end_node.previous; 2771 | source.end_node.previous->next = end_iterator.node_pointer; 2772 | } 2773 | 2774 | append_process(source); 2775 | } 2776 | 2777 | 2778 | 2779 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 2780 | template 2781 | void merge(list &&source, comparison_function compare) 2782 | { 2783 | merge(source, compare); 2784 | } 2785 | 2786 | 2787 | 2788 | void merge(list &&source) 2789 | { 2790 | merge(source); 2791 | } 2792 | #endif 2793 | 2794 | 2795 | 2796 | void reverse() PLF_NOEXCEPT 2797 | { 2798 | // Note: current_node->next has to be read during swapping in this process anyway, so including a test to figure out whether or not a given group has erased elements within it, and thus avoid per-node tests, is actually detrimental to performance according to benchmarks. This is unlike sort() where current_node->next is not used in the rest of the process and avoiding per-node tests of it's value is therefore beneficial in benchmarks. 2799 | if (total_size > 1) 2800 | { 2801 | for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group) 2802 | { 2803 | const node_pointer_type end = current_group->beyond_end; 2804 | 2805 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 2806 | { 2807 | if (current_node->next != NULL) /* ie. is not free list node */ std::swap(current_node->next, current_node->previous); 2808 | } 2809 | } 2810 | 2811 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 2812 | { 2813 | if (current_node->next != NULL) std::swap(current_node->next, current_node->previous); 2814 | } 2815 | 2816 | std::swap(end_node.previous, begin_iterator.node_pointer); 2817 | end_node.previous->next = end_iterator.node_pointer; 2818 | begin_iterator.node_pointer->previous = end_iterator.node_pointer; 2819 | } 2820 | } 2821 | 2822 | 2823 | 2824 | private: 2825 | 2826 | // Used by unique() 2827 | struct eq 2828 | { 2829 | bool operator() (const element_type &a, const element_type &b) const PLF_NOEXCEPT 2830 | { 2831 | return a == b; 2832 | } 2833 | }; 2834 | 2835 | 2836 | 2837 | public: 2838 | 2839 | template 2840 | size_type unique(comparison_function compare) 2841 | { 2842 | const size_type original_number_of_elements = total_size; 2843 | 2844 | if (original_number_of_elements > 1) 2845 | { 2846 | element_type *previous = &(begin_iterator.node_pointer->element); 2847 | 2848 | for (iterator current = ++iterator(begin_iterator); current != end_iterator;) 2849 | { 2850 | if (compare(*current, *previous)) 2851 | { 2852 | current = erase(current); 2853 | } 2854 | else 2855 | { 2856 | previous = &(current++.node_pointer->element); 2857 | } 2858 | } 2859 | } 2860 | 2861 | return original_number_of_elements - total_size; 2862 | } 2863 | 2864 | 2865 | 2866 | size_type unique() 2867 | { 2868 | return unique(eq()); 2869 | } 2870 | 2871 | 2872 | 2873 | template 2874 | size_type remove_if(predicate_function predicate) 2875 | { 2876 | const size_type original_number_of_elements = total_size; 2877 | 2878 | if (original_number_of_elements != 0) 2879 | { 2880 | for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group) 2881 | { 2882 | group_size_type num_elements = current_group->number_of_elements; 2883 | const node_pointer_type end = current_group->beyond_end; 2884 | 2885 | if (end - current_group->nodes != num_elements) // If there are erased nodes present in the group 2886 | { 2887 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 2888 | { 2889 | if (current_node->next != NULL && predicate(current_node->element)) // is not free list node and validates predicate 2890 | { 2891 | erase(current_node); 2892 | 2893 | if (--num_elements == 0) // ie. group will be empty (and removed) now - nothing left to iterate over 2894 | { 2895 | --current_group; // As current group has been removed, subsequent groups have already shifted back by one, hence, the ++ to the current group in the for loop is unnecessary, and negated here 2896 | break; 2897 | } 2898 | } 2899 | } 2900 | } 2901 | else // No erased nodes in group 2902 | { 2903 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 2904 | { 2905 | if (predicate(current_node->element)) 2906 | { 2907 | erase(current_node); 2908 | 2909 | if (--num_elements == 0) 2910 | { 2911 | --current_group; 2912 | break; 2913 | } 2914 | } 2915 | } 2916 | } 2917 | } 2918 | 2919 | group_size_type num_elements = groups.last_endpoint_group->number_of_elements; 2920 | 2921 | if (last_endpoint - groups.last_endpoint_group->nodes != num_elements) // If there are erased nodes present in the group 2922 | { 2923 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 2924 | { 2925 | if (current_node->next != NULL && predicate(current_node->element)) 2926 | { 2927 | erase(current_node); 2928 | if (--num_elements == 0) break; 2929 | } 2930 | } 2931 | } 2932 | else 2933 | { 2934 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 2935 | { 2936 | if (predicate(current_node->element)) 2937 | { 2938 | erase(current_node); 2939 | if (--num_elements == 0) break; 2940 | } 2941 | } 2942 | } 2943 | } 2944 | 2945 | return original_number_of_elements - total_size; 2946 | } 2947 | 2948 | 2949 | 2950 | size_type remove(const element_type &value) 2951 | { 2952 | return remove_if(plf::equal_to(value)); 2953 | } 2954 | 2955 | 2956 | 2957 | void resize(const size_type number_of_elements, const element_type &value = element_type()) 2958 | { 2959 | if (total_size == number_of_elements) 2960 | { 2961 | return; 2962 | } 2963 | else if (number_of_elements == 0) 2964 | { 2965 | clear(); 2966 | return; 2967 | } 2968 | else if (total_size < number_of_elements) 2969 | { 2970 | insert(end_iterator, number_of_elements - total_size, value); 2971 | } 2972 | else // ie. total_size > number_of_elements 2973 | { 2974 | const_iterator current(end_node.previous); 2975 | 2976 | for (size_type number_to_remove = total_size - number_of_elements; number_to_remove != 0; --number_to_remove) 2977 | { 2978 | const node_pointer_type temp = current.node_pointer->previous; 2979 | erase(current); 2980 | current.node_pointer = temp; 2981 | } 2982 | } 2983 | } 2984 | 2985 | 2986 | 2987 | void reserve(size_type reserve_amount) 2988 | { 2989 | if (reserve_amount == 0 || reserve_amount <= groups.node_pointer_allocator_pair.capacity) 2990 | { 2991 | return; 2992 | } 2993 | else if (reserve_amount < list_min_block_capacity()) 2994 | { 2995 | reserve_amount = list_min_block_capacity(); 2996 | } 2997 | else if (reserve_amount > max_size()) 2998 | { 2999 | #ifdef PLF_EXCEPTIONS_SUPPORT 3000 | throw std::length_error("Capacity requested via reserve() greater than max_size()"); 3001 | #else 3002 | std::terminate(); 3003 | #endif 3004 | } 3005 | 3006 | 3007 | if (groups.block_pointer != NULL && total_size == 0) 3008 | { // edge case: has been filled with elements then clear()'d - some groups may be smaller than would be desired, should be replaced 3009 | group_size_type end_group_size = static_cast((groups.block_pointer + groups.size - 1)->beyond_end - (groups.block_pointer + groups.size - 1)->nodes); 3010 | 3011 | if (reserve_amount > end_group_size && end_group_size != list_max_block_capacity()) // if last group isn't large enough, remove all groups 3012 | { 3013 | reset(); 3014 | } 3015 | else 3016 | { 3017 | size_type number_of_full_groups_needed = reserve_amount / list_max_block_capacity(); 3018 | group_size_type remainder = static_cast(reserve_amount - (number_of_full_groups_needed * list_max_block_capacity())); 3019 | 3020 | // Remove any max_size groups which're not needed and any groups that're smaller than remainder: 3021 | for (group_pointer_type current_group = groups.block_pointer; current_group < groups.block_pointer + groups.size;) // note: groups.size may change during procedure so we calculate this every loop 3022 | { 3023 | const group_size_type current_group_size = static_cast(groups.block_pointer->beyond_end - groups.block_pointer->nodes); 3024 | 3025 | if (number_of_full_groups_needed != 0 && current_group_size == list_max_block_capacity()) 3026 | { 3027 | --number_of_full_groups_needed; 3028 | ++current_group; 3029 | } 3030 | else if (remainder != 0 && current_group_size >= remainder) 3031 | { 3032 | remainder = 0; 3033 | ++current_group; 3034 | } 3035 | else 3036 | { 3037 | groups.remove(current_group); 3038 | } 3039 | } 3040 | 3041 | last_endpoint = groups.block_pointer->nodes; 3042 | } 3043 | } 3044 | 3045 | reserve_amount -= groups.node_pointer_allocator_pair.capacity; 3046 | 3047 | // To correct from possible reallocation caused by add_new: 3048 | const difference_type last_endpoint_group_number = groups.last_endpoint_group - groups.block_pointer; 3049 | 3050 | size_type number_of_full_groups = reserve_amount / list_max_block_capacity(); 3051 | reserve_amount -= (number_of_full_groups++ * list_max_block_capacity()); // ++ to aid while loop below 3052 | 3053 | if (groups.block_pointer == NULL) // Previously uninitialized list or reset in above if statement; most common scenario 3054 | { 3055 | if (reserve_amount != 0) 3056 | { 3057 | groups.initialize(static_cast(((reserve_amount < list_min_block_capacity()) ? list_min_block_capacity() : reserve_amount))); 3058 | } 3059 | else 3060 | { 3061 | groups.initialize(list_max_block_capacity()); 3062 | --number_of_full_groups; 3063 | } 3064 | } 3065 | else if (reserve_amount != 0) 3066 | { // Create a group at least as large as the last group - may allocate more than necessary, but better solution than creating a very small group in the middle of the group vector, I think: 3067 | const group_size_type last_endpoint_group_capacity = static_cast(groups.last_endpoint_group->beyond_end - groups.last_endpoint_group->nodes); 3068 | groups.add_new(static_cast((reserve_amount < last_endpoint_group_capacity) ? last_endpoint_group_capacity : reserve_amount)); 3069 | } 3070 | 3071 | while (--number_of_full_groups != 0) 3072 | { 3073 | groups.add_new(list_max_block_capacity()); 3074 | } 3075 | 3076 | groups.last_endpoint_group = groups.block_pointer + last_endpoint_group_number; 3077 | } 3078 | 3079 | 3080 | 3081 | void trim_capacity() PLF_NOEXCEPT 3082 | { 3083 | groups.trim_unused_groups(); 3084 | } 3085 | 3086 | 3087 | 3088 | void shrink_to_fit() 3089 | { 3090 | if ((groups.block_pointer == NULL) | (total_size == groups.node_pointer_allocator_pair.capacity)) // if list is uninitialized or full 3091 | { 3092 | return; 3093 | } 3094 | else if (total_size == 0) // Edge case 3095 | { 3096 | reset(); 3097 | return; 3098 | } 3099 | else if (node_allocator_pair.number_of_erased_nodes == 0 && last_endpoint == groups.last_endpoint_group->beyond_end) //edge case - currently no wasted space except for possible trailing groups 3100 | { 3101 | groups.trim_unused_groups(); 3102 | return; 3103 | } 3104 | 3105 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 3106 | list temp; 3107 | 3108 | #ifdef PLF_TYPE_TRAITS_SUPPORT 3109 | if PLF_CONSTEXPR (std::is_move_assignable::value && std::is_move_constructible::value) // move elements if possible, otherwise copy them 3110 | { 3111 | temp.range_insert(temp.end_iterator, total_size, plf::make_move_iterator(begin_iterator)); 3112 | } 3113 | else 3114 | #endif 3115 | { 3116 | temp.range_insert(temp.end_iterator, total_size, begin_iterator); 3117 | } 3118 | 3119 | *this = std::move(temp); 3120 | #else 3121 | list temp(*this); 3122 | reset(); 3123 | swap(temp); 3124 | #endif 3125 | } 3126 | 3127 | 3128 | 3129 | // Range assign: 3130 | 3131 | private: 3132 | 3133 | 3134 | template 3135 | void range_assign(iterator_type it, size_type range_size) 3136 | { 3137 | if (range_size == 0) 3138 | { 3139 | clear(); 3140 | return; 3141 | } 3142 | 3143 | #ifdef PLF_TYPE_TRAITS_SUPPORT 3144 | if PLF_CONSTEXPR ((std::is_trivially_destructible::value && std::is_trivially_constructible::value && std::is_trivially_copy_assignable::value) || !std::is_copy_assignable::value) // ie. If there is no benefit nor difference to assigning vs constructing, or if we can't assign 3145 | { 3146 | clear(); 3147 | range_insert(end_iterator, range_size, it); 3148 | } 3149 | else 3150 | #endif 3151 | { 3152 | if (total_size == 0) 3153 | { 3154 | range_insert(end_iterator, range_size, it); 3155 | } 3156 | else if (range_size < total_size) 3157 | { 3158 | iterator current = begin_iterator; 3159 | 3160 | do 3161 | { 3162 | *current++ = *it++; 3163 | } while (--range_size != 0); 3164 | 3165 | erase(current, end_iterator); 3166 | } 3167 | else 3168 | { 3169 | iterator current = begin_iterator; 3170 | 3171 | do 3172 | { 3173 | *current = *it++; 3174 | } while (++current != end_iterator); 3175 | 3176 | range_insert(end_iterator, range_size - total_size, it); 3177 | } 3178 | } 3179 | 3180 | groups.trim_unused_groups(); 3181 | } 3182 | 3183 | 3184 | 3185 | 3186 | public: 3187 | 3188 | 3189 | template 3190 | void assign(typename plf::enable_if::is_integer, iterator_type>::type first, const iterator_type last) 3191 | { 3192 | range_assign(first, std::distance(first, last)); 3193 | } 3194 | 3195 | 3196 | 3197 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 3198 | template 3199 | void insert (const std::move_iterator first, const std::move_iterator last) 3200 | { 3201 | range_assign(first, static_cast(std::distance(first.base(),last.base()))); 3202 | } 3203 | #endif 3204 | 3205 | 3206 | 3207 | // Fill assign: 3208 | 3209 | void assign(size_type number_of_elements, const element_type &value) 3210 | { 3211 | if (number_of_elements == 0) 3212 | { 3213 | clear(); 3214 | return; 3215 | } 3216 | 3217 | #ifdef PLF_TYPE_TRAITS_SUPPORT 3218 | if PLF_CONSTEXPR ((std::is_trivially_destructible::value && std::is_trivially_constructible::value && std::is_trivially_copy_assignable::value) || !std::is_copy_assignable::value) 3219 | { 3220 | clear(); 3221 | insert(end_iterator, number_of_elements, value); 3222 | } 3223 | #endif 3224 | { 3225 | if (total_size == 0) 3226 | { 3227 | insert(end_iterator, number_of_elements, value); 3228 | } 3229 | else if (number_of_elements < total_size) 3230 | { 3231 | iterator current = begin_iterator; 3232 | 3233 | do 3234 | { 3235 | *current++ = value; 3236 | } while (--number_of_elements != 0); 3237 | 3238 | erase(current, end_iterator); 3239 | } 3240 | else 3241 | { 3242 | iterator current = begin_iterator; 3243 | 3244 | do 3245 | { 3246 | *current = value; 3247 | } while (++current != end_iterator); 3248 | 3249 | insert(end_iterator, number_of_elements - total_size, value); 3250 | } 3251 | } 3252 | 3253 | groups.trim_unused_groups(); 3254 | } 3255 | 3256 | 3257 | 3258 | #ifdef PLF_INITIALIZER_LIST_SUPPORT 3259 | // Initializer-list assign: 3260 | 3261 | void assign(const std::initializer_list &element_list) 3262 | { 3263 | range_assign(element_list.begin(), static_cast(element_list.size())); 3264 | } 3265 | #endif 3266 | 3267 | 3268 | 3269 | #ifdef PLF_CPP20_SUPPORT 3270 | template 3271 | requires std::ranges::range 3272 | void assign_range(range_type &&the_range) 3273 | { 3274 | range_assign(std::ranges::begin(the_range), static_cast(std::ranges::distance(the_range))); 3275 | } 3276 | #endif 3277 | 3278 | 3279 | 3280 | allocator_type get_allocator() const PLF_NOEXCEPT 3281 | { 3282 | return allocator_type(); 3283 | } 3284 | 3285 | 3286 | 3287 | template 3288 | iterator unordered_find_single(predicate_function predicate) const PLF_NOEXCEPT 3289 | { 3290 | if (total_size != 0) 3291 | { 3292 | for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group) 3293 | { 3294 | const node_pointer_type end = current_group->beyond_end; 3295 | 3296 | if (end - current_group->nodes != current_group->number_of_elements) // If there are erased nodes present in the group 3297 | { 3298 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 3299 | { 3300 | if (current_node->next != NULL && predicate(current_node->element)) return iterator(current_node); // is not free list node and matches element 3301 | } 3302 | } 3303 | else // No erased nodes in group 3304 | { 3305 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 3306 | { 3307 | if (predicate(current_node->element)) return iterator(current_node); 3308 | } 3309 | } 3310 | } 3311 | 3312 | if (last_endpoint - groups.last_endpoint_group->nodes != groups.last_endpoint_group->number_of_elements) 3313 | { 3314 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 3315 | { 3316 | if (current_node->next != NULL && predicate(current_node->element)) return iterator(current_node); 3317 | } 3318 | } 3319 | else 3320 | { 3321 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 3322 | { 3323 | if (predicate(current_node->element)) return iterator(current_node); 3324 | } 3325 | } 3326 | } 3327 | 3328 | return end_iterator; 3329 | } 3330 | 3331 | 3332 | 3333 | iterator unordered_find_single(const element_type &element_to_match) const PLF_NOEXCEPT 3334 | { 3335 | return unordered_find_single(plf::equal_to(element_to_match)); 3336 | } 3337 | 3338 | 3339 | 3340 | template 3341 | list unordered_find_multiple(predicate_function predicate, size_type number_to_find) const 3342 | { 3343 | list return_list; 3344 | 3345 | if (total_size != 0) 3346 | { 3347 | for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group) 3348 | { 3349 | const node_pointer_type end = current_group->beyond_end; 3350 | 3351 | if (end - current_group->nodes != current_group->number_of_elements) 3352 | { 3353 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 3354 | { 3355 | if (current_node->next != NULL && predicate(current_node->element)) 3356 | { 3357 | return_list.push_back(iterator(current_node)); 3358 | if (--number_to_find == 0) return return_list; 3359 | } 3360 | } 3361 | } 3362 | else // No erased nodes in group 3363 | { 3364 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 3365 | { 3366 | if (predicate(current_node->element)) 3367 | { 3368 | return_list.push_back(iterator(current_node)); 3369 | if (--number_to_find == 0) return return_list; 3370 | } 3371 | } 3372 | } 3373 | } 3374 | 3375 | if (last_endpoint - groups.last_endpoint_group->nodes != groups.last_endpoint_group->number_of_elements) 3376 | { 3377 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 3378 | { 3379 | if (current_node->next != NULL && predicate(current_node->element)) 3380 | { 3381 | return_list.push_back(iterator(current_node)); 3382 | if (--number_to_find == 0) return return_list; 3383 | } 3384 | } 3385 | } 3386 | else 3387 | { 3388 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 3389 | { 3390 | if (predicate(current_node->element)) 3391 | { 3392 | return_list.push_back(iterator(current_node)); 3393 | if (--number_to_find == 0) return return_list; 3394 | } 3395 | } 3396 | } 3397 | } 3398 | 3399 | return return_list; 3400 | } 3401 | 3402 | 3403 | 3404 | list unordered_find_multiple(const element_type &element_to_match, size_type number_to_find) const 3405 | { 3406 | return unordered_find_multiple(plf::equal_to(element_to_match), number_to_find); 3407 | } 3408 | 3409 | 3410 | 3411 | template 3412 | list unordered_find_all(predicate_function predicate) const 3413 | { 3414 | list return_list; 3415 | 3416 | if (total_size != 0) 3417 | { 3418 | for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group) 3419 | { 3420 | const node_pointer_type end = current_group->beyond_end; 3421 | 3422 | if (end - current_group->nodes != current_group->number_of_elements) 3423 | { 3424 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 3425 | { 3426 | if (current_node->next != NULL && predicate(current_node->element)) return_list.push_back(iterator(current_node)); 3427 | } 3428 | } 3429 | else // No erased nodes in group 3430 | { 3431 | for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node) 3432 | { 3433 | if (predicate(current_node->element)) return_list.push_back(iterator(current_node)); 3434 | } 3435 | } 3436 | } 3437 | 3438 | if (last_endpoint - groups.last_endpoint_group->nodes != groups.last_endpoint_group->number_of_elements) 3439 | { 3440 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 3441 | { 3442 | if (current_node->next != NULL && predicate(current_node->element)) return_list.push_back(iterator(current_node)); 3443 | } 3444 | } 3445 | else 3446 | { 3447 | for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node) 3448 | { 3449 | if (predicate(current_node->element)) return_list.push_back(iterator(current_node)); 3450 | } 3451 | } 3452 | } 3453 | 3454 | return return_list; 3455 | } 3456 | 3457 | 3458 | 3459 | list unordered_find_all(const element_type &element_to_match) const 3460 | { 3461 | return unordered_find_all(plf::equal_to(element_to_match)); 3462 | } 3463 | 3464 | 3465 | 3466 | void swap(list &source) PLF_NOEXCEPT_SWAP(allocator_type) 3467 | { 3468 | #if defined(PLF_TYPE_TRAITS_SUPPORT) && defined(PLF_MOVE_SEMANTICS_SUPPORT) 3469 | if PLF_CONSTEXPR (std::is_move_assignable::value && std::is_move_assignable::value && std::is_move_constructible::value && std::is_move_constructible::value) 3470 | { 3471 | list temp(std::move(source)); 3472 | source = std::move(*this); 3473 | *this = std::move(temp); 3474 | } 3475 | else 3476 | #endif 3477 | { 3478 | groups.swap(source.groups); 3479 | 3480 | const node_pointer_type swap_end_node_previous = end_node.previous, swap_last_endpoint = last_endpoint; 3481 | const iterator swap_begin_iterator = begin_iterator; 3482 | const size_type swap_total_size = total_size, swap_number_of_erased_nodes = node_allocator_pair.number_of_erased_nodes; 3483 | 3484 | last_endpoint = source.last_endpoint; 3485 | end_node.next = begin_iterator.node_pointer = (source.begin_iterator.node_pointer != source.end_iterator.node_pointer) ? source.begin_iterator.node_pointer : end_iterator.node_pointer; 3486 | end_node.previous = (source.begin_iterator.node_pointer != source.end_iterator.node_pointer) ? source.end_node.previous : end_iterator.node_pointer; 3487 | end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer; 3488 | total_size = source.total_size; 3489 | node_allocator_pair.number_of_erased_nodes = source.node_allocator_pair.number_of_erased_nodes; 3490 | 3491 | source.last_endpoint = swap_last_endpoint; 3492 | source.end_node.next = source.begin_iterator.node_pointer = (swap_begin_iterator.node_pointer != end_iterator.node_pointer) ? swap_begin_iterator.node_pointer : source.end_iterator.node_pointer; 3493 | source.end_node.previous = (swap_begin_iterator.node_pointer != end_iterator.node_pointer) ? swap_end_node_previous : source.end_iterator.node_pointer; 3494 | source.end_node.previous->next = source.begin_iterator.node_pointer->previous = source.end_iterator.node_pointer; 3495 | source.total_size = swap_total_size; 3496 | source.node_allocator_pair.number_of_erased_nodes = swap_number_of_erased_nodes; 3497 | 3498 | #ifdef PLF_IS_ALWAYS_EQUAL_SUPPORT 3499 | if PLF_CONSTEXPR (std::allocator_traits::propagate_on_container_swap::value && !std::allocator_traits::is_always_equal::value) 3500 | #endif 3501 | { 3502 | std::swap(static_cast(source), static_cast(*this)); 3503 | 3504 | // Reconstruct rebinds for swapped allocators: 3505 | static_cast(node_allocator_pair) = node_allocator_type(*this); 3506 | static_cast(source.node_allocator_pair) = node_allocator_type(source); 3507 | } // else: undefined behaviour, as per standard 3508 | } 3509 | } 3510 | 3511 | 3512 | 3513 | // Iterators: 3514 | 3515 | template class list_iterator 3516 | { 3517 | private: 3518 | typedef typename list::node_pointer_type node_pointer_type; 3519 | node_pointer_type node_pointer; 3520 | 3521 | public: 3522 | struct list_iterator_tag {}; 3523 | typedef std::bidirectional_iterator_tag iterator_category; 3524 | typedef std::bidirectional_iterator_tag iterator_concept; 3525 | typedef typename list::value_type value_type; 3526 | typedef typename list::difference_type difference_type; 3527 | typedef list_reverse_iterator reverse_type; 3528 | typedef typename plf::conditional::type pointer; 3529 | typedef typename plf::conditional::type reference; 3530 | 3531 | friend class list; 3532 | 3533 | 3534 | 3535 | bool operator == (const list_iterator rh) const PLF_NOEXCEPT 3536 | { 3537 | return (node_pointer == rh.node_pointer); 3538 | } 3539 | 3540 | 3541 | 3542 | bool operator == (const list_iterator rh) const PLF_NOEXCEPT 3543 | { 3544 | return (node_pointer == rh.node_pointer); 3545 | } 3546 | 3547 | 3548 | 3549 | bool operator != (const list_iterator rh) const PLF_NOEXCEPT 3550 | { 3551 | return (node_pointer != rh.node_pointer); 3552 | } 3553 | 3554 | 3555 | 3556 | bool operator != (const list_iterator rh) const PLF_NOEXCEPT 3557 | { 3558 | return (node_pointer != rh.node_pointer); 3559 | } 3560 | 3561 | 3562 | 3563 | reference operator * () const 3564 | { 3565 | return node_pointer->element; 3566 | } 3567 | 3568 | 3569 | 3570 | pointer operator -> () const 3571 | { 3572 | return &(node_pointer->element); 3573 | } 3574 | 3575 | 3576 | 3577 | list_iterator & operator ++ () PLF_NOEXCEPT 3578 | { 3579 | assert(node_pointer != NULL); // covers uninitialised list_iterator 3580 | node_pointer = node_pointer->next; 3581 | return *this; 3582 | } 3583 | 3584 | 3585 | 3586 | list_iterator operator ++(int) PLF_NOEXCEPT 3587 | { 3588 | const list_iterator copy(*this); 3589 | ++*this; 3590 | return copy; 3591 | } 3592 | 3593 | 3594 | 3595 | list_iterator & operator -- () PLF_NOEXCEPT 3596 | { 3597 | assert(node_pointer != NULL); 3598 | node_pointer = node_pointer->previous; 3599 | return *this; 3600 | } 3601 | 3602 | 3603 | 3604 | list_iterator operator -- (int) PLF_NOEXCEPT 3605 | { 3606 | const list_iterator copy(*this); 3607 | --*this; 3608 | return copy; 3609 | } 3610 | 3611 | 3612 | 3613 | list_iterator() PLF_NOEXCEPT: node_pointer(NULL) {} 3614 | 3615 | 3616 | 3617 | list_iterator(const list_iterator &source) PLF_NOEXCEPT: node_pointer(source.node_pointer) {} 3618 | 3619 | 3620 | 3621 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3622 | template ::type > 3623 | list_iterator(const list_iterator &source) PLF_NOEXCEPT: node_pointer(source.node_pointer) {} 3624 | #else 3625 | list_iterator(const list_iterator &source) PLF_NOEXCEPT: node_pointer(source.node_pointer) {} 3626 | #endif 3627 | 3628 | 3629 | 3630 | list_iterator & operator = (const list_iterator &rh) PLF_NOEXCEPT 3631 | { 3632 | node_pointer = rh.node_pointer; 3633 | return *this; 3634 | } 3635 | 3636 | 3637 | 3638 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3639 | template ::type > 3640 | list_iterator & operator = (const list_iterator &rh) PLF_NOEXCEPT 3641 | #else 3642 | list_iterator & operator = (const list_iterator &rh) PLF_NOEXCEPT 3643 | #endif 3644 | { 3645 | node_pointer = rh.node_pointer; 3646 | return *this; 3647 | } 3648 | 3649 | 3650 | 3651 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 3652 | list_iterator (list_iterator &&source) PLF_NOEXCEPT: node_pointer(std::move(source.node_pointer)) {} 3653 | 3654 | 3655 | 3656 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3657 | template ::type > 3658 | list_iterator(list_iterator &&source) PLF_NOEXCEPT: node_pointer(std::move(source.node_pointer)) {} 3659 | #else 3660 | list_iterator(list_iterator &&source) PLF_NOEXCEPT: node_pointer(std::move(source.node_pointer)) {} 3661 | #endif 3662 | 3663 | 3664 | 3665 | list_iterator & operator = (list_iterator &&rh) PLF_NOEXCEPT 3666 | { 3667 | assert (&rh != this); 3668 | node_pointer = std::move(rh.node_pointer); 3669 | return *this; 3670 | } 3671 | 3672 | 3673 | 3674 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3675 | template ::type > 3676 | list_iterator & operator = (list_iterator &&rh) PLF_NOEXCEPT 3677 | #else 3678 | list_iterator & operator = (list_iterator &&rh) PLF_NOEXCEPT 3679 | #endif 3680 | { 3681 | node_pointer = std::move(rh.node_pointer); 3682 | return *this; 3683 | } 3684 | #endif 3685 | 3686 | 3687 | 3688 | private: 3689 | 3690 | list_iterator (const node_pointer_type node_p) PLF_NOEXCEPT: node_pointer(node_p) {} 3691 | }; 3692 | 3693 | 3694 | 3695 | 3696 | template class list_reverse_iterator 3697 | { 3698 | private: 3699 | typedef typename list::node_pointer_type node_pointer_type; 3700 | node_pointer_type node_pointer; 3701 | 3702 | public: 3703 | typedef std::bidirectional_iterator_tag iterator_concept; 3704 | typedef std::bidirectional_iterator_tag iterator_category; 3705 | typedef typename list::value_type value_type; 3706 | typedef typename list::difference_type difference_type; 3707 | typedef typename plf::conditional::type pointer; 3708 | typedef typename plf::conditional::type reference; 3709 | 3710 | friend class list; 3711 | 3712 | 3713 | bool operator == (const list_reverse_iterator rh) const PLF_NOEXCEPT 3714 | { 3715 | return (node_pointer == rh.node_pointer); 3716 | } 3717 | 3718 | 3719 | 3720 | bool operator == (const list_reverse_iterator rh) const PLF_NOEXCEPT 3721 | { 3722 | return (node_pointer == rh.node_pointer); 3723 | } 3724 | 3725 | 3726 | 3727 | bool operator != (const list_reverse_iterator rh) const PLF_NOEXCEPT 3728 | { 3729 | return (node_pointer != rh.node_pointer); 3730 | } 3731 | 3732 | 3733 | 3734 | bool operator != (const list_reverse_iterator rh) const PLF_NOEXCEPT 3735 | { 3736 | return (node_pointer != rh.node_pointer); 3737 | } 3738 | 3739 | 3740 | 3741 | reference operator * () const 3742 | { 3743 | return node_pointer->element; 3744 | } 3745 | 3746 | 3747 | 3748 | pointer operator -> () const 3749 | { 3750 | return &(node_pointer->element); 3751 | } 3752 | 3753 | 3754 | 3755 | list_reverse_iterator & operator ++ () PLF_NOEXCEPT 3756 | { 3757 | assert(node_pointer != NULL); 3758 | node_pointer = node_pointer->previous; 3759 | return *this; 3760 | } 3761 | 3762 | 3763 | 3764 | list_reverse_iterator operator ++(int) PLF_NOEXCEPT 3765 | { 3766 | const list_reverse_iterator copy(*this); 3767 | ++*this; 3768 | return copy; 3769 | } 3770 | 3771 | 3772 | 3773 | list_reverse_iterator & operator -- () PLF_NOEXCEPT 3774 | { 3775 | assert(node_pointer != NULL); 3776 | node_pointer = node_pointer->next; 3777 | return *this; 3778 | } 3779 | 3780 | 3781 | 3782 | list_reverse_iterator operator -- (int) PLF_NOEXCEPT 3783 | { 3784 | const list_reverse_iterator copy(*this); 3785 | --*this; 3786 | return copy; 3787 | } 3788 | 3789 | 3790 | 3791 | typename list::iterator base() const PLF_NOEXCEPT 3792 | { 3793 | return typename list::iterator(node_pointer->next); 3794 | } 3795 | 3796 | 3797 | 3798 | list_reverse_iterator() PLF_NOEXCEPT: node_pointer(NULL) {} 3799 | 3800 | 3801 | 3802 | list_reverse_iterator(const list_reverse_iterator &source) PLF_NOEXCEPT: node_pointer(source.node_pointer) {} 3803 | 3804 | 3805 | 3806 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3807 | template ::type > 3808 | list_reverse_iterator (const list_reverse_iterator &source) PLF_NOEXCEPT: 3809 | #else 3810 | list_reverse_iterator (const list_reverse_iterator &source) PLF_NOEXCEPT: 3811 | #endif 3812 | node_pointer(source.node_pointer) 3813 | {} 3814 | 3815 | 3816 | 3817 | list_reverse_iterator& operator = (const list_reverse_iterator &source) PLF_NOEXCEPT 3818 | { 3819 | node_pointer = source.node_pointer; 3820 | return *this; 3821 | } 3822 | 3823 | 3824 | 3825 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3826 | template ::type > 3827 | list_reverse_iterator& operator = (const list_reverse_iterator &source) PLF_NOEXCEPT 3828 | #else 3829 | list_reverse_iterator& operator = (const list_reverse_iterator &source) PLF_NOEXCEPT 3830 | #endif 3831 | { 3832 | node_pointer = source.node_pointer; 3833 | return *this; 3834 | } 3835 | 3836 | 3837 | 3838 | list_reverse_iterator (const list_iterator &source) PLF_NOEXCEPT: 3839 | node_pointer(source.node_pointer->previous) 3840 | {} 3841 | 3842 | 3843 | 3844 | list_reverse_iterator& operator = (const list_iterator &source) PLF_NOEXCEPT 3845 | { 3846 | node_pointer = source.node_pointer->previous; 3847 | return *this; 3848 | } 3849 | 3850 | 3851 | 3852 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3853 | template ::type > 3854 | list_reverse_iterator& operator = (const list_iterator &source) PLF_NOEXCEPT 3855 | #else 3856 | list_reverse_iterator& operator = (const list_iterator &source) PLF_NOEXCEPT 3857 | #endif 3858 | { 3859 | node_pointer = source.node_pointer->previous; 3860 | return *this; 3861 | } 3862 | 3863 | 3864 | 3865 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT 3866 | list_reverse_iterator (list_reverse_iterator &&source) PLF_NOEXCEPT: 3867 | node_pointer(std::move(source.node_pointer)) 3868 | {} 3869 | 3870 | 3871 | 3872 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3873 | template ::type > 3874 | list_reverse_iterator (list_reverse_iterator &&source) PLF_NOEXCEPT: 3875 | #else 3876 | list_reverse_iterator (list_reverse_iterator &&source) PLF_NOEXCEPT: 3877 | #endif 3878 | node_pointer(std::move(source.node_pointer)) 3879 | {} 3880 | 3881 | 3882 | 3883 | list_reverse_iterator& operator = (list_reverse_iterator &&source) PLF_NOEXCEPT 3884 | { 3885 | assert (&source != this); 3886 | node_pointer = std::move(source.node_pointer); 3887 | return *this; 3888 | } 3889 | 3890 | 3891 | 3892 | #ifdef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3893 | template ::type > 3894 | list_reverse_iterator& operator = (list_reverse_iterator &&source) PLF_NOEXCEPT 3895 | #else 3896 | list_reverse_iterator& operator = (list_reverse_iterator &&source) PLF_NOEXCEPT 3897 | #endif 3898 | { 3899 | assert (&source != this); 3900 | node_pointer = std::move(source.node_pointer); 3901 | return *this; 3902 | } 3903 | #endif 3904 | 3905 | 3906 | 3907 | private: 3908 | 3909 | list_reverse_iterator (const node_pointer_type node_p) PLF_NOEXCEPT: node_pointer(node_p) {} 3910 | }; 3911 | 3912 | 3913 | 3914 | }; // end of plf::list 3915 | 3916 | 3917 | } // plf namespace 3918 | 3919 | 3920 | namespace std 3921 | { 3922 | template 3923 | void swap(plf::list &a, plf::list &b) PLF_NOEXCEPT_SWAP(allocator_type) 3924 | { 3925 | a.swap(b); 3926 | } 3927 | 3928 | 3929 | 3930 | template 3931 | typename plf::list::size_type erase_if(plf::list &container, predicate_function predicate) 3932 | { 3933 | return container.remove_if(predicate); 3934 | } 3935 | 3936 | 3937 | 3938 | template 3939 | typename plf::list::size_type erase(plf::list &container, const element_type &value) 3940 | { 3941 | return container.remove(value); 3942 | } 3943 | 3944 | 3945 | 3946 | #ifdef PLF_CPP20_SUPPORT 3947 | // std::reverse_iterator overload, to allow use of plf::list with ranges and make_reverse_iterator primarily: 3948 | template 3949 | class reverse_iterator : public it_type::reverse_type 3950 | { 3951 | public: 3952 | typedef typename it_type::reverse_type rit; 3953 | using rit::rit; 3954 | }; 3955 | #endif 3956 | } 3957 | 3958 | 3959 | #undef PLF_CONSTRUCT_NODE 3960 | #undef PLF_EXCEPTIONS_SUPPORT 3961 | #undef PLF_TO_ADDRESS 3962 | #undef PLF_DEFAULT_TEMPLATE_ARGUMENT_SUPPORT 3963 | #undef PLF_ALIGNMENT_SUPPORT 3964 | #undef PLF_INITIALIZER_LIST_SUPPORT 3965 | #undef PLF_TYPE_TRAITS_SUPPORT 3966 | #undef PLF_IS_ALWAYS_EQUAL_SUPPORT 3967 | #undef PLF_ALLOCATOR_TRAITS_SUPPORT 3968 | #undef PLF_VARIADICS_SUPPORT 3969 | #undef PLF_MOVE_SEMANTICS_SUPPORT 3970 | #undef PLF_NOEXCEPT 3971 | #undef PLF_NOEXCEPT_ALLOCATOR 3972 | #undef PLF_NOEXCEPT_SWAP 3973 | #undef PLF_NOEXCEPT_MOVE_ASSIGN 3974 | #undef PLF_CONSTEXPR 3975 | #undef PLF_CONSTFUNC 3976 | #undef PLF_CPP20_SUPPORT 3977 | 3978 | #undef PLF_CONSTRUCT 3979 | #undef PLF_DESTROY 3980 | #undef PLF_ALLOCATE 3981 | #undef PLF_DEALLOCATE 3982 | 3983 | #if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) 3984 | #pragma warning ( pop ) 3985 | #endif 3986 | 3987 | #endif // PLF_LIST_H 3988 | -------------------------------------------------------------------------------- /plf_list_test_suite.cpp: -------------------------------------------------------------------------------- 1 | #if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) 2 | #if _MSC_VER >= 1600 3 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT 4 | #endif 5 | #if _MSC_VER >= 1700 6 | #define PLF_TEST_TYPE_TRAITS_SUPPORT 7 | #endif 8 | #if _MSC_VER >= 1800 9 | #define PLF_TEST_VARIADICS_SUPPORT // Variadics, in this context, means both variadic templates and variadic macros are supported 10 | #define PLF_TEST_INITIALIZER_LIST_SUPPORT 11 | #endif 12 | 13 | #if defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L) && _MSC_VER >= 1929 14 | #define PLF_TEST_CPP20_SUPPORT 15 | #endif 16 | #elif defined(__cplusplus) && __cplusplus >= 201103L // C++11 support, at least 17 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT 18 | 19 | #if defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__clang__) // If compiler is GCC/G++ 20 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4 // 4.2 and below do not support variadic templates 21 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT 22 | #define PLF_TEST_VARIADICS_SUPPORT 23 | #endif 24 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4 // 4.3 and below do not support initializer lists 25 | #define PLF_TEST_INITIALIZER_LIST_SUPPORT 26 | #endif 27 | #if __GNUC__ >= 5 // GCC v4.9 and below do not support std::is_trivially_copyable 28 | #define PLF_TEST_TYPE_TRAITS_SUPPORT 29 | #endif 30 | #elif defined(__clang__) && !defined(__GLIBCXX__) && !defined(_LIBCPP_CXX03_LANG) 31 | #if __clang_major__ >= 3 // clang versions < 3 don't support __has_feature() or traits 32 | #define PLF_TEST_TYPE_TRAITS_SUPPORT 33 | 34 | #if __has_feature(cxx_rvalue_references) && !defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) 35 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT 36 | #endif 37 | #if __has_feature(cxx_variadic_templates) && !defined(_LIBCPP_HAS_NO_VARIADICS) 38 | #define PLF_TEST_VARIADICS_SUPPORT 39 | #endif 40 | #if (__clang_major__ == 3 && __clang_minor__ >= 1) || __clang_major__ > 3 41 | #define PLF_TEST_INITIALIZER_LIST_SUPPORT 42 | #endif 43 | #endif 44 | #elif defined(__GLIBCXX__) 45 | #if __GLIBCXX__ >= 20080606 46 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT 47 | #define PLF_TEST_VARIADICS_SUPPORT 48 | #endif 49 | #if __GLIBCXX__ >= 20090421 50 | #define PLF_TEST_INITIALIZER_LIST_SUPPORT 51 | #endif 52 | #if __GLIBCXX__ >= 20150422 53 | #define PLF_TEST_TYPE_TRAITS_SUPPORT 54 | #endif 55 | #elif !(defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) || defined(_LIBCPP_HAS_NO_VARIADICS)) 56 | // Assume full support for other compilers and standard libraries 57 | #define PLF_TEST_VARIADICS_SUPPORT 58 | #define PLF_TEST_TYPE_TRAITS_SUPPORT 59 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT 60 | #define PLF_TEST_INITIALIZER_LIST_SUPPORT 61 | #endif 62 | 63 | #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__))) 64 | #define PLF_TEST_CPP20_SUPPORT 65 | #endif 66 | #endif 67 | 68 | 69 | 70 | #include // std::greater 71 | #include // range-insert testing 72 | #include 73 | #include // std::find 74 | #include // log redirection 75 | #include // abort 76 | 77 | #ifdef PLF_TEST_MOVE_SEMANTICS_SUPPORT 78 | #include // std::move 79 | #include // std::shared_ptr 80 | #endif 81 | 82 | 83 | #include "plf_list.h" 84 | 85 | 86 | 87 | 88 | void title1(const char *title_text) 89 | { 90 | std::cout << std::endl << std::endl << std::endl << "*** " << title_text << " ***" << std::endl; 91 | std::cout << "===========================================" << std::endl << std::endl << std::endl; 92 | } 93 | 94 | 95 | 96 | void title2(const char *title_text) 97 | { 98 | std::cout << std::endl << std::endl << "--- " << title_text << " ---" << std::endl << std::endl; 99 | } 100 | 101 | 102 | void title3(const char *title_text) 103 | { 104 | std::cout << std::endl << title_text << std::endl; 105 | } 106 | 107 | 108 | 109 | void failpass(const char *test_type, bool condition) 110 | { 111 | std::cout << "\n" << test_type << ": "; 112 | 113 | if (condition) 114 | { 115 | std::cout << "Pass\n\n"; 116 | } 117 | else 118 | { 119 | std::cout << "Fail" << std::endl; 120 | std::cin.get(); 121 | abort(); 122 | } 123 | } 124 | 125 | 126 | 127 | 128 | struct small_struct 129 | { 130 | double *empty_field_1; 131 | double unused_number; 132 | unsigned int empty_field2; 133 | double *empty_field_3; 134 | int number; 135 | unsigned int empty_field4; 136 | 137 | small_struct(const int num): number(num) {}; 138 | }; 139 | 140 | 141 | 142 | 143 | 144 | // For remove_if testing: 145 | 146 | template 147 | struct larger_than_fifteen 148 | { 149 | bool operator() (const T &value) 150 | { 151 | return value > 15; 152 | } 153 | }; 154 | 155 | 156 | 157 | #ifdef PLF_TEST_VARIADICS_SUPPORT 158 | struct perfect_forwarding_test 159 | { 160 | const bool success; 161 | 162 | perfect_forwarding_test(int&& /*perfect1*/, int& perfect2) 163 | : success(true) 164 | { 165 | perfect2 = 1; 166 | } 167 | 168 | template 169 | perfect_forwarding_test(T&& /*imperfect1*/, U&& /*imperfect2*/) 170 | : success(false) 171 | {} 172 | }; 173 | #endif 174 | 175 | 176 | unsigned int generic_global = 0; 177 | 178 | struct small_struct_non_trivial 179 | { 180 | double *empty_field_1; 181 | double unused_number; 182 | unsigned int empty_field2; 183 | double *empty_field_3; 184 | double number; 185 | unsigned int empty_field4; 186 | 187 | int operator * () const { return number; }; 188 | bool operator == (const small_struct_non_trivial &source) const { return source.number == number; }; 189 | bool operator != (const small_struct_non_trivial &source) const { return source.number != number; }; 190 | bool operator > (const small_struct_non_trivial &source) const { return number > source.number; }; 191 | bool operator < (const small_struct_non_trivial &source) const { return number < source.number; }; 192 | bool operator >= (const small_struct_non_trivial &source) const { return number >= source.number; }; 193 | bool operator <= (const small_struct_non_trivial &source) const { return number <= source.number; }; 194 | 195 | small_struct_non_trivial(const unsigned int num): number(num) {}; 196 | 197 | ~small_struct_non_trivial() 198 | { 199 | ++generic_global; 200 | } 201 | }; 202 | 203 | 204 | 205 | int main() 206 | { 207 | freopen("errors.log","w", stderr); 208 | 209 | using namespace plf; 210 | 211 | #if defined(PLF_TEST_INITIALIZER_LIST_SUPPORT) || defined(PLF_TEST_MOVE_SEMANTICS_SUPPORT) 212 | bool passed = true; 213 | #endif 214 | 215 | #ifndef PLF_TEST_INITIALIZER_LIST_SUPPORT 216 | std::cout << "Initializer_list support (C++11 or higher) is required for most tests. Most tests will skipped without it. Press ENTER to continue." << std::endl; 217 | std::cin.get(); 218 | #endif 219 | 220 | for (unsigned int prime_loop_counter = 0; prime_loop_counter != 50; ++prime_loop_counter) 221 | { 222 | int test_counter = 1; 223 | 224 | { 225 | title2("Non-trivial type tests"); 226 | plf::list sslist1; 227 | 228 | for (int counter = 0; counter != 50; ++counter) 229 | { 230 | sslist1.push_back(small_struct_non_trivial(counter)); 231 | } 232 | 233 | unsigned int size = sslist1.size(); 234 | failpass("Non-trivial insert test", size == 50); 235 | 236 | for (plf::list::iterator it = sslist1.begin(); it != sslist1.end();) 237 | { 238 | if ((rand() & 7) == 0) 239 | { 240 | it = sslist1.erase(it); 241 | --size; 242 | } 243 | else 244 | { 245 | ++it; 246 | } 247 | } 248 | 249 | failpass("Non-trivial erase test", sslist1.size() == size); 250 | } 251 | 252 | 253 | #ifdef PLF_TEST_INITIALIZER_LIST_SUPPORT 254 | { 255 | title2("Merge tests"); 256 | plf::list list1; 257 | 258 | list1.insert(list1.end(), {1, 3, 5, 7, 9}); 259 | plf::list list2 = {2, 4, 6, 8, 10}; 260 | 261 | list1.merge(list2); 262 | 263 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 264 | { 265 | std::cout << *it << ", "; 266 | 267 | if (test_counter++ != *it) 268 | { 269 | passed = false; 270 | } 271 | } 272 | 273 | failpass("Merge test", passed); 274 | 275 | 276 | 277 | title2("Clear tests"); 278 | 279 | list1.clear(); 280 | list2.clear(); 281 | 282 | list1.push_back(15); 283 | 284 | test_counter = 0; 285 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 286 | { 287 | ++test_counter; 288 | } 289 | 290 | failpass("Push_back singular post-clear test", list1.size() == static_cast(test_counter) && list1.size() == 1); 291 | 292 | 293 | list1.insert(list1.begin(), 14); 294 | 295 | test_counter = 0; 296 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 297 | { 298 | ++test_counter; 299 | } 300 | 301 | failpass("Insert singular post-clear test", list1.size() == static_cast(test_counter) && list1.size() == 2); 302 | 303 | 304 | list1.clear(); 305 | 306 | 307 | 308 | title2("Splice tests"); 309 | 310 | list1 = {1, 2, 3, 4, 5}; 311 | list2 = {6, 7, 8, 9, 10}; 312 | 313 | 314 | list1.splice(list1.end(), list2); 315 | 316 | test_counter = 1; 317 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 318 | { 319 | std::cout << *it << ", "; 320 | 321 | if (test_counter++ != *it) 322 | { 323 | passed = false; 324 | } 325 | } 326 | 327 | failpass("Test splice at end", passed); 328 | 329 | 330 | list1.clear(); 331 | list2.clear(); 332 | 333 | 334 | 335 | list1 = {1, 2, 3, 4, 5}; 336 | list2 = {6, 7, 8, 9, 10}; 337 | 338 | 339 | list1.splice(list1.begin(), list2); 340 | 341 | 342 | test_counter = 0; 343 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 344 | { 345 | std::cout << *it << ", "; 346 | test_counter += *it; 347 | } 348 | 349 | failpass("Test splice at begin", test_counter == 55); 350 | 351 | 352 | 353 | list1.clear(); 354 | list2.clear(); 355 | 356 | list1 = {1, 2, 3, 4, 5}; 357 | list2 = {6, 7, 8, 9, 10}; 358 | 359 | plf::list::iterator it2 = list1.begin(); 360 | std::advance(it2, 3); 361 | 362 | list1.splice(it2, list2); 363 | 364 | test_counter = 0; 365 | 366 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 367 | { 368 | std::cout << *it << ", "; 369 | test_counter += *it; 370 | } 371 | 372 | list1.clear(); 373 | list2.clear(); 374 | 375 | 376 | for (int counter = 1; counter != 25; ++counter) 377 | { 378 | list1.push_back(counter); 379 | list2.push_back(counter + 25); 380 | } 381 | 382 | 383 | plf::list::iterator it3 = list1.begin(); 384 | std::advance(it3, 18); 385 | 386 | list1.splice(it3, list2); 387 | 388 | test_counter = 0; 389 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 390 | { 391 | std::cout << *it << ", "; 392 | test_counter += *it; 393 | } 394 | 395 | failpass("Test splice past middle", test_counter == 1200); 396 | 397 | 398 | list1.clear(); 399 | list2.clear(); 400 | 401 | for (int counter = 5; counter != 36; ++counter) 402 | { 403 | list1.push_back(counter); 404 | list2.push_front(counter); 405 | } 406 | 407 | 408 | list1.splice(list1.begin(), list2); 409 | 410 | test_counter = 0; 411 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 412 | { 413 | std::cout << *it << ", "; 414 | test_counter += *it; 415 | } 416 | 417 | failpass("Large list splice", test_counter == 1240); 418 | 419 | 420 | 421 | title2("Sort tests"); 422 | 423 | list1.sort(); 424 | 425 | test_counter = 0; 426 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 427 | { 428 | std::cout << *it << ", "; 429 | 430 | if(*it < test_counter) 431 | { 432 | passed = false; 433 | } 434 | 435 | test_counter = *it; 436 | } 437 | 438 | failpass("Sort ((default) less than)", passed); 439 | 440 | 441 | list1.sort(std::greater()); 442 | 443 | test_counter = 65535; 444 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 445 | { 446 | std::cout << *it << ", "; 447 | 448 | if(*it > test_counter) 449 | { 450 | passed = false; 451 | } 452 | 453 | test_counter = *it; 454 | } 455 | 456 | failpass("Sort ((predicate), greater than)", passed); 457 | 458 | 459 | 460 | title2("Reverse tests"); 461 | 462 | list1.reverse(); 463 | 464 | test_counter = 0; 465 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 466 | { 467 | std::cout << *it << ", "; 468 | 469 | if(*it < test_counter) 470 | { 471 | passed = false; 472 | } 473 | 474 | test_counter = *it; 475 | } 476 | 477 | failpass("Reverse test 1", passed); 478 | 479 | list2.clear(); 480 | plf::list list3; 481 | 482 | int counter2 = 50000; 483 | for (int counter = 0; counter != 50000; ++counter) 484 | { 485 | list2.push_back(counter); 486 | list3.push_back(--counter2); 487 | } 488 | 489 | list2.reverse(); 490 | 491 | it2 = list2.begin(), it3 = list3.begin(); 492 | 493 | do 494 | { 495 | if (*it2++ != *it3++) 496 | { 497 | passed = false; 498 | break; 499 | } 500 | } while(it2 != list2.end()); 501 | 502 | failpass("Reverse test 2", passed); 503 | 504 | 505 | title2("Unique tests"); 506 | 507 | list1.unique(); 508 | 509 | test_counter = 0; 510 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 511 | { 512 | std::cout << *it << ", "; 513 | 514 | if(*it == test_counter) 515 | { 516 | passed = false; 517 | } 518 | 519 | test_counter = *it; 520 | } 521 | 522 | failpass("Unique test", passed); 523 | 524 | 525 | 526 | 527 | title2("Remove tests"); 528 | 529 | list1.remove_if(larger_than_fifteen()); 530 | 531 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 532 | { 533 | std::cout << *it << ", "; 534 | 535 | if (*it > 15) 536 | { 537 | passed = false; 538 | } 539 | } 540 | 541 | failpass("remove_if ( > 15) test", passed); 542 | 543 | 544 | list1.clear(); 545 | list2.clear(); 546 | 547 | 548 | 549 | list1 = {5, 5, 4, 4}; 550 | 551 | list1.remove(5); 552 | 553 | 554 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 555 | { 556 | std::cout << *it << ", "; 557 | 558 | if(*it == 5) 559 | { 560 | passed = false; 561 | } 562 | } 563 | 564 | failpass("remove ( == 5) test", passed); 565 | 566 | 567 | list1.remove(4); 568 | failpass("Remove to create empty list test", list1.empty()); 569 | 570 | 571 | title2("Reserve tests"); 572 | 573 | list1.clear(); 574 | list2.clear(); 575 | 576 | 577 | list1.reserve(4097); 578 | 579 | std::cout << "\nInitial capacity after reserve = " << list1.capacity() << std::endl; 580 | 581 | failpass("Reserve from empty test", list1.capacity() >= 4097); 582 | 583 | 584 | 585 | list1.push_back(15); 586 | 587 | test_counter = 0; 588 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 589 | { 590 | ++test_counter; 591 | } 592 | 593 | failpass("Push_back singular test post-reserve", list1.size() == static_cast(test_counter)); 594 | 595 | 596 | 597 | 598 | list1.insert(list1.end(), 10000, 15); 599 | 600 | test_counter = 0; 601 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 602 | { 603 | ++test_counter; 604 | } 605 | 606 | std::cout << "\nSize (after iteration) = " << test_counter << std::endl; 607 | std::cout << "\nCapacity after insertion = " << list1.capacity() << std::endl; 608 | 609 | failpass("Fill-insert test", list1.size() == 10001 && test_counter == 10001 && list1.capacity() >= 10001); 610 | 611 | 612 | 613 | list1.reserve(15000); 614 | std::cout << "\nCapacity after 2nd reserve = " << list1.capacity() << std::endl; 615 | failpass("Reserve post-insertion test", list1.capacity() >= 15000); 616 | 617 | 618 | 619 | title2("Resize tests"); 620 | 621 | list1.resize(2); 622 | std::cout << "\nCapacity after resize = " << list1.capacity() << std::endl; 623 | 624 | test_counter = 0; 625 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 626 | { 627 | std::cout << *it << ", "; 628 | ++test_counter; 629 | } 630 | 631 | failpass("Resize test", list1.size() == 2 && test_counter == 2); 632 | 633 | 634 | 635 | title2("Assign tests"); 636 | 637 | std::vector test_vector = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 638 | 639 | list1.assign(test_vector.begin(), test_vector.end()); 640 | std::cout << "\nCapacity after range-assign = " << list1.capacity() << std::endl; 641 | 642 | test_counter = 0; 643 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 644 | { 645 | std::cout << *it << ", "; 646 | 647 | if (++test_counter != *it) 648 | { 649 | passed = false; 650 | } 651 | } 652 | 653 | failpass("Range-assign test", list1.size() == 10 && test_counter == 10 && passed); 654 | 655 | 656 | 657 | list1.assign(20, 1); 658 | std::cout << "\nCapacity after fill-assign = " << list1.capacity() << std::endl; 659 | 660 | test_counter = 0; 661 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 662 | { 663 | std::cout << *it << ", "; 664 | ++test_counter; 665 | 666 | if (*it != 1) 667 | { 668 | passed = false; 669 | } 670 | } 671 | 672 | failpass("Fill-assign test", list1.size() == 20 && test_counter == 20 && passed); 673 | 674 | 675 | 676 | 677 | std::initializer_list inlist = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; 678 | 679 | 680 | list1.assign(inlist); 681 | std::cout << "\nCapacity after initializer-list assign = " << list1.capacity() << std::endl; 682 | 683 | test_counter = 11; 684 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 685 | { 686 | std::cout << *it << ", "; 687 | 688 | if (--test_counter != *it) 689 | { 690 | passed = false; 691 | } 692 | } 693 | 694 | failpass("Initializer-list assign test", list1.size() == 10 && test_counter == 1 && passed); 695 | 696 | 697 | title2("Insert tests"); 698 | 699 | 700 | list2.insert(list2.end(), test_vector.begin(), test_vector.end()); 701 | 702 | test_counter = 1; 703 | 704 | for (plf::list::iterator it = list2.begin(); it != list2.end(); ++it) 705 | { 706 | std::cout << *it << ", "; 707 | 708 | if (test_counter++ != *it) 709 | { 710 | passed = false; 711 | } 712 | } 713 | 714 | failpass("Range-insert test", passed); 715 | 716 | #ifdef PLF_TEST_CPP20_SUPPORT 717 | { 718 | plf::list list5(list2.begin(), list2.cend()); 719 | 720 | failpass("Range construction with differing iterators test", list5.size() == list2.size()); 721 | 722 | plf::list list6; 723 | list6.insert(list6.end(), list2.begin(), list2.cend()); 724 | 725 | failpass("Range insertion with differing iterators test", list6.size() == list2.size()); 726 | } 727 | #endif 728 | 729 | 730 | list2.insert(list2.begin(), 50, 50000); 731 | 732 | 733 | test_counter = 0; 734 | 735 | for (plf::list::iterator it = list2.begin(); it != list2.end(); ++it) 736 | { 737 | ++test_counter; 738 | } 739 | 740 | failpass("Fill-insert post range-insert test", test_counter == 60 && list2.size() == 60); 741 | 742 | 743 | 744 | test_counter = 0; 745 | 746 | while (!list2.empty()) 747 | { 748 | for (plf::list::iterator it = list2.begin(); it != list2.end();) 749 | { 750 | if ((rand() & 15) == 0) 751 | { 752 | list2.insert(it, 13); 753 | } 754 | 755 | if ((rand() & 7) == 0) 756 | { 757 | it = list2.erase(it); 758 | } 759 | else 760 | { 761 | ++it; 762 | } 763 | 764 | ++test_counter; 765 | } 766 | 767 | 768 | unsigned int size_counter = 0; 769 | 770 | for (plf::list::iterator it = list2.begin(); it != list2.end(); ++it) 771 | { 772 | ++size_counter; 773 | } 774 | 775 | if (size_counter != list2.size()) 776 | { 777 | std::cout << "Failing at counter == " << test_counter << std::endl; 778 | std::cin.get(); 779 | abort(); 780 | } 781 | } 782 | 783 | failpass("Erase and insert randomly till empty test", list2.empty()); 784 | 785 | 786 | list1.clear(); 787 | list2.clear(); 788 | 789 | 790 | test_counter = 0; 791 | 792 | for (unsigned int counter = 0; counter != 500; ++counter) 793 | { 794 | list1.emplace_back(1); 795 | list2.emplace_front(1); 796 | test_counter += 2; 797 | 798 | for (counter2 = 0; counter2 != 50; ++counter2) 799 | { 800 | if ((rand() & 7) == 0) 801 | { 802 | list1.emplace_back(counter2); 803 | ++test_counter; 804 | } 805 | 806 | if ((rand() & 15) == 0) 807 | { 808 | list2.emplace_back(counter2); 809 | ++test_counter; 810 | } 811 | } 812 | 813 | for (plf::list::iterator it = list1.begin(); it != list1.end();) 814 | { 815 | if ((rand() & 7) == 0) 816 | { 817 | list1.insert(it, 13); 818 | ++test_counter; 819 | } 820 | 821 | if ((rand() & 3) == 0) 822 | { 823 | it = list1.erase(it); 824 | --test_counter; 825 | } 826 | else 827 | { 828 | ++it; 829 | } 830 | } 831 | 832 | 833 | for (plf::list::iterator it = --list2.end(); it != list2.begin();) 834 | { 835 | if ((rand() & 7) == 0) 836 | { 837 | list2.insert(it, 13); 838 | ++test_counter; 839 | } 840 | 841 | if ((rand() & 7) == 0) 842 | { 843 | it = list2.erase(it); 844 | --test_counter; 845 | } 846 | 847 | --it; 848 | } 849 | 850 | 851 | list2.splice(++list2.begin(), list1); 852 | test_counter -= static_cast(list2.remove(1)); 853 | 854 | unsigned int size_counter = 0; 855 | 856 | for (plf::list::iterator it = list2.begin(); it != list2.end(); ++it) 857 | { 858 | ++size_counter; 859 | } 860 | 861 | if (size_counter != list2.size()) 862 | { 863 | std::cout << "Failing at counter == " << counter << std::endl; 864 | std::cin.get(); 865 | abort(); 866 | } 867 | } 868 | 869 | failpass("Erase and insert randomly + splicing test", test_counter == static_cast(list2.size())); 870 | } 871 | #endif 872 | 873 | 874 | #if defined(PLF_TEST_MOVE_SEMANTICS_SUPPORT) && defined(PLF_TEST_VARIADICS_SUPPORT) 875 | { 876 | title2("Emplace, move and Reverse iterate tests"); 877 | 878 | plf::list s_list1; 879 | 880 | 881 | for (int counter = 0; counter != 254; ++counter) 882 | { 883 | s_list1.emplace_back(counter); 884 | } 885 | 886 | small_struct temp = s_list1.emplace_back(254); 887 | 888 | failpass("Emplace_back return value test", temp.number == 254); 889 | 890 | 891 | test_counter = 0; 892 | 893 | for (plf::list::iterator it = s_list1.begin(); it != s_list1.end(); ++it) 894 | { 895 | if (test_counter++ != it->number) 896 | { 897 | passed = false; 898 | break; 899 | } 900 | } 901 | 902 | failpass("Emplace_back test", passed); 903 | 904 | 905 | for (plf::list::reverse_iterator rit = s_list1.rbegin(); rit != s_list1.rend(); ++rit) 906 | { 907 | if (--test_counter != rit->number) 908 | { 909 | passed = false; 910 | break; 911 | } 912 | } 913 | 914 | failpass("Reverse iteration test", passed); 915 | 916 | 917 | for (int counter = -1; counter != -255; --counter) 918 | { 919 | s_list1.emplace_front(counter); 920 | } 921 | 922 | temp = s_list1.emplace_front(-255); 923 | 924 | failpass("Emplace_front return value test", temp.number == -255); 925 | 926 | 927 | test_counter = -255; 928 | 929 | for (plf::list::iterator it = s_list1.begin(); it != s_list1.end(); ++it) 930 | { 931 | if (test_counter++ != it->number) 932 | { 933 | passed = false; 934 | break; 935 | } 936 | } 937 | 938 | failpass("Emplace_front test", passed); 939 | 940 | 941 | test_counter = 255; 942 | 943 | for (plf::list::reverse_iterator rit = s_list1.rbegin(); rit != s_list1.rend(); ++rit) 944 | { 945 | if (--test_counter != rit->number) 946 | { 947 | passed = false; 948 | break; 949 | } 950 | } 951 | 952 | failpass("Reverse iteration test 2", passed); 953 | 954 | 955 | 956 | title2("Move tests"); 957 | 958 | plf::list s_list2(std::move(s_list1)); 959 | 960 | 961 | test_counter = 255; 962 | 963 | for (plf::list::reverse_iterator rit = s_list2.rbegin(); rit != s_list2.rend(); ++rit) 964 | { 965 | if (--test_counter != rit->number) 966 | { 967 | passed = false; 968 | break; 969 | } 970 | } 971 | 972 | failpass("Move constructor", passed && s_list1.empty()); 973 | 974 | s_list1.emplace_back(3); // Reuse the moved list will cause segmentation fault 975 | 976 | failpass("Emplace post-moved-list test", s_list1.size() == 1); 977 | 978 | s_list1 = std::move(s_list2); 979 | 980 | test_counter = 255; 981 | 982 | for (plf::list::reverse_iterator rit = s_list1.rbegin(); rit != s_list1.rend(); ++rit) 983 | { 984 | if (--test_counter != rit->number) 985 | { 986 | passed = false; 987 | break; 988 | } 989 | } 990 | 991 | failpass("Move assignment", passed && s_list2.empty()); 992 | 993 | 994 | title2("Copy tests"); 995 | 996 | 997 | 998 | s_list2 = s_list1; 999 | 1000 | 1001 | test_counter = 255; 1002 | 1003 | for (plf::list::reverse_iterator rit = s_list2.rbegin(); rit != s_list2.rend(); ++rit) 1004 | { 1005 | if (--test_counter != rit->number) 1006 | { 1007 | passed = false; 1008 | break; 1009 | } 1010 | } 1011 | 1012 | failpass("Copy assignment", passed); 1013 | 1014 | 1015 | plf::list s_list3(s_list1); 1016 | 1017 | 1018 | test_counter = 255; 1019 | 1020 | for (plf::list::reverse_iterator rit = s_list3.rbegin(); rit != s_list3.rend(); ++rit) 1021 | { 1022 | if (--test_counter != rit->number) 1023 | { 1024 | passed = false; 1025 | break; 1026 | } 1027 | } 1028 | 1029 | failpass("Copy constructor", passed); 1030 | 1031 | title2("Shared_ptr tests"); 1032 | 1033 | plf::list> shared_ptr_list; 1034 | shared_ptr_list.reserve(20); 1035 | 1036 | failpass("Shared_ptr list reserve test", shared_ptr_list.capacity() == 20); 1037 | 1038 | // May crash here if shared_ptr_list destruction fails. 1039 | } 1040 | #endif 1041 | 1042 | 1043 | { 1044 | title2("Intra-list splice tests"); 1045 | 1046 | plf::list list1; 1047 | 1048 | 1049 | for (int counter = 0; counter != 255; ++counter) 1050 | { 1051 | list1.push_back(counter); 1052 | } 1053 | 1054 | 1055 | int original_total = 0; 1056 | 1057 | for (plf::list::iterator it = list1.begin(); it != list1.end(); ++it) 1058 | { 1059 | original_total += *it; 1060 | } 1061 | 1062 | plf::list::iterator it1 = list1.begin(), it2 = list1.begin(), it3 = list1.begin(); 1063 | 1064 | std::advance(it1, 25); 1065 | std::advance(it2, 5); 1066 | 1067 | list1.splice(it2, it1); 1068 | 1069 | it1 = list1.begin(); 1070 | std::advance(it1, 5); 1071 | 1072 | failpass("Single splice", *it1 == 25); 1073 | 1074 | 1075 | it1 = list1.begin(); 1076 | std::advance(it1, 152); 1077 | 1078 | list1.splice(list1.begin(), it1); 1079 | 1080 | failpass("Single splice to begin", *(list1.begin()) == 152); 1081 | 1082 | list1.splice(list1.end(), it2); 1083 | 1084 | it1 = --(list1.end()); 1085 | 1086 | failpass("Single splice to end", *it1 == 5); 1087 | 1088 | it2 = it1 = list1.begin(); 1089 | 1090 | std::advance(it1, 50); 1091 | std::advance(it2, 61); 1092 | std::advance(it3, 70); 1093 | 1094 | list1.splice(it3, it1, it2); 1095 | 1096 | bool pass = true; 1097 | 1098 | it3 = list1.begin(); 1099 | std::advance(it3, 60); 1100 | 1101 | for (int test = 50; test != 60; ++test) 1102 | { 1103 | if (*it3 != test) 1104 | { 1105 | pass = false; 1106 | } 1107 | ++it3; 1108 | } 1109 | 1110 | failpass("Range splice", pass == true); 1111 | 1112 | 1113 | it2 = it1 = list1.begin(); 1114 | 1115 | std::advance(it1, 80); 1116 | std::advance(it2, 121); 1117 | 1118 | 1119 | list1.splice(list1.end(), it1, it2); 1120 | 1121 | pass = true; 1122 | 1123 | it3 = list1.end(); 1124 | std::advance(it3, -41); 1125 | 1126 | for (int test = 80; test != 120; ++test) 1127 | { 1128 | if (*it3 != test) 1129 | { 1130 | pass = false; 1131 | } 1132 | ++it3; 1133 | } 1134 | 1135 | failpass("Range splice to end", pass == true); 1136 | 1137 | 1138 | 1139 | it2 = it1 = list1.begin(); 1140 | 1141 | std::advance(it1, 40); 1142 | std::advance(it2, 46); 1143 | 1144 | list1.splice(list1.begin(), it1, it2); 1145 | 1146 | pass = true; 1147 | 1148 | it3 = list1.begin(); 1149 | 1150 | for (int test = 40; test != 45; ++test) 1151 | { 1152 | if (*it3 != test) 1153 | { 1154 | pass = false; 1155 | } 1156 | ++it3; 1157 | } 1158 | 1159 | failpass("Range splice to begin", pass == true); 1160 | 1161 | int total = 0; 1162 | for (it1 = list1.begin(); it1 != list1.end(); ++it1) 1163 | { 1164 | total += *it1; 1165 | } 1166 | 1167 | failpass("Post-spliceing data consistency", total == original_total); 1168 | 1169 | 1170 | } 1171 | 1172 | { 1173 | title1("List"); 1174 | title2("Test Basics"); 1175 | 1176 | list p_list; 1177 | 1178 | failpass("List empty", p_list.empty()); 1179 | 1180 | int ten = 10; 1181 | p_list.push_front(&ten); 1182 | 1183 | failpass("List not-empty", !p_list.empty()); 1184 | 1185 | title2("Iterator tests"); 1186 | 1187 | failpass("Begin() working", **p_list.begin() == 10); 1188 | failpass("End() working", p_list.begin() != p_list.end()); 1189 | 1190 | 1191 | p_list.clear(); 1192 | 1193 | failpass("Begin = End after clear", p_list.begin() == p_list.end()); 1194 | 1195 | int twenty = 20; 1196 | 1197 | for (unsigned int temp = 0; temp != 200; ++temp) 1198 | { 1199 | p_list.push_back(&ten); 1200 | p_list.push_back(&twenty); 1201 | } 1202 | 1203 | int total = 0, numtotal = 0; 1204 | 1205 | for(list::iterator the_iterator = p_list.begin(); the_iterator != p_list.end(); ++the_iterator) 1206 | { 1207 | ++total; 1208 | numtotal += **the_iterator; 1209 | } 1210 | 1211 | failpass("Iteration count test", total == 400); 1212 | failpass("Iterator access test", numtotal == 6000); 1213 | 1214 | list::iterator plus_twenty = p_list.begin(); 1215 | std::advance(plus_twenty, 20); 1216 | list::iterator plus_two_hundred = p_list.begin(); 1217 | std::advance(plus_two_hundred, 200); 1218 | 1219 | failpass("Iterator + distance test", std::distance(p_list.begin(), plus_twenty) == 20); 1220 | failpass("Iterator + distance test 2", std::distance(p_list.begin(), plus_two_hundred) == 200); 1221 | 1222 | #ifdef PLF_TEST_INITIALIZER_LIST_SUPPORT 1223 | list::iterator next_iterator = std::next(p_list.begin(), 5); 1224 | list::const_iterator prev_iterator = std::prev(p_list.cend(), 300); 1225 | #else 1226 | list::iterator next_iterator = p_list.begin(); 1227 | list::const_iterator prev_iterator = p_list.cend(); 1228 | std::advance(next_iterator, 5); 1229 | std::advance(prev_iterator, -300); 1230 | #endif 1231 | 1232 | failpass("Iterator next test", std::distance(p_list.begin(), next_iterator) == 5); 1233 | failpass("Const iterator prev test", std::distance(prev_iterator, p_list.cend()) == 300); 1234 | 1235 | #if defined(__cplusplus) && __cplusplus >= 201402L 1236 | list::iterator prev_iterator2 = std::prev(p_list.end(), 300); 1237 | failpass("Iterator/Const iterator equality operator test", prev_iterator == prev_iterator2); 1238 | #endif 1239 | 1240 | 1241 | list p_list2; 1242 | p_list2 = p_list; 1243 | list p_list3(p_list); 1244 | list p_list4(p_list2, p_list2.get_allocator()); 1245 | 1246 | list::iterator it1 = p_list.begin(); 1247 | list::const_iterator cit(it1); 1248 | 1249 | failpass("Copy test", p_list2.size() == 400); 1250 | failpass("Copy construct test", p_list3.size() == 400); 1251 | failpass("Allocator-extended copy construct test", p_list4.size() == 400); 1252 | 1253 | 1254 | failpass("Equality operator test", p_list == p_list2); 1255 | failpass("Equality operator test 2", p_list2 == p_list3); 1256 | 1257 | p_list2.push_back(&ten); 1258 | 1259 | failpass("Inequality operator test", p_list2 != p_list3); 1260 | 1261 | #ifdef PLF_TEST_CPP20_SUPPORT 1262 | failpass("Spaceship operator test", (p_list2 <=> p_list3) != std::strong_ordering::equal); 1263 | #endif 1264 | 1265 | numtotal = 0; 1266 | total = 0; 1267 | 1268 | for (list::reverse_iterator the_iterator = p_list.rbegin(); the_iterator != p_list.rend(); ++the_iterator) 1269 | { 1270 | ++total; 1271 | numtotal += **the_iterator; 1272 | } 1273 | 1274 | 1275 | failpass("Reverse iteration count test", total == 400); 1276 | failpass("Reverse iterator access test", numtotal == 6000); 1277 | 1278 | list::reverse_iterator r_iterator = p_list.rbegin(); 1279 | std::advance(r_iterator, 50); 1280 | 1281 | failpass("Reverse iterator advance and distance test", std::distance(p_list.rbegin(), r_iterator) == 50); 1282 | 1283 | #ifdef PLF_TEST_INITIALIZER_LIST_SUPPORT 1284 | list::reverse_iterator r_iterator2 = std::next(r_iterator, 2); 1285 | #else 1286 | list::reverse_iterator r_iterator2 = r_iterator; 1287 | std::advance(r_iterator2, 2); 1288 | #endif 1289 | 1290 | failpass("Reverse iterator next and distance test", std::distance(p_list.rbegin(), r_iterator2) == 52); 1291 | 1292 | numtotal = 0; 1293 | total = 0; 1294 | 1295 | for(list::iterator the_iterator = p_list.begin(); the_iterator != p_list.end(); std::advance(the_iterator, 2)) 1296 | { 1297 | ++total; 1298 | numtotal += **the_iterator; 1299 | } 1300 | 1301 | failpass("Multiple iteration test", total == 200); 1302 | failpass("Multiple iteration access test", numtotal == 2000); 1303 | 1304 | numtotal = 0; 1305 | total = 0; 1306 | 1307 | for(list::const_iterator the_iterator = p_list.cbegin(); the_iterator != p_list.cend(); ++the_iterator) 1308 | { 1309 | ++total; 1310 | numtotal += **the_iterator; 1311 | } 1312 | 1313 | failpass("Const_iterator test", total == 400); 1314 | failpass("Const_iterator access test", numtotal == 6000); 1315 | 1316 | 1317 | numtotal = 0; 1318 | total = 0; 1319 | 1320 | for(list::const_reverse_iterator the_iterator = --list::const_reverse_iterator(p_list.crend()); the_iterator != list::const_reverse_iterator(p_list.crbegin()); --the_iterator) 1321 | { 1322 | ++total; 1323 | numtotal += **the_iterator; 1324 | } 1325 | 1326 | failpass("Const_reverse_iterator -- test", total == 399); 1327 | failpass("Const_reverse_iterator -- access test", numtotal == 5980); 1328 | 1329 | total = 0; 1330 | 1331 | for(list::iterator the_iterator = ++list::iterator(p_list.begin()); the_iterator != p_list.end(); ++the_iterator) 1332 | { 1333 | ++total; 1334 | the_iterator = p_list.erase(the_iterator); 1335 | 1336 | if (the_iterator == p_list.end()) 1337 | { 1338 | break; 1339 | } 1340 | } 1341 | 1342 | failpass("Partial erase iteration test", total == 200); 1343 | failpass("Post-erase size test", p_list.size() == 200); 1344 | 1345 | const unsigned int temp_capacity = static_cast(p_list.capacity()); 1346 | p_list.shrink_to_fit(); 1347 | failpass("Shrink_to_fit test", p_list.capacity() < temp_capacity); 1348 | failpass("Shrink_to_fit test 2", p_list.capacity() == 200); 1349 | 1350 | { 1351 | plf::list p_list_s; 1352 | p_list_s.push_back(1); 1353 | p_list_s.push_back(7); 1354 | p_list_s.push_back(3); 1355 | p_list_s.push_back(5); 1356 | p_list_s.push_back(4); 1357 | 1358 | p_list_s.shrink_to_fit(); 1359 | failpass("Shrink_to_fit test 3", p_list_s.size() == 5); 1360 | 1361 | p_list_s.resize(4); 1362 | failpass("Resize post-shrink-to-fit test", p_list_s.size() == 4); 1363 | 1364 | p_list_s.shrink_to_fit(); 1365 | failpass("Shrink_to_fit test 4", p_list_s.size() == 4); 1366 | } 1367 | 1368 | total = 0; 1369 | 1370 | for(list::reverse_iterator the_iterator = p_list.rbegin(); the_iterator != p_list.rend();) 1371 | { 1372 | ++the_iterator; 1373 | list::iterator it = the_iterator.base(); // grabs the_iterator--, essentially 1374 | p_list.erase(it); 1375 | ++total; 1376 | } 1377 | 1378 | failpass("Full erase reverse iteration test", total == 200); 1379 | failpass("Post-erase size test", p_list.size() == 0); 1380 | 1381 | for (unsigned int temp = 0; temp != 200; ++temp) 1382 | { 1383 | p_list.push_back(&ten); 1384 | p_list.push_back(&twenty); 1385 | } 1386 | 1387 | total = 0; 1388 | 1389 | for(list::iterator the_iterator = --list::iterator(p_list.end()); the_iterator != p_list.begin(); --the_iterator) 1390 | { 1391 | ++total; 1392 | } 1393 | 1394 | failpass("Negative iteration test", total == 399); 1395 | 1396 | 1397 | total = 0; 1398 | 1399 | for(list::iterator the_iterator = --(list::iterator(p_list.end())); the_iterator != p_list.begin() && the_iterator != ++plf::list::iterator(p_list.begin()); std::advance(the_iterator, -2)) 1400 | { 1401 | ++total; 1402 | } 1403 | 1404 | failpass("Negative multiple iteration test", total == 199); 1405 | 1406 | #ifdef PLF_TEST_MOVE_SEMANTICS_SUPPORT 1407 | p_list2 = std::move(p_list); 1408 | failpass("Move test", p_list2.size() == 400); 1409 | 1410 | list p_list5(p_list2); 1411 | list p_list6(std::move(p_list5), p_list2.get_allocator()); 1412 | 1413 | failpass("Allocator-extended move construct test", p_list6.size() == 400); 1414 | #else 1415 | p_list2 = p_list; 1416 | #endif 1417 | 1418 | p_list3 = p_list2; 1419 | 1420 | failpass("Copy test 2", p_list3.size() == 400); 1421 | 1422 | p_list2.push_back(&ten); 1423 | 1424 | p_list2.swap(p_list3); 1425 | 1426 | failpass("Swap test", p_list2.size() == p_list3.size() - 1); 1427 | 1428 | swap(p_list2, p_list3); 1429 | 1430 | failpass("Swap test 2", p_list3.size() == p_list2.size() - 1); 1431 | 1432 | failpass("max_size() test", p_list2.max_size() > p_list2.size()); 1433 | 1434 | } 1435 | 1436 | 1437 | { 1438 | title2("Insert and Erase tests"); 1439 | 1440 | list i_list; 1441 | 1442 | for (int temp = 0; temp != 500000; ++temp) 1443 | { 1444 | i_list.push_back(temp); 1445 | } 1446 | 1447 | 1448 | failpass("Size after insert test", i_list.size() == 500000); 1449 | 1450 | 1451 | list::iterator found_item = std::find(i_list.begin(), i_list.end(), 5000);; 1452 | 1453 | failpass("std::find iterator test", *found_item == 5000); 1454 | 1455 | 1456 | list::reverse_iterator found_item2 = std::find(i_list.rbegin(), i_list.rend(), 5000);; 1457 | 1458 | failpass("std::find reverse_iterator test", *found_item2 == 5000); 1459 | 1460 | 1461 | for (list::iterator the_iterator = i_list.begin(); the_iterator != i_list.end(); ++the_iterator) 1462 | { 1463 | the_iterator = i_list.erase(the_iterator); 1464 | } 1465 | 1466 | failpass("Erase alternating test", i_list.size() == 250000); 1467 | 1468 | do 1469 | { 1470 | for (list::iterator the_iterator = i_list.begin(); the_iterator != i_list.end();) 1471 | { 1472 | if ((rand() & 7) == 0) 1473 | { 1474 | the_iterator = i_list.erase(the_iterator); 1475 | } 1476 | else 1477 | { 1478 | ++the_iterator; 1479 | } 1480 | } 1481 | 1482 | } while (!i_list.empty()); 1483 | 1484 | failpass("Erase randomly till-empty test", i_list.size() == 0); 1485 | 1486 | 1487 | i_list.clear(); 1488 | 1489 | for (unsigned int temp = 0; temp != 30000; ++temp) 1490 | { 1491 | i_list.push_back(1); 1492 | } 1493 | 1494 | failpass("Size after reinitialize + insert test", i_list.size() == 30000); 1495 | 1496 | unsigned short count2 = 0; 1497 | 1498 | do 1499 | { 1500 | for (list::iterator the_iterator = i_list.begin(); the_iterator != i_list.end();) 1501 | { 1502 | if ((rand() & 7) == 0) 1503 | { 1504 | the_iterator = i_list.erase(the_iterator); 1505 | ++count2; 1506 | } 1507 | else 1508 | { 1509 | ++the_iterator; 1510 | } 1511 | } 1512 | 1513 | } while (count2 < 15000); 1514 | 1515 | failpass("Erase randomly till half-empty test", i_list.size() == 30000u - count2); 1516 | 1517 | for (unsigned int temp = 0; temp != count2; ++temp) 1518 | { 1519 | i_list.push_front(1); 1520 | } 1521 | 1522 | failpass("Size after reinsert test", i_list.size() == 30000); 1523 | 1524 | 1525 | 1526 | 1527 | unsigned int sum = 0; 1528 | 1529 | for (list::iterator the_iterator = i_list.begin(); the_iterator != i_list.end();) 1530 | { 1531 | if (++sum == 3) 1532 | { 1533 | sum = 0; 1534 | the_iterator = i_list.erase(the_iterator); 1535 | } 1536 | else 1537 | { 1538 | i_list.insert(the_iterator, 1); 1539 | ++the_iterator; 1540 | } 1541 | } 1542 | 1543 | failpass("Alternating insert/erase test", i_list.size() == 40000); 1544 | 1545 | 1546 | do 1547 | { 1548 | for (list::iterator the_iterator = i_list.begin(); the_iterator != i_list.end();) 1549 | { 1550 | if ((rand() & 3) == 0) 1551 | { 1552 | ++the_iterator; 1553 | i_list.push_front(1); 1554 | } 1555 | else 1556 | { 1557 | the_iterator = i_list.erase(the_iterator); 1558 | } 1559 | } 1560 | } while (!i_list.empty()); 1561 | 1562 | failpass("Random insert/erase till empty test", i_list.size() == 0); 1563 | 1564 | 1565 | for (unsigned int temp = 0; temp != 500000; ++temp) 1566 | { 1567 | i_list.push_back(10); 1568 | } 1569 | 1570 | failpass("Insert post-erase test", i_list.size() == 500000); 1571 | list::iterator it2 = i_list.begin(); 1572 | std::advance(it2, 250000); 1573 | 1574 | 1575 | for (; it2 != i_list.end();) 1576 | { 1577 | it2 = i_list.erase(it2); 1578 | } 1579 | 1580 | failpass("Large multi-increment iterator test", i_list.size() == 250000); 1581 | 1582 | 1583 | for (unsigned int temp = 0; temp != 250000; ++temp) 1584 | { 1585 | i_list.push_back(10); 1586 | } 1587 | 1588 | list::iterator end_iterator = i_list.end(); 1589 | std::advance(end_iterator, -250000); 1590 | 1591 | for (list::iterator the_iterator = i_list.begin(); the_iterator != end_iterator;) 1592 | { 1593 | the_iterator = i_list.erase(the_iterator); 1594 | } 1595 | 1596 | failpass("Large multi-decrement iterator test", i_list.size() == 250000); 1597 | 1598 | 1599 | for (unsigned int temp = 0; temp != 250000; ++temp) 1600 | { 1601 | i_list.push_front(10); 1602 | } 1603 | 1604 | 1605 | int total = 0; 1606 | 1607 | for (list::iterator the_iterator = i_list.begin(); the_iterator != i_list.end(); ++the_iterator) 1608 | { 1609 | total += *the_iterator; 1610 | } 1611 | 1612 | failpass("Re-insert post-heavy-erasure test", total == 5000000); 1613 | 1614 | 1615 | end_iterator = i_list.end(); 1616 | std::advance(end_iterator, -50001); 1617 | list::iterator begin_iterator = i_list.begin(); 1618 | std::advance(begin_iterator, 300000); 1619 | 1620 | for (list::iterator the_iterator = begin_iterator; the_iterator != end_iterator;) 1621 | { 1622 | the_iterator = i_list.erase(the_iterator); 1623 | } 1624 | 1625 | failpass("Non-end decrement + erase test", i_list.size() == 350001); 1626 | 1627 | 1628 | for (unsigned int temp = 0; temp != 100000; ++temp) 1629 | { 1630 | i_list.push_back(10); 1631 | } 1632 | 1633 | begin_iterator = i_list.begin(); 1634 | std::advance(begin_iterator, 300001); 1635 | 1636 | 1637 | for (list::iterator the_iterator = begin_iterator; the_iterator != i_list.end();) 1638 | { 1639 | the_iterator = i_list.erase(the_iterator); 1640 | } 1641 | 1642 | failpass("Non-beginning increment + erase test", i_list.size() == 300001); 1643 | 1644 | list::iterator temp_iterator = i_list.begin(); 1645 | std::advance(temp_iterator, 2); // Advance test 1 1646 | 1647 | i_list.erase(temp_iterator); 1648 | temp_iterator = i_list.begin(); // Check edge-case with advance when erasures present in initial group 1649 | std::advance(temp_iterator, 500); 1650 | 1651 | for (list::iterator the_iterator = i_list.begin(); the_iterator != i_list.end();) 1652 | { 1653 | the_iterator = i_list.erase(the_iterator); 1654 | } 1655 | 1656 | failpass("Total erase test", i_list.empty()); 1657 | 1658 | 1659 | i_list.clear(); 1660 | i_list.shrink_to_fit(); 1661 | 1662 | const unsigned int temp_capacity2 = static_cast(i_list.capacity()); 1663 | i_list.reserve(1000); 1664 | failpass("List reserve test", temp_capacity2 != i_list.capacity()); 1665 | failpass("List reserve test2", i_list.capacity() == 1000); 1666 | 1667 | unsigned int count = 0; 1668 | 1669 | for (unsigned int loop1 = 0; loop1 != 50000; ++loop1) 1670 | { 1671 | for (unsigned int loop = 0; loop != 10; ++loop) 1672 | { 1673 | if ((rand() & 7) == 0) 1674 | { 1675 | i_list.push_back(1); 1676 | ++count; 1677 | } 1678 | } 1679 | 1680 | for (list::iterator the_iterator = i_list.begin(); the_iterator != i_list.end();) 1681 | { 1682 | if ((rand() & 7) == 0) 1683 | { 1684 | the_iterator = i_list.erase(the_iterator); 1685 | --count; 1686 | } 1687 | else 1688 | { 1689 | ++the_iterator; 1690 | } 1691 | } 1692 | } 1693 | 1694 | failpass("Multiple sequential small insert/erase commands test", count == i_list.size()); 1695 | } 1696 | 1697 | 1698 | 1699 | { 1700 | title2("unordered_find tests"); 1701 | 1702 | list i_list; 1703 | 1704 | for (int temp = 0; temp != 1000; ++temp) 1705 | { 1706 | i_list.push_back(10); 1707 | i_list.push_back(20); 1708 | } 1709 | 1710 | i_list.push_back(50); 1711 | 1712 | list::iterator found_item = i_list.unordered_find_single(10); 1713 | 1714 | failpass("unordered_find_single test 1", *found_item == 10); 1715 | 1716 | 1717 | found_item = i_list.unordered_find_single(20); 1718 | 1719 | failpass("unordered_find_single test 2", *found_item == 20); 1720 | 1721 | 1722 | found_item = i_list.unordered_find_single(50); 1723 | 1724 | failpass("unordered_find_single test 3", --(list::iterator(i_list.end())) == found_item); 1725 | 1726 | 1727 | #if defined(__cplusplus) && __cplusplus >= 201103L 1728 | found_item = i_list.unordered_find_single([](const int x){return x > 20;}); 1729 | failpass("unordered_find_single test 4", *found_item == 50); 1730 | #endif 1731 | 1732 | 1733 | list::iterator> found_items_list = i_list.unordered_find_multiple(20, 50); 1734 | 1735 | failpass("unordered_find_multiple test", found_items_list.size() == 50); 1736 | 1737 | 1738 | found_items_list = i_list.unordered_find_multiple(10, 135); 1739 | 1740 | failpass("unordered_find_multiple test 2", found_items_list.size() == 135); 1741 | 1742 | 1743 | #if defined(__cplusplus) && __cplusplus >= 201103L 1744 | found_items_list = i_list.unordered_find_multiple([](const int x){return x > 10;}, 2000); 1745 | failpass("unordered_find_multiple test 3", found_items_list.size() == 1001); 1746 | #endif 1747 | 1748 | 1749 | found_items_list = i_list.unordered_find_all(10); 1750 | 1751 | failpass("unordered_find_all test", found_items_list.size() == 1000); 1752 | 1753 | 1754 | found_items_list = i_list.unordered_find_all(20); 1755 | 1756 | failpass("unordered_find_all test 2", found_items_list.size() == 1000); 1757 | 1758 | 1759 | found_items_list = i_list.unordered_find_all(50); 1760 | 1761 | failpass("unordered_find_all test 3", found_items_list.size() == 1); 1762 | 1763 | 1764 | #if defined(__cplusplus) && __cplusplus >= 201103L 1765 | found_items_list = i_list.unordered_find_all([](const int x){return x > 10;}); 1766 | failpass("unordered_find_all test 4", found_items_list.size() == 1001); 1767 | #endif 1768 | } 1769 | 1770 | 1771 | 1772 | { 1773 | title2("Range-erase tests"); 1774 | 1775 | list i_list; 1776 | 1777 | for (int counter = 0; counter != 1000; ++counter) 1778 | { 1779 | i_list.push_back(counter); 1780 | } 1781 | 1782 | 1783 | list::iterator it1 = i_list.begin(), it2 = i_list.begin(), it3; 1784 | 1785 | std::advance(it1, 500); 1786 | std::advance(it2, 800); 1787 | 1788 | it3 = i_list.erase(it1, it2); 1789 | 1790 | int counter = 0; 1791 | 1792 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1793 | { 1794 | ++counter; 1795 | } 1796 | 1797 | failpass("Simple range-erase test 1", counter == 700 && i_list.size() == 700); 1798 | 1799 | failpass("Range-erase return value test", it3 == it2); 1800 | 1801 | 1802 | it1 = it2 = i_list.begin(); 1803 | 1804 | std::advance(it1, 400); 1805 | std::advance(it2, 500); // This should put it2 past the point of previous erasures 1806 | 1807 | i_list.erase(it1, it2); 1808 | 1809 | counter = 0; 1810 | 1811 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1812 | { 1813 | ++counter; 1814 | } 1815 | 1816 | failpass("Simple range-erase test 2", counter == 600 && i_list.size() == 600); 1817 | 1818 | 1819 | 1820 | it2 = it1 = i_list.begin(); 1821 | 1822 | std::advance(it1, 4); 1823 | std::advance(it2, 9); // This should put it2 past the point of previous erasures 1824 | 1825 | i_list.erase(it1, it2); 1826 | 1827 | counter = 0; 1828 | 1829 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1830 | { 1831 | ++counter; 1832 | } 1833 | 1834 | failpass("Simple range-erase test 3", counter == 595 && i_list.size() == 595); 1835 | 1836 | 1837 | 1838 | 1839 | it2 = it1 = i_list.begin(); 1840 | 1841 | std::advance(it2, 50); 1842 | 1843 | i_list.erase(it1, it2); 1844 | 1845 | counter = 0; 1846 | 1847 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1848 | { 1849 | ++counter; 1850 | } 1851 | 1852 | failpass("Range-erase from begin() test 1", counter == 545 && i_list.size() == 545); 1853 | 1854 | 1855 | 1856 | 1857 | it1 = i_list.begin(); 1858 | it2 = i_list.end(); 1859 | 1860 | std::advance(it1, 345); // Test erasing and validity when it removes the final group in list 1861 | i_list.erase(it1, it2); 1862 | 1863 | counter = 0; 1864 | 1865 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1866 | { 1867 | ++counter; 1868 | } 1869 | 1870 | failpass("Range-erase to end() test 1", counter == 345 && i_list.size() == 345); 1871 | 1872 | 1873 | 1874 | i_list.clear(); 1875 | 1876 | for (counter = 0; counter != 3000; ++counter) 1877 | { 1878 | i_list.push_back(counter); 1879 | } 1880 | 1881 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1882 | { 1883 | it = i_list.erase(it); 1884 | 1885 | if (it == i_list.end()) 1886 | { 1887 | break; 1888 | } 1889 | } 1890 | 1891 | it2 = it1 = i_list.begin(); 1892 | 1893 | std::advance(it1, 4); 1894 | std::advance(it2, 600); 1895 | i_list.erase(it1, it2); 1896 | 1897 | counter = 0; 1898 | 1899 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1900 | { 1901 | ++counter; 1902 | } 1903 | 1904 | failpass("Range-erase with list already half-erased, alternating erasures", counter == 904 && i_list.size() == 904); 1905 | 1906 | 1907 | 1908 | i_list.clear(); 1909 | 1910 | for (counter = 0; counter != 3000; ++counter) 1911 | { 1912 | i_list.push_back(counter); 1913 | } 1914 | 1915 | test_counter = 0; 1916 | 1917 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1918 | { 1919 | if ((rand() & 1) == 0) 1920 | { 1921 | it = i_list.erase(it); 1922 | ++test_counter; 1923 | } 1924 | } 1925 | 1926 | failpass("Random erasures vs size check", test_counter == static_cast(3000 - i_list.size())); 1927 | 1928 | if (i_list.size() < 500) 1929 | { 1930 | i_list.resize(800, 1); 1931 | } 1932 | 1933 | it1 = i_list.begin(); 1934 | it2 = i_list.end(); 1935 | 1936 | std::advance(it1, 400); 1937 | i_list.erase(it1, it2); 1938 | 1939 | counter = 0; 1940 | 1941 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1942 | { 1943 | ++counter; 1944 | } 1945 | 1946 | failpass("Range-erase with list already third-erased, randomizes erasures", counter == 400 && i_list.size() == 400); 1947 | 1948 | 1949 | 1950 | unsigned int size, range1, range2, internal_loop_counter; 1951 | 1952 | for (unsigned int loop_counter = 0; loop_counter != 50; ++loop_counter) 1953 | { 1954 | i_list.clear(); 1955 | 1956 | for (counter = 0; counter != 1000; ++counter) 1957 | { 1958 | i_list.push_back(counter); 1959 | } 1960 | 1961 | internal_loop_counter = 0; 1962 | 1963 | while (!i_list.empty()) 1964 | { 1965 | it2 = it1 = i_list.begin(); 1966 | 1967 | size = static_cast(i_list.size()); 1968 | range1 = static_cast(rand()) % size; 1969 | range2 = range1 + 1 + (static_cast(rand()) % (size - range1)); 1970 | std::advance(it1, range1); 1971 | std::advance(it2, range2); 1972 | 1973 | i_list.erase(it1, it2); 1974 | 1975 | counter = 0; 1976 | 1977 | for (list::iterator it = i_list.begin(); it != i_list.end(); ++it) 1978 | { 1979 | ++counter; 1980 | } 1981 | 1982 | if (i_list.size() != static_cast(counter)) 1983 | { 1984 | std::cout << "Fail. loop counter: " << loop_counter << ", internal_loop_counter: " << internal_loop_counter << "." << std::endl; 1985 | std::cin.get(); 1986 | abort(); 1987 | } 1988 | 1989 | if (i_list.size() > 2) 1990 | { // Test to make sure our stored erased_locations are valid 1991 | i_list.push_back(1); 1992 | i_list.push_back(10); 1993 | } 1994 | 1995 | ++internal_loop_counter; 1996 | } 1997 | } 1998 | 1999 | failpass("Fuzz-test range-erase randomly until empty", i_list.size() == 0); 2000 | } 2001 | 2002 | 2003 | { 2004 | title2("Sort tests"); 2005 | 2006 | list i_list; 2007 | 2008 | i_list.reserve(50000); 2009 | 2010 | for (unsigned int temp = 0; temp != 50000; ++temp) 2011 | { 2012 | i_list.push_back(rand() & 65535); 2013 | } 2014 | 2015 | i_list.sort(); 2016 | 2017 | bool sorted = true; 2018 | int previous = 0; 2019 | 2020 | for (list::iterator current = i_list.begin(); current != i_list.end(); ++current) 2021 | { 2022 | if (previous > *current) 2023 | { 2024 | sorted = false; 2025 | break; 2026 | } 2027 | 2028 | previous = *current; 2029 | } 2030 | 2031 | failpass("Less-than sort test", sorted); 2032 | 2033 | i_list.sort(std::greater()); 2034 | 2035 | previous = 65536; 2036 | 2037 | for (list::iterator current = i_list.begin(); current != i_list.end(); ++current) 2038 | { 2039 | if (previous < *current) 2040 | { 2041 | sorted = false; 2042 | break; 2043 | } 2044 | 2045 | previous = *current; 2046 | } 2047 | 2048 | failpass("Greater-than sort test", sorted); 2049 | } 2050 | 2051 | 2052 | 2053 | { 2054 | title2("Different insertion-style tests"); 2055 | 2056 | #ifdef PLF_TEST_INITIALIZER_LIST_SUPPORT 2057 | list i_list = {1, 2, 3}; 2058 | 2059 | failpass("Initializer-list constructor test", i_list.size() == 3); 2060 | #else 2061 | list i_list(3, 1); 2062 | #endif 2063 | 2064 | list i_list2(i_list.begin(), i_list.end()); 2065 | 2066 | failpass("Range-based constructor test", i_list2.size() == 3); 2067 | 2068 | list i_list3(5000, 2); 2069 | 2070 | failpass("Fill construction test", i_list3.size() == 5000); 2071 | 2072 | i_list2.insert(i_list2.end(), 500000, 5); 2073 | 2074 | failpass("Fill insertion test", i_list2.size() == 500003); 2075 | 2076 | std::vector some_ints(500, 2); 2077 | 2078 | i_list2.insert(i_list2.begin(), some_ints.begin(), some_ints.end()); 2079 | 2080 | failpass("Fill insertion test", i_list2.size() == 500503); 2081 | 2082 | #ifdef PLF_TEST_INITIALIZER_LIST_SUPPORT 2083 | i_list = {5, 4, 3, 2, 1}; 2084 | 2085 | failpass("Initializer-list operator = test", i_list.size() == 5 && i_list.front() == 5); 2086 | #endif 2087 | 2088 | } 2089 | 2090 | 2091 | #ifdef PLF_TEST_VARIADICS_SUPPORT 2092 | { 2093 | title2("Perfect Forwarding tests"); 2094 | 2095 | list pf_list; 2096 | 2097 | int lvalue = 0; 2098 | int &lvalueref = lvalue; 2099 | 2100 | pf_list.emplace(pf_list.end(), 7, lvalueref); 2101 | 2102 | failpass("Perfect forwarding test", (*pf_list.begin()).success); 2103 | failpass("Perfect forwarding test 2", lvalueref == 1); 2104 | } 2105 | 2106 | 2107 | { 2108 | title2("Basic emplace_back test"); 2109 | 2110 | list ss_list; 2111 | int total1 = 0, total2 = 0; 2112 | 2113 | for (int counter = 0; counter != 100; ++counter) 2114 | { 2115 | ss_list.emplace_back(counter); 2116 | total1 += counter; 2117 | } 2118 | 2119 | for (list::iterator it = ss_list.begin(); it != ss_list.end(); ++it) 2120 | { 2121 | total2 += it->number; 2122 | } 2123 | 2124 | failpass("Basic emplace test", total1 == total2); 2125 | failpass("Basic emplace test 2", ss_list.size() == 100); 2126 | } 2127 | #endif 2128 | 2129 | 2130 | { 2131 | title2("Shrink_to_fit when empty test"); 2132 | 2133 | list bug; 2134 | bug.shrink_to_fit(); // Shouldn't crash here 2135 | } 2136 | 2137 | 2138 | } 2139 | 2140 | 2141 | title1("All tests passed! Press Enter to Exit."); 2142 | std::cin.get(); 2143 | 2144 | return 0; 2145 | } 2146 | --------------------------------------------------------------------------------