RavEngine
Loading...
Searching...
No Matches
plf_list.h
1// Copyright (c) 2020, 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#define PLF_LIST_BLOCK_MIN static_cast<group_size_type>((sizeof(node) * 8 > (sizeof(*this) + sizeof(group)) * 2) ? 8 : (((sizeof(*this) + sizeof(group)) * 2) / sizeof(node)) + 1)
26#define PLF_LIST_BLOCK_MAX 2048
27
28
29
30// Compiler-specific defines used by list:
31
32#if defined(_MSC_VER)
33 #define PLF_LIST_FORCE_INLINE __forceinline
34
35 #if _MSC_VER >= 1900
36 #define PLF_LIST_ALIGNMENT_SUPPORT
37 #define PLF_LIST_NOEXCEPT noexcept
38 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_swap::value || std::allocator_traits<the_allocator>::is_always_equal::value)
39 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_move_assignment::value || std::allocator_traits<the_allocator>::is_always_equal::value)
40 #else
41 #define PLF_LIST_NOEXCEPT throw()
42 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator)
43 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) throw()
44 #endif
45
46 #if _MSC_VER >= 1600
47 #define PLF_LIST_MOVE_SEMANTICS_SUPPORT
48 #endif
49 #if _MSC_VER >= 1700
50 #define PLF_LIST_TYPE_TRAITS_SUPPORT
51 #define PLF_LIST_ALLOCATOR_TRAITS_SUPPORT
52 #endif
53 #if _MSC_VER >= 1800
54 #define PLF_LIST_VARIADICS_SUPPORT // Variadics, in this context, means both variadic templates and variadic macros are supported
55 #define PLF_LIST_INITIALIZER_LIST_SUPPORT
56 #endif
57
58 #if defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)
59 #define PLF_LIST_CONSTEXPR constexpr
60 #else
61 #define PLF_LIST_CONSTEXPR
62 #endif
63 #if defined(_MSVC_LANG) && (_MSVC_LANG > 201703L)
64 #define PLF_LIST_CPP20_SUPPORT
65 #endif
66#elif defined(__cplusplus) && __cplusplus >= 201103L // C++11 support, at least
67 #define PLF_LIST_FORCE_INLINE // note: GCC creates faster code without forcing inline
68 #define PLF_LIST_MOVE_SEMANTICS_SUPPORT
69
70 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__clang__) // If compiler is GCC/G++
71 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4 // 4.2 and below do not support variadic templates
72 #define PLF_LIST_VARIADICS_SUPPORT
73 #endif
74 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4 // 4.3 and below do not support initializer lists
75 #define PLF_LIST_INITIALIZER_LIST_SUPPORT
76 #endif
77 #if (__GNUC__ == 4 && __GNUC_MINOR__ < 6) || __GNUC__ < 4
78 #define PLF_LIST_NOEXCEPT throw()
79 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator)
80 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator)
81 #elif __GNUC__ < 6
82 #define PLF_LIST_NOEXCEPT noexcept
83 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept
84 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator) noexcept
85 #else // C++17 support
86 #define PLF_LIST_NOEXCEPT noexcept
87 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_move_assignment::value || std::allocator_traits<the_allocator>::is_always_equal::value)
88 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_swap::value || std::allocator_traits<the_allocator>::is_always_equal::value)
89 #endif
90 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4
91 #define PLF_LIST_ALLOCATOR_TRAITS_SUPPORT
92 #endif
93 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4
94 #define PLF_LIST_ALIGNMENT_SUPPORT
95 #endif
96 #if __GNUC__ >= 5 // GCC v4.9 and below do not support std::is_trivially_copyable
97 #define PLF_LIST_TYPE_TRAITS_SUPPORT
98 #endif
99 #elif defined(__GLIBCXX__) // Using another compiler type with libstdc++ - we are assuming full c++11 compliance for compiler - which may not be true
100 #if __GLIBCXX__ >= 20080606 // libstdc++ 4.2 and below do not support variadic templates
101 #define PLF_LIST_VARIADICS_SUPPORT
102 #endif
103 #if __GLIBCXX__ >= 20090421 // libstdc++ 4.3 and below do not support initializer lists
104 #define PLF_LIST_INITIALIZER_LIST_SUPPORT
105 #endif
106 #if __GLIBCXX__ >= 20160111
107 #define PLF_LIST_ALLOCATOR_TRAITS_SUPPORT
108 #define PLF_LIST_NOEXCEPT noexcept
109 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_move_assignment::value || std::allocator_traits<the_allocator>::is_always_equal::value)
110 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_swap::value || std::allocator_traits<the_allocator>::is_always_equal::value)
111 #elif __GLIBCXX__ >= 20120322
112 #define PLF_LIST_ALLOCATOR_TRAITS_SUPPORT
113 #define PLF_LIST_NOEXCEPT noexcept
114 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept
115 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator) noexcept
116 #else
117 #define PLF_LIST_NOEXCEPT throw()
118 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator)
119 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator)
120 #endif
121 #if __GLIBCXX__ >= 20130322
122 #define PLF_LIST_ALIGNMENT_SUPPORT
123 #endif
124 #if __GLIBCXX__ >= 20150422 // libstdc++ v4.9 and below do not support std::is_trivially_copyable
125 #define PLF_LIST_TYPE_TRAITS_SUPPORT
126 #endif
127 #elif (defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) || defined(_LIBCPP_HAS_NO_VARIADICS)) // Special case for checking C++11 support with libCPP
128 #define PLF_LIST_NOEXCEPT throw()
129 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator)
130 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator)
131 #else // Assume type traits and initializer support for other compilers and standard libraries
132 #define PLF_LIST_VARIADICS_SUPPORT
133 #define PLF_LIST_TYPE_TRAITS_SUPPORT
134 #define PLF_LIST_MOVE_SEMANTICS_SUPPORT
135 #define PLF_LIST_ALLOCATOR_TRAITS_SUPPORT
136 #define PLF_LIST_ALIGNMENT_SUPPORT
137 #define PLF_LIST_INITIALIZER_LIST_SUPPORT
138 #define PLF_LIST_NOEXCEPT noexcept
139 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept(std::allocator_traits<the_allocator>::is_always_equal::value)
140 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator) noexcept
141 #endif
142
143 #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-GNU/cland compilers
144 #define PLF_LIST_CONSTEXPR constexpr
145 #else
146 #define PLF_LIST_CONSTEXPR
147 #endif
148 #if __cplusplus > 201703L && ((defined(__clang__) && (__clang_major__ >= 10)) || (defined(__GNUC__) && __GNUC__ >= 10) || (!defined(__clang__) && !defined(__GNUC__))) // assume correct C++20 implementation for other compilers
149 #define PLF_LIST_CPP20_SUPPORT
150 #endif
151#else
152 #define PLF_LIST_FORCE_INLINE
153 #define PLF_LIST_NOEXCEPT throw()
154 #define PLF_LIST_NOEXCEPT_SWAP(the_allocator)
155 #define PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator)
156 #define PLF_LIST_CONSTEXPR
157#endif
158
159
160
161#ifdef PLF_LIST_ALLOCATOR_TRAITS_SUPPORT
162 #ifdef PLF_LIST_VARIADICS_SUPPORT
163 #define PLF_LIST_CONSTRUCT(the_allocator, allocator_instance, location, ...) std::allocator_traits<the_allocator>::construct(allocator_instance, location, __VA_ARGS__)
164 #else
165 #define PLF_LIST_CONSTRUCT(the_allocator, allocator_instance, location, data) std::allocator_traits<the_allocator>::construct(allocator_instance, location, data)
166 #endif
167
168 #define PLF_LIST_DESTROY(the_allocator, allocator_instance, location) std::allocator_traits<the_allocator>::destroy(allocator_instance, location)
169 #define PLF_LIST_ALLOCATE(the_allocator, allocator_instance, size, hint) std::allocator_traits<the_allocator>::allocate(allocator_instance, size, hint)
170 #define PLF_LIST_ALLOCATE_INITIALIZATION(the_allocator, size, hint) std::allocator_traits<the_allocator>::allocate(*this, size, hint)
171 #define PLF_LIST_DEALLOCATE(the_allocator, allocator_instance, location, size) std::allocator_traits<the_allocator>::deallocate(allocator_instance, location, size)
172#else
173 #ifdef PLF_LIST_VARIADICS_SUPPORT
174 #define PLF_LIST_CONSTRUCT(the_allocator, allocator_instance, location, ...) allocator_instance.construct(location, __VA_ARGS__)
175 #else
176 #define PLF_LIST_CONSTRUCT(the_allocator, allocator_instance, location, data) allocator_instance.construct(location, data)
177 #endif
178
179 #define PLF_LIST_DESTROY(the_allocator, allocator_instance, location) allocator_instance.destroy(location)
180 #define PLF_LIST_ALLOCATE(the_allocator, allocator_instance, size, hint) allocator_instance.allocate(size, hint)
181 #define PLF_LIST_ALLOCATE_INITIALIZATION(the_allocator, size, hint) the_allocator::allocate(size, hint)
182 #define PLF_LIST_DEALLOCATE(the_allocator, allocator_instance, location, size) allocator_instance.deallocate(location, size)
183#endif
184
185
186
187
188#include <cstring> // memmove, memcpy
189#include <cassert> // assert
190#include <limits> // std::numeric_limits
191#include <memory> // std::uninitialized_copy, std::allocator
192#include <iterator> // std::bidirectional_iterator_tag
193
194
195#ifndef GFX_TIMSORT_HPP
196 #include <algorithm> // std::sort
197#endif
198
199#ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
200 #include <type_traits> // std::is_trivially_destructible, etc
201#endif
202
203#ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
204 #include <utility> // std::move
205#endif
206
207#ifdef PLF_LIST_INITIALIZER_LIST_SUPPORT
208 #include <initializer_list>
209#endif
210
211
212
213
214namespace plf
215{
216
217
218
219template <class element_type, class element_allocator_type = std::allocator<element_type> > class list : private element_allocator_type
220{
221public:
222 // Standard container typedefs:
223 typedef element_type value_type;
224 typedef element_allocator_type allocator_type;
225 typedef unsigned short group_size_type;
226
227 #ifdef PLF_LIST_ALLOCATOR_TRAITS_SUPPORT // >= C++11
228 typedef typename std::allocator_traits<element_allocator_type>::size_type size_type;
229 typedef typename std::allocator_traits<element_allocator_type>::difference_type difference_type;
230 typedef element_type & reference;
231 typedef const element_type & const_reference;
232 typedef typename std::allocator_traits<element_allocator_type>::pointer pointer;
233 typedef typename std::allocator_traits<element_allocator_type>::const_pointer const_pointer;
234 #else
235 typedef typename element_allocator_type::size_type size_type;
236 typedef typename element_allocator_type::difference_type difference_type;
237 typedef typename element_allocator_type::reference reference;
238 typedef typename element_allocator_type::const_reference const_reference;
239 typedef typename element_allocator_type::pointer pointer;
240 typedef typename element_allocator_type::const_pointer const_pointer;
241 #endif
242
243
244 // Iterator declarations:
245 template <bool is_const> class list_iterator;
248 friend class list_iterator<false>; // Using 'iterator' typedef name here is illegal under C++03
249 friend class list_iterator<true>;
250
251 template <bool is_const> class list_reverse_iterator;
254 friend class list_reverse_iterator<false>;
255 friend class list_reverse_iterator<true>;
256
257
258private:
259 struct group; // forward declarations for typedefs below
260 struct node;
261
262 #ifdef PLF_LIST_ALLOCATOR_TRAITS_SUPPORT // >= C++11
263 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<group> group_allocator_type;
264 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<node> node_allocator_type;
265 typedef typename std::allocator_traits<group_allocator_type>::pointer group_pointer_type;
266 typedef typename std::allocator_traits<node_allocator_type>::pointer node_pointer_type;
267 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<node_pointer_type> node_pointer_allocator_type;
268 #else
269 typedef typename element_allocator_type::template rebind<group>::other group_allocator_type;
270 typedef typename element_allocator_type::template rebind<node>::other node_allocator_type;
271 typedef typename group_allocator_type::pointer group_pointer_type;
272 typedef typename node_allocator_type::pointer node_pointer_type;
273 typedef typename element_allocator_type::template rebind<node_pointer_type>::other node_pointer_allocator_type;
274 #endif
275
276
277
278 struct node_base
279 {
280 node_pointer_type next, previous;
281
282 node_base()
283 {}
284
285 node_base(const node_pointer_type &n, const node_pointer_type &p):
286 next(n),
287 previous(p)
288 {}
289
290
291 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
292 node_base(node_pointer_type &&n, node_pointer_type &&p) PLF_LIST_NOEXCEPT:
293 next(std::move(n)),
294 previous(std::move(p))
295 {}
296 #endif
297 };
298
299
300
301 struct node : public node_base
302 {
303 element_type element;
304
305 node(const node_pointer_type next, const node_pointer_type previous, const element_type &source):
306 node_base(next, previous),
307 element(source)
308 {}
309
310
311 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
312 node(node_pointer_type &&next, node_pointer_type &&previous, element_type &&source) PLF_LIST_NOEXCEPT:
313 node_base(std::move(next), std::move(previous)),
314 element(std::move(source))
315 {}
316 #endif
317
318
319 #ifdef PLF_LIST_VARIADICS_SUPPORT
320 template<typename... arguments>
321 node(node_pointer_type const next, node_pointer_type const previous, arguments&&... parameters):
322 node_base(next, previous),
323 element(std::forward<arguments>(parameters) ...)
324 {}
325 #endif
326 };
327
328
329
330 struct group : public node_allocator_type // Node memory block + metadata
331 {
332 node_pointer_type nodes;
333 node_pointer_type free_list_head;
334 node_pointer_type beyond_end;
335 group_size_type number_of_elements;
336
337
338 group() PLF_LIST_NOEXCEPT:
339 nodes(NULL),
340 free_list_head(NULL),
341 beyond_end(NULL),
342 number_of_elements(0)
343 {}
344
345
346 #if defined(PLF_LIST_VARIADICS_SUPPORT) || defined(PLF_LIST_MOVE_SEMANTICS_SUPPORT)
347 group(const group_size_type group_size, node_pointer_type const previous = NULL):
348 nodes(PLF_LIST_ALLOCATE_INITIALIZATION(node_allocator_type, group_size, previous)),
349 free_list_head(NULL),
350 beyond_end(nodes + group_size),
351 number_of_elements(0)
352 {}
353 #else
354 // 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.
355 group(const group_size_type group_size, node_pointer_type const previous = NULL) PLF_LIST_NOEXCEPT:
356 nodes(NULL),
357 free_list_head(previous),
358 beyond_end(NULL),
359 number_of_elements(group_size)
360 {}
361
362 // Not a real copy constructor ie. actually a move constructor. Only used for allocator.construct in C++03 for reasons stated above:
363 group(const group &source):
364 node_allocator_type(source),
365 nodes(PLF_LIST_ALLOCATE_INITIALIZATION(node_allocator_type, source.number_of_elements, source.free_list_head)),
366 free_list_head(NULL),
367 beyond_end(nodes + source.number_of_elements),
368 number_of_elements(0)
369 {}
370 #endif
371
372
373 group & operator = (const group &source) PLF_LIST_NOEXCEPT // Actually a move operator, used by c++03 in group_vector's remove, expand_capacity and append
374 {
375 nodes = source.nodes;
376 free_list_head = source.free_list_head;
377 beyond_end = source.beyond_end;
378 number_of_elements = source.number_of_elements;
379 return *this;
380 }
381
382
383 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
384 group(group &&source) PLF_LIST_NOEXCEPT:
385 node_allocator_type(source),
386 nodes(std::move(source.nodes)),
387 free_list_head(std::move(source.free_list_head)),
388 beyond_end(std::move(source.beyond_end)),
389 number_of_elements(source.number_of_elements)
390 {
391 source.nodes = NULL;
392 source.beyond_end = NULL;
393 }
394
395
396 group & operator = (group &&source) PLF_LIST_NOEXCEPT
397 {
398 nodes = std::move(source.nodes);
399 free_list_head = std::move(source.free_list_head);
400 beyond_end = std::move(source.beyond_end);
401 number_of_elements = std::move(source.number_of_elements);
402 source.nodes = NULL;
403 source.beyond_end = NULL;
404 return *this;
405 }
406 #endif
407
408
409 ~group() PLF_LIST_NOEXCEPT
410 {
411 PLF_LIST_DEALLOCATE(node_allocator_type, (*this), nodes, static_cast<size_type>(beyond_end - nodes));
412 }
413 };
414
415
416
417
418 class group_vector : private node_pointer_allocator_type // Simple vector of groups + associated functions
419 {
420 public:
421 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.
422 size_type size;
423
424
425 struct ebco_pair2 : allocator_type // empty-base-class optimisation
426 {
427 size_type capacity; // Total element capacity of all initialized groups
428 explicit ebco_pair2(const size_type number_of_elements) PLF_LIST_NOEXCEPT: capacity(number_of_elements) {};
429 } element_allocator_pair;
430
431 struct ebco_pair : group_allocator_type
432 {
433 size_type capacity; // Total group capacity
434 explicit ebco_pair(const size_type number_of_groups) PLF_LIST_NOEXCEPT: capacity(number_of_groups) {};
435 } group_allocator_pair;
436
437
438
439 group_vector() PLF_LIST_NOEXCEPT:
440 node_pointer_allocator_type(node_pointer_allocator_type()),
441 last_endpoint_group(NULL),
442 block_pointer(NULL),
443 last_searched_group(NULL),
444 size(0),
445 element_allocator_pair(0),
446 group_allocator_pair(0)
447 {}
448
449
450
451 inline PLF_LIST_FORCE_INLINE void blank() PLF_LIST_NOEXCEPT
452 {
453 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
454 if PLF_LIST_CONSTEXPR (std::is_trivial<group_pointer_type>::value)
455 {
456 std::memset(static_cast<void *>(this), 0, sizeof(group_vector));
457 }
458 else
459 #endif
460 {
461 last_endpoint_group = NULL;
462 block_pointer = NULL;
463 last_searched_group = NULL;
464 size = 0;
465 element_allocator_pair.capacity = 0;
466 group_allocator_pair.capacity = 0;
467 }
468 }
469
470
471
472 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
473 group_vector(group_vector &&source) PLF_LIST_NOEXCEPT:
474 last_endpoint_group(std::move(source.last_endpoint_group)),
475 block_pointer(std::move(source.block_pointer)),
476 last_searched_group(std::move(source.last_searched_group)),
477 size(source.size),
478 element_allocator_pair(source.element_allocator_pair.capacity),
479 group_allocator_pair(source.group_allocator_pair.capacity)
480 {
481 source.blank();
482 }
483
484
485 group_vector & operator = (group_vector &&source) PLF_LIST_NOEXCEPT
486 {
487 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
488 if PLF_LIST_CONSTEXPR (std::is_trivial<group_pointer_type>::value)
489 {
490 std::memcpy(static_cast<void *>(this), &source, sizeof(group_vector));
491 }
492 else
493 #endif
494 {
495 last_endpoint_group = std::move(source.last_endpoint_group);
496 block_pointer = std::move(source.block_pointer);
497 last_searched_group = std::move(source.last_searched_group);
498 size = source.size;
499 element_allocator_pair.capacity = source.element_allocator_pair.capacity;
500 group_allocator_pair.capacity = source.group_allocator_pair.capacity;
501 }
502
503 source.blank();
504 return *this;
505 }
506 #endif
507
508
509
510 ~group_vector() PLF_LIST_NOEXCEPT
511 {}
512
513
514
515 void destroy_all_data(const node_pointer_type last_endpoint_node) PLF_LIST_NOEXCEPT
516 {
517 if (block_pointer == NULL)
518 {
519 return;
520 }
521
522 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
523 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<element_type>::value || !std::is_trivially_destructible<node_pointer_type>::value)
524 #endif
525 {
526 if (last_endpoint_node != NULL)
527 {
528 clear(last_endpoint_node);
529 }
530 }
531
532 const group_pointer_type end_group = block_pointer + size;
533 for (group_pointer_type current_group = block_pointer; current_group != end_group; ++current_group)
534 {
535 PLF_LIST_DESTROY(group_allocator_type, group_allocator_pair, current_group);
536 }
537
538 PLF_LIST_DEALLOCATE(group_allocator_type, group_allocator_pair, block_pointer, group_allocator_pair.capacity);
539 blank();
540 }
541
542
543
544 void clear(const node_pointer_type last_endpoint_node) PLF_LIST_NOEXCEPT
545 {
546 for (group_pointer_type current_group = block_pointer; current_group != last_endpoint_group; ++current_group)
547 {
548 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
549 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<element_type>::value || !std::is_trivially_destructible<node_pointer_type>::value)
550 #endif
551 {
552 const node_pointer_type end = current_group->beyond_end;
553
554 if ((end - current_group->nodes) != current_group->number_of_elements) // If there are erased nodes present in the group
555 {
556 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
557 {
558 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
559 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
560 #endif
561 {
562 if (current_node->next != NULL) // ie. is not part of free list
563 {
564 PLF_LIST_DESTROY(element_allocator_type, element_allocator_pair, &(current_node->element));
565 }
566 }
567
568 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
569 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
570 #endif
571 {
572 PLF_LIST_DESTROY(node_pointer_allocator_type, (*this), &(current_node->next));
573 PLF_LIST_DESTROY(node_pointer_allocator_type, (*this), &(current_node->previous));
574 }
575 }
576 }
577 else
578 {
579 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
580 {
581 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
582 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
583 #endif
584 {
585 PLF_LIST_DESTROY(element_allocator_type, element_allocator_pair, &(current_node->element));
586 }
587
588 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
589 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
590 #endif
591 {
592 PLF_LIST_DESTROY(node_pointer_allocator_type, (*this), &(current_node->next));
593 PLF_LIST_DESTROY(node_pointer_allocator_type, (*this), &(current_node->previous));
594 }
595 }
596 }
597 }
598
599 current_group->free_list_head = NULL;
600 current_group->number_of_elements = 0;
601 }
602
603 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
604 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<element_type>::value || !std::is_trivially_destructible<node_pointer_type>::value)
605 #endif
606 {
607 if ((last_endpoint_node - last_endpoint_group->nodes) != last_endpoint_group->number_of_elements) // If there are erased nodes present in the group
608 {
609 for (node_pointer_type current_node = last_endpoint_group->nodes; current_node != last_endpoint_node; ++current_node)
610 {
611 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
612 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
613 #endif
614 {
615 if (current_node->next != NULL) // is not part of free list ie. element has not already had it's destructor called
616 {
617 PLF_LIST_DESTROY(element_allocator_type, element_allocator_pair, &(current_node->element));
618 }
619 }
620
621 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
622 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
623 #endif
624 {
625 PLF_LIST_DESTROY(node_pointer_allocator_type, (*this), &(current_node->next));
626 PLF_LIST_DESTROY(node_pointer_allocator_type, (*this), &(current_node->previous));
627 }
628 }
629 }
630 else
631 {
632 for (node_pointer_type current_node = last_endpoint_group->nodes; current_node != last_endpoint_node; ++current_node)
633 {
634 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
635 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
636 #endif
637 {
638 PLF_LIST_DESTROY(element_allocator_type, element_allocator_pair, &(current_node->element));
639 }
640
641 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
642 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
643 #endif
644 {
645 PLF_LIST_DESTROY(node_pointer_allocator_type, (*this), &(current_node->next));
646 PLF_LIST_DESTROY(node_pointer_allocator_type, (*this), &(current_node->previous));
647 }
648 }
649 }
650 }
651
652 last_endpoint_group->free_list_head = NULL;
653 last_endpoint_group->number_of_elements = 0;
654 last_searched_group = last_endpoint_group = block_pointer;
655 }
656
657
658
659 void expand_capacity(const size_type new_capacity) // used by add_new and append
660 {
661 group_pointer_type const old_block = block_pointer;
662 block_pointer = PLF_LIST_ALLOCATE(group_allocator_type, group_allocator_pair, new_capacity, 0);
663
664 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
665 if PLF_LIST_CONSTEXPR (std::is_trivially_copyable<node_pointer_type>::value && std::is_trivially_destructible<node_pointer_type>::value)
666 { // Dereferencing here in order to deal with smart pointer situations ie. obtaining the raw pointer from the smart pointer
667 std::memcpy(static_cast<void *>(&*block_pointer), static_cast<void *>(&*old_block), sizeof(group) * size); // static_cast or reinterpret_cast necessary to deal with GCC 8 warnings
668 }
669 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
670 else if PLF_LIST_CONSTEXPR (std::is_move_constructible<node_pointer_type>::value)
671 {
672 std::uninitialized_copy(std::make_move_iterator(old_block), std::make_move_iterator(old_block + size), block_pointer);
673 }
674 #endif
675 else
676 #endif
677 {
678 // 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:
679 const group_pointer_type beyond_end = old_block + size;
680 group_pointer_type current_new_group = block_pointer;
681
682 for (group_pointer_type current_group = old_block; current_group != beyond_end; ++current_group)
683 {
684 *(current_new_group++) = *(current_group);
685
686 current_group->nodes = NULL;
687 current_group->beyond_end = NULL;
688 PLF_LIST_DESTROY(group_allocator_type, group_allocator_pair, current_group);
689 }
690 }
691
692 last_searched_group = block_pointer + (last_searched_group - old_block); // correct pointer post-reallocation
693 PLF_LIST_DEALLOCATE(group_allocator_type, group_allocator_pair, old_block, group_allocator_pair.capacity);
694 group_allocator_pair.capacity = new_capacity;
695 }
696
697
698
699 void add_new(const group_size_type group_size)
700 {
701 if (group_allocator_pair.capacity == size)
702 {
703 expand_capacity(group_allocator_pair.capacity * 2);
704 }
705
706 last_endpoint_group = block_pointer + size - 1;
707
708 #ifdef PLF_LIST_VARIADICS_SUPPORT
709 PLF_LIST_CONSTRUCT(group_allocator_type, group_allocator_pair, last_endpoint_group + 1, group_size, last_endpoint_group->nodes);
710 #else
711 PLF_LIST_CONSTRUCT(group_allocator_type, group_allocator_pair, last_endpoint_group + 1, group(group_size, last_endpoint_group->nodes));
712 #endif
713
714 ++last_endpoint_group; // Doing this here instead of pre-construct to avoid need for a try-catch block
715 element_allocator_pair.capacity += group_size;
716 ++size;
717 }
718
719
720
721 void initialize(const group_size_type group_size) // For adding first group *only* when group vector is completely empty and block_pointer is NULL
722 {
723 last_endpoint_group = block_pointer = last_searched_group = PLF_LIST_ALLOCATE(group_allocator_type, group_allocator_pair, 1, 0);
724 group_allocator_pair.capacity = 1;
725
726 #ifdef PLF_LIST_VARIADICS_SUPPORT
727 PLF_LIST_CONSTRUCT(group_allocator_type, group_allocator_pair, last_endpoint_group, group_size);
728 #else
729 PLF_LIST_CONSTRUCT(group_allocator_type, group_allocator_pair, last_endpoint_group, group(group_size));
730 #endif
731
732 size = 1; // Doing these here instead of pre-construct to avoid need for a try-catch block
733 element_allocator_pair.capacity = group_size;
734 }
735
736
737
738 void remove(group_pointer_type const group_to_erase) PLF_LIST_NOEXCEPT
739 {
740 if (last_searched_group >= group_to_erase && last_searched_group != block_pointer)
741 {
742 --last_searched_group;
743 }
744
745 element_allocator_pair.capacity -= static_cast<size_type>(group_to_erase->beyond_end - group_to_erase->nodes);
746
747 PLF_LIST_DESTROY(group_allocator_type, group_allocator_pair, group_to_erase);
748
749 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
750 if PLF_LIST_CONSTEXPR (std::is_trivially_copyable<node_pointer_type>::value && std::is_trivially_destructible<node_pointer_type>::value)
751 { // Dereferencing here in order to deal with smart pointer situations ie. obtaining the raw pointer from the smart pointer
752 std::memmove(static_cast<void *>(&*group_to_erase), static_cast<void *>(&*group_to_erase + 1), sizeof(group) * (--size - static_cast<size_type>(&*group_to_erase - &*block_pointer)));
753 }
754 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
755 else if PLF_LIST_CONSTEXPR (std::is_move_constructible<node_pointer_type>::value)
756 {
757 std::move(group_to_erase + 1, block_pointer + size--, group_to_erase);
758 }
759 #endif
760 else
761 #endif
762 {
763 group_pointer_type back = block_pointer + size--;
764 std::copy(group_to_erase + 1, back--, group_to_erase);
765
766 back->nodes = NULL;
767 back->beyond_end = NULL;
768 PLF_LIST_DESTROY(group_allocator_type, group_allocator_pair, back);
769 }
770 }
771
772
773
774 void move_to_back(group_pointer_type const group_to_erase)
775 {
776 if (last_searched_group >= group_to_erase && last_searched_group != block_pointer)
777 {
778 --last_searched_group;
779 }
780
781 group *temp_group = PLF_LIST_ALLOCATE(group_allocator_type, group_allocator_pair, 1, NULL);
782
783 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
784 if PLF_LIST_CONSTEXPR (std::is_trivially_copyable<node_pointer_type>::value && std::is_trivially_destructible<node_pointer_type>::value)
785 {
786 std::memcpy(static_cast<void *>(&*temp_group), static_cast<void *>(&*group_to_erase), sizeof(group));
787 std::memmove(static_cast<void *>(&*group_to_erase), static_cast<void *>(&*group_to_erase + 1), sizeof(group) * ((size - 1) - static_cast<size_type>(&*group_to_erase - &*block_pointer)));
788 std::memcpy(static_cast<void *>(&*(block_pointer + size - 1)), static_cast<void *>(&*temp_group), sizeof(group));
789 }
790 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
791 else if PLF_LIST_CONSTEXPR (std::is_move_constructible<node_pointer_type>::value)
792 {
793 PLF_LIST_CONSTRUCT(group_allocator_type, group_allocator_pair, temp_group, std::move(*group_to_erase));
794 std::move(group_to_erase + 1, block_pointer + size, group_to_erase);
795 *(block_pointer + size - 1) = std::move(*temp_group);
796
797 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
798 {
799 PLF_LIST_DESTROY(group_allocator_type, group_allocator_pair, temp_group);
800 }
801 }
802 #endif
803 else
804 #endif
805 {
806 PLF_LIST_CONSTRUCT(group_allocator_type, group_allocator_pair, temp_group, group());
807
808 *temp_group = *group_to_erase;
809 std::copy(group_to_erase + 1, block_pointer + size, group_to_erase);
810 *(block_pointer + --size) = *temp_group;
811
812 temp_group->nodes = NULL;
813 PLF_LIST_DESTROY(group_allocator_type, group_allocator_pair, temp_group);
814 }
815
816 PLF_LIST_DEALLOCATE(group_allocator_type, group_allocator_pair, temp_group, 1);
817 }
818
819
820
821 group_pointer_type get_nearest_freelist_group(const node_pointer_type location_node) PLF_LIST_NOEXCEPT // In working implementation this cannot throw
822 {
823 const group_pointer_type beyond_end_group = last_endpoint_group + 1;
824 group_pointer_type left = last_searched_group - 1, right = last_searched_group + 1, freelist_group = NULL;
825 bool right_not_beyond_back = (right < beyond_end_group);
826 bool left_not_beyond_front = (left >= block_pointer);
827
828
829 if (location_node >= last_searched_group->nodes && location_node < last_searched_group->beyond_end) // ie. location is within last_search_group
830 {
831 if (last_searched_group->free_list_head != NULL) // if last_searched_group has previously-erased nodes
832 {
833 return last_searched_group;
834 }
835 }
836 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:
837 {
838 group_pointer_type closest_freelist_left = (last_searched_group->free_list_head == NULL) ? NULL : last_searched_group, closest_freelist_right = (last_searched_group->free_list_head == NULL) ? NULL : last_searched_group;
839
840 while (true)
841 {
842 if (right_not_beyond_back)
843 {
844 if ((location_node < right->beyond_end) && (location_node >= right->nodes)) // location_node's group is found
845 {
846 if (right->free_list_head != NULL) // group has erased nodes, reuse them:
847 {
848 last_searched_group = right;
849 return right;
850 }
851
852 difference_type left_distance;
853
854 if (closest_freelist_right != NULL)
855 {
856 last_searched_group = right;
857 left_distance = right - closest_freelist_right;
858
859 if (left_distance <= 2) // ie. this group is close enough to location_node's group
860 {
861 return closest_freelist_right;
862 }
863
864 freelist_group = closest_freelist_right;
865 }
866 else
867 {
868 last_searched_group = right;
869 left_distance = right - left;
870 }
871
872
873 // Otherwise find closest group with freelist - check an equal distance on the right to the distance we've checked on the left:
874 const group_pointer_type end_group = (((right + left_distance) > beyond_end_group) ? beyond_end_group : (right + left_distance - 1));
875
876 while (++right != end_group)
877 {
878 if (right->free_list_head != NULL)
879 {
880 return right;
881 }
882 }
883
884 if (freelist_group != NULL)
885 {
886 return freelist_group;
887 }
888
889 right_not_beyond_back = (right < beyond_end_group);
890 break; // group with reusable erased nodes not found yet, continue searching in loop below
891 }
892
893 if (right->free_list_head != NULL) // location_node's group not found, but a reusable location found
894 {
895 if ((closest_freelist_right == NULL) & (closest_freelist_left == NULL))
896 {
897 closest_freelist_left = right;
898 }
899
900 closest_freelist_right = right;
901 }
902
903 right_not_beyond_back = (++right < beyond_end_group);
904 }
905
906
907 if (left_not_beyond_front)
908 {
909 if ((location_node >= left->nodes) && (location_node < left->beyond_end))
910 {
911 if (left->free_list_head != NULL)
912 {
913 last_searched_group = left;
914 return left;
915 }
916
917 difference_type right_distance;
918
919 if (closest_freelist_left != NULL)
920 {
921 last_searched_group = left;
922 right_distance = closest_freelist_left - left;
923
924 if (right_distance <= 2)
925 {
926 return closest_freelist_left;
927 }
928
929 freelist_group = closest_freelist_left;
930 }
931 else
932 {
933 last_searched_group = left;
934 right_distance = right - left;
935 }
936
937 // Otherwise find closest group with freelist:
938 const group_pointer_type end_group = (((left - right_distance) < block_pointer) ? block_pointer - 1 : (left - right_distance) + 1);
939
940 while (--left != end_group)
941 {
942 if (left->free_list_head != NULL)
943 {
944 return left;
945 }
946 }
947
948 if (freelist_group != NULL)
949 {
950 return freelist_group;
951 }
952
953 left_not_beyond_front = (left >= block_pointer);
954 break;
955 }
956
957 if (left->free_list_head != NULL)
958 {
959 if ((closest_freelist_left == NULL) & (closest_freelist_right == NULL))
960 {
961 closest_freelist_right = left;
962 }
963
964 closest_freelist_left = left;
965 }
966
967 left_not_beyond_front = (--left >= block_pointer);
968 }
969 }
970 }
971
972
973 // 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:
974 while (true)
975 {
976 if (right_not_beyond_back)
977 {
978 if (right->free_list_head != NULL)
979 {
980 return right;
981 }
982
983 right_not_beyond_back = (++right < beyond_end_group);
984 }
985
986 if (left_not_beyond_front)
987 {
988 if (left->free_list_head != NULL)
989 {
990 return left;
991 }
992
993 left_not_beyond_front = (--left >= block_pointer);
994 }
995 }
996
997 // Will never reach here on a functioning implementation
998 }
999
1000
1001
1002 void swap(group_vector &source) PLF_LIST_NOEXCEPT_SWAP(group_allocator_type)
1003 {
1004 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
1005 if PLF_LIST_CONSTEXPR (std::is_trivial<group_pointer_type>::value) // if pointer type is trivial we can just copy using memcpy - faster - avoids constructors/destructors etc
1006 {
1007 char temp[sizeof(group_vector)];
1008 std::memcpy(static_cast<void *>(&temp), static_cast<void *>(this), sizeof(group_vector));
1009 std::memcpy(static_cast<void *>(this), static_cast<void *>(&source), sizeof(group_vector));
1010 std::memcpy(static_cast<void *>(&source), static_cast<void *>(&temp), sizeof(group_vector));
1011 }
1012 else
1013 #endif
1014 {
1015 const group_pointer_type swap_last_endpoint_group = last_endpoint_group, swap_block_pointer = block_pointer, swap_last_searched_group = last_searched_group;
1016 const size_type swap_size = size, swap_element_capacity = element_allocator_pair.capacity, swap_capacity = group_allocator_pair.capacity;
1017
1018 last_endpoint_group = source.last_endpoint_group;
1019 block_pointer = source.block_pointer;
1020 last_searched_group = source.last_searched_group;
1021 size = source.size;
1022 element_allocator_pair.capacity = source.element_allocator_pair.capacity;
1023 group_allocator_pair.capacity = source.group_allocator_pair.capacity;
1024
1025 source.last_endpoint_group = swap_last_endpoint_group;
1026 source.block_pointer = swap_block_pointer;
1027 source.last_searched_group = swap_last_searched_group;
1028 source.size = swap_size;
1029 source.element_allocator_pair.capacity = swap_element_capacity;
1030 source.group_allocator_pair.capacity = swap_capacity;
1031 }
1032 }
1033
1034
1035
1036 void trim_trailing_groups() PLF_LIST_NOEXCEPT
1037 {
1038 const group_pointer_type beyond_last = block_pointer + size;
1039
1040 for (group_pointer_type current_group = last_endpoint_group + 1; current_group != beyond_last; ++current_group)
1041 {
1042 element_allocator_pair.capacity -= static_cast<size_type>(current_group->beyond_end - current_group->nodes);
1043 PLF_LIST_DESTROY(group_allocator_type, group_allocator_pair, current_group);
1044 }
1045
1046 size -= static_cast<size_type>(beyond_last - (last_endpoint_group + 1));
1047 }
1048
1049
1050
1051 void append(group_vector &source)
1052 {
1053 source.trim_trailing_groups();
1054 trim_trailing_groups();
1055
1056 if (size + source.size > group_allocator_pair.capacity)
1057 {
1058 expand_capacity(size + source.size);
1059 }
1060
1061 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
1062 if PLF_LIST_CONSTEXPR (std::is_trivially_copyable<node_pointer_type>::value && std::is_trivially_destructible<node_pointer_type>::value)
1063 { // &* in order to deal with smart pointer situations ie. obtaining the raw pointer from the smart pointer
1064 std::memcpy(static_cast<void *>(&*block_pointer + size), static_cast<void *>(&*source.block_pointer), sizeof(group) * source.size);
1065 }
1066 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
1067 else if PLF_LIST_CONSTEXPR (std::is_move_constructible<node_pointer_type>::value)
1068 {
1069 std::uninitialized_copy(std::make_move_iterator(source.block_pointer), std::make_move_iterator(source.block_pointer + source.size), block_pointer + size);
1070 }
1071 #endif
1072 else
1073 #endif
1074 {
1075 group_pointer_type current_new_group = block_pointer + size;
1076 const group_pointer_type beyond_end_source = source.block_pointer + source.size;
1077
1078 for (group_pointer_type current_group = source.block_pointer; current_group != beyond_end_source; ++current_group)
1079 {
1080 *(current_new_group++) = *(current_group);
1081
1082 current_group->nodes = NULL;
1083 current_group->beyond_end = NULL;
1084 PLF_LIST_DESTROY(group_allocator_type, source.group_allocator_pair, current_group);
1085 }
1086 }
1087
1088 PLF_LIST_DEALLOCATE(group_allocator_type, source.group_allocator_pair, source.block_pointer, source.group_allocator_pair.capacity);
1089 size += source.size;
1090 last_endpoint_group = block_pointer + size - 1;
1091 element_allocator_pair.capacity += source.element_allocator_pair.capacity;
1092 source.blank();
1093 }
1094 };
1095
1096
1097
1098 // Implement const/non-const iterator switching pattern:
1099 template <bool flag, class IsTrue, class IsFalse> struct choose;
1100
1101 template <class IsTrue, class IsFalse> struct choose<true, IsTrue, IsFalse>
1102 {
1103 typedef IsTrue type;
1104 };
1105
1106 template <class IsTrue, class IsFalse> struct choose<false, IsTrue, IsFalse>
1107 {
1108 typedef IsFalse type;
1109 };
1110
1111
1112public:
1113
1114 template <bool is_const> class list_iterator
1115 {
1116 private:
1117 node_pointer_type node_pointer;
1118
1119 public:
1120 typedef std::bidirectional_iterator_tag iterator_category;
1121 typedef typename list::value_type value_type;
1122 typedef typename list::difference_type difference_type;
1123 typedef typename choose<is_const, typename list::const_pointer, typename list::pointer>::type pointer;
1124 typedef typename choose<is_const, typename list::const_reference, typename list::reference>::type reference;
1125
1126 friend class list;
1127
1128 auto getNodePointer() const {
1129 return node_pointer;
1130 }
1131
1132 inline PLF_LIST_FORCE_INLINE bool operator == (const list_iterator rh) const PLF_LIST_NOEXCEPT
1133 {
1134 return (node_pointer == rh.node_pointer);
1135 }
1136
1137
1138
1139 inline PLF_LIST_FORCE_INLINE bool operator == (const list_iterator<!is_const> rh) const PLF_LIST_NOEXCEPT
1140 {
1141 return (node_pointer == rh.node_pointer);
1142 }
1143
1144
1145
1146 inline PLF_LIST_FORCE_INLINE bool operator != (const list_iterator rh) const PLF_LIST_NOEXCEPT
1147 {
1148 return (node_pointer != rh.node_pointer);
1149 }
1150
1151
1152
1153 inline PLF_LIST_FORCE_INLINE bool operator != (const list_iterator<!is_const> rh) const PLF_LIST_NOEXCEPT
1154 {
1155 return (node_pointer != rh.node_pointer);
1156 }
1157
1158
1159
1160 inline PLF_LIST_FORCE_INLINE reference operator * () const
1161 {
1162 return node_pointer->element;
1163 }
1164
1165
1166
1167 inline PLF_LIST_FORCE_INLINE pointer operator -> () const
1168 {
1169 return &(node_pointer->element);
1170 }
1171
1172
1173
1174 inline PLF_LIST_FORCE_INLINE list_iterator & operator ++ () PLF_LIST_NOEXCEPT
1175 {
1176 assert(node_pointer != NULL); // covers uninitialised list_iterator
1177 node_pointer = node_pointer->next;
1178 return *this;
1179 }
1180
1181
1182
1183 inline list_iterator operator ++(int) PLF_LIST_NOEXCEPT
1184 {
1185 const list_iterator copy(*this);
1186 ++*this;
1187 return copy;
1188 }
1189
1190
1191
1192 inline PLF_LIST_FORCE_INLINE list_iterator & operator -- () PLF_LIST_NOEXCEPT
1193 {
1194 assert(node_pointer != NULL); // covers uninitialised list_iterator
1195 node_pointer = node_pointer->previous;
1196 return *this;
1197 }
1198
1199
1200
1201 inline list_iterator operator -- (int) PLF_LIST_NOEXCEPT
1202 {
1203 const list_iterator copy(*this);
1204 --*this;
1205 return copy;
1206 }
1207
1208
1209
1210 inline list_iterator & operator = (const list_iterator &rh) PLF_LIST_NOEXCEPT
1211 {
1212 node_pointer = rh.node_pointer;
1213 return *this;
1214 }
1215
1216
1217
1218 inline list_iterator & operator = (const list_iterator<!is_const> &rh) PLF_LIST_NOEXCEPT
1219 {
1220 node_pointer = rh.node_pointer;
1221 return *this;
1222 }
1223
1224
1225
1226 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
1227 inline list_iterator & operator = (const list_iterator &&rh) PLF_LIST_NOEXCEPT
1228 {
1229 assert (&rh != this);
1230 node_pointer = std::move(rh.node_pointer);
1231 return *this;
1232 }
1233
1234
1235 inline list_iterator & operator = (const list_iterator<!is_const> &&rh) PLF_LIST_NOEXCEPT
1236 {
1237 node_pointer = std::move(rh.node_pointer);
1238 return *this;
1239 }
1240 #endif
1241
1242
1243
1244 list_iterator() PLF_LIST_NOEXCEPT: node_pointer(NULL) {}
1245
1246 list_iterator(const list_iterator &source) PLF_LIST_NOEXCEPT: node_pointer(source.node_pointer) {}
1247
1248 list_iterator(const list_iterator<!is_const> &source) PLF_LIST_NOEXCEPT: node_pointer(source.node_pointer) {}
1249
1250 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
1251 list_iterator (const list_iterator &&source) PLF_LIST_NOEXCEPT: node_pointer(std::move(source.node_pointer)) {}
1252
1253 list_iterator(const list_iterator<!is_const> &&source) PLF_LIST_NOEXCEPT: node_pointer(std::move(source.node_pointer)) {}
1254 #endif
1255
1256 private:
1257
1258 list_iterator (const node_pointer_type node_p) PLF_LIST_NOEXCEPT: node_pointer(node_p) {}
1259 };
1260
1261
1262
1263
1264 template <bool is_const> class list_reverse_iterator
1265 {
1266 private:
1267 node_pointer_type node_pointer;
1268
1269 public:
1270 typedef std::bidirectional_iterator_tag iterator_category;
1271 typedef typename list::value_type value_type;
1272 typedef typename list::difference_type difference_type;
1273 typedef typename choose<is_const, typename list::const_pointer, typename list::pointer>::type pointer;
1274 typedef typename choose<is_const, typename list::const_reference, typename list::reference>::type reference;
1275
1276 friend class list;
1277
1278
1279 inline PLF_LIST_FORCE_INLINE bool operator == (const list_reverse_iterator rh) const PLF_LIST_NOEXCEPT
1280 {
1281 return (node_pointer == rh.node_pointer);
1282 }
1283
1284
1285
1286 inline PLF_LIST_FORCE_INLINE bool operator == (const list_reverse_iterator<!is_const> rh) const PLF_LIST_NOEXCEPT
1287 {
1288 return (node_pointer == rh.node_pointer);
1289 }
1290
1291
1292
1293 inline PLF_LIST_FORCE_INLINE bool operator != (const list_reverse_iterator rh) const PLF_LIST_NOEXCEPT
1294 {
1295 return (node_pointer != rh.node_pointer);
1296 }
1297
1298
1299
1300 inline PLF_LIST_FORCE_INLINE bool operator != (const list_reverse_iterator<!is_const> rh) const PLF_LIST_NOEXCEPT
1301 {
1302 return (node_pointer != rh.node_pointer);
1303 }
1304
1305
1306
1307 inline PLF_LIST_FORCE_INLINE reference operator * () const
1308 {
1309 return node_pointer->element;
1310 }
1311
1312
1313
1314 inline PLF_LIST_FORCE_INLINE pointer operator -> () const
1315 {
1316 return &(node_pointer->element);
1317 }
1318
1319
1320
1321 inline PLF_LIST_FORCE_INLINE list_reverse_iterator & operator ++ () PLF_LIST_NOEXCEPT
1322 {
1323 assert(node_pointer != NULL); // covers uninitialised list_reverse_iterator
1324 node_pointer = node_pointer->previous;
1325 return *this;
1326 }
1327
1328
1329
1330 inline list_reverse_iterator operator ++(int) PLF_LIST_NOEXCEPT
1331 {
1332 const list_reverse_iterator copy(*this);
1333 ++*this;
1334 return copy;
1335 }
1336
1337
1338
1339 inline PLF_LIST_FORCE_INLINE list_reverse_iterator & operator -- () PLF_LIST_NOEXCEPT
1340 {
1341 assert(node_pointer != NULL);
1342 node_pointer = node_pointer->next;
1343 return *this;
1344 }
1345
1346
1347
1348 inline list_reverse_iterator operator -- (int) PLF_LIST_NOEXCEPT
1349 {
1350 const list_reverse_iterator copy(*this);
1351 --*this;
1352 return copy;
1353 }
1354
1355
1356
1357 inline list_reverse_iterator & operator = (const list_reverse_iterator &rh) PLF_LIST_NOEXCEPT
1358 {
1359 node_pointer = rh.node_pointer;
1360 return *this;
1361 }
1362
1363
1364
1365 inline list_reverse_iterator & operator = (const list_reverse_iterator<!is_const> &rh) PLF_LIST_NOEXCEPT
1366 {
1367 node_pointer = rh.node_pointer;
1368 return *this;
1369 }
1370
1371
1372
1373 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
1374 inline list_reverse_iterator & operator = (const list_reverse_iterator &&rh) PLF_LIST_NOEXCEPT
1375 {
1376 assert (&rh != this);
1377 node_pointer = std::move(rh.node_pointer);
1378 return *this;
1379 }
1380
1381
1382 inline list_reverse_iterator & operator = (const list_reverse_iterator<!is_const> &&rh) PLF_LIST_NOEXCEPT
1383 {
1384 node_pointer = std::move(rh.node_pointer);
1385 return *this;
1386 }
1387 #endif
1388
1389
1390
1391 inline typename list::iterator base() const PLF_LIST_NOEXCEPT
1392 {
1393 return typename list::iterator(node_pointer->next);
1394 }
1395
1396
1397
1398 list_reverse_iterator() PLF_LIST_NOEXCEPT: node_pointer(NULL) {}
1399
1400 list_reverse_iterator(const list_reverse_iterator &source) PLF_LIST_NOEXCEPT: node_pointer(source.node_pointer) {}
1401
1402 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
1403 list_reverse_iterator (const list_reverse_iterator &&source) PLF_LIST_NOEXCEPT:
1404 node_pointer(std::move(source.node_pointer))
1405 {
1406 assert(&source != this);
1407 }
1408
1409 list_reverse_iterator (const list_reverse_iterator<!is_const> &&source) PLF_LIST_NOEXCEPT: node_pointer(std::move(source.node_pointer)) {}
1410 #endif
1411
1412 private:
1413
1414 list_reverse_iterator (const node_pointer_type node_p) PLF_LIST_NOEXCEPT: node_pointer(node_p) {}
1415 };
1416
1417
1418
1419private:
1420
1421 // Used by range-insert and range-constructor to prevent fill-insert and fill-constructor function calls mistakenly resolving to the range insert/constructor
1422 template <bool condition, class T = void>
1423 struct plf_enable_if_c
1424 {
1425 typedef T type;
1426 };
1427
1428 template <class T>
1429 struct plf_enable_if_c<false, T>
1430 {};
1431
1432
1433
1434 group_vector groups; // Structure which contains all groups (structures containing node memory blocks + block metadata)
1435 node_base end_node; // The independent, content-less node which is returned by end()
1436 // When the list is empty, the previous and next pointers of end_node both point to end_node.
1437 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).
1438 // 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.
1439 // last_endpoint == NULL means total_number_of_elements 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
1440 // groups.block_pointer == NULL means an uninitialized container ie. no groups or elements yet
1441 iterator end_iterator, begin_iterator; // Returned by begin() and end().
1442 // 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().
1443 // When the list is empty of elements, begin_iterator == end_iterator so that program loops iterating from begin() to end() will function as expected.
1444
1445 struct ebco_pair1 : node_pointer_allocator_type // Packaging the group allocator with least-used member variables, for empty-base-class optimisation
1446 {
1447 size_type total_number_of_elements;
1448 explicit ebco_pair1(const size_type total_num_elements) PLF_LIST_NOEXCEPT: total_number_of_elements(total_num_elements) {}
1449 } node_pointer_allocator_pair;
1450
1451 struct ebco_pair2 : node_allocator_type
1452 {
1453 size_type number_of_erased_nodes;
1454 explicit ebco_pair2(const size_type num_erased_nodes) PLF_LIST_NOEXCEPT: number_of_erased_nodes(num_erased_nodes) {}
1455 } node_allocator_pair;
1456
1457
1458
1459public:
1460
1461 // Default constructor:
1462
1463 list() PLF_LIST_NOEXCEPT:
1464 element_allocator_type(element_allocator_type()),
1465 end_node(reinterpret_cast<node_pointer_type>(&end_node), reinterpret_cast<node_pointer_type>(&end_node)),
1466 last_endpoint(NULL),
1467 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1468 begin_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1469 node_pointer_allocator_pair(0),
1470 node_allocator_pair(0)
1471 {}
1472
1473
1474
1475 // Allocator-extended constructor:
1476
1477 explicit list(const element_allocator_type &alloc):
1478 element_allocator_type(alloc),
1479 end_node(reinterpret_cast<node_pointer_type>(&end_node), reinterpret_cast<node_pointer_type>(&end_node)),
1480 last_endpoint(NULL),
1481 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1482 begin_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1483 node_pointer_allocator_pair(0),
1484 node_allocator_pair(0)
1485 {}
1486
1487
1488
1489 // Copy constructor:
1490
1491 list(const list &source):
1492 element_allocator_type(source),
1493 end_node(reinterpret_cast<node_pointer_type>(&end_node), reinterpret_cast<node_pointer_type>(&end_node)),
1494 last_endpoint(NULL),
1495 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1496 begin_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1497 node_pointer_allocator_pair(0),
1498 node_allocator_pair(0)
1499 {
1500 reserve(source.node_pointer_allocator_pair.total_number_of_elements);
1501 insert(end_iterator, source.begin_iterator, source.end_iterator);
1502 }
1503
1504
1505
1506 // Allocator-extended copy constructor:
1507
1508 list(const list &source, const allocator_type &alloc):
1509 element_allocator_type(alloc),
1510 end_node(reinterpret_cast<node_pointer_type>(&end_node), reinterpret_cast<node_pointer_type>(&end_node)),
1511 last_endpoint(NULL),
1512 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1513 begin_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1514 node_pointer_allocator_pair(0),
1515 node_allocator_pair(0)
1516 {
1517 reserve(source.node_pointer_allocator_pair.total_number_of_elements);
1518 insert(end_iterator, source.begin_iterator, source.end_iterator);
1519 }
1520
1521
1522
1523 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
1524 // Move constructor:
1525
1526 list(list &&source) PLF_LIST_NOEXCEPT:
1527 element_allocator_type(source),
1528 groups(std::move(source.groups)),
1529 end_node(std::move(source.end_node)),
1530 last_endpoint(std::move(source.last_endpoint)),
1531 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1532 begin_iterator((source.begin_iterator.node_pointer == source.end_iterator.node_pointer) ? reinterpret_cast<node_pointer_type>(&end_node) : std::move(source.begin_iterator)),
1533 node_pointer_allocator_pair(source.node_pointer_allocator_pair.total_number_of_elements),
1534 node_allocator_pair(source.node_allocator_pair.number_of_erased_nodes)
1535 {
1536 assert(&source != this);
1537 end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer;
1538 source.groups.blank();
1539 source.reset();
1540 }
1541
1542
1543
1544 // Allocator-extended move constructor:
1545
1546 list(list &&source, const allocator_type &alloc):
1547 element_allocator_type(alloc),
1548 groups(std::move(source.groups)),
1549 end_node(std::move(source.end_node)),
1550 last_endpoint(std::move(source.last_endpoint)),
1551 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1552 begin_iterator((source.begin_iterator.node_pointer == source.end_iterator.node_pointer) ? reinterpret_cast<node_pointer_type>(&end_node) : std::move(source.begin_iterator)),
1553 node_pointer_allocator_pair(source.node_pointer_allocator_pair.total_number_of_elements),
1554 node_allocator_pair(source.node_allocator_pair.number_of_erased_nodes)
1555 {
1556 assert(&source != this);
1557 end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer;
1558 source.groups.blank();
1559 source.reset();
1560 }
1561 #endif
1562
1563
1564
1565 // Fill constructor:
1566
1567 list(const size_type fill_number, const element_type &element, const element_allocator_type &alloc = element_allocator_type()):
1568 element_allocator_type(alloc),
1569 end_node(reinterpret_cast<node_pointer_type>(&end_node), reinterpret_cast<node_pointer_type>(&end_node)),
1570 last_endpoint(NULL),
1571 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1572 begin_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1573 node_pointer_allocator_pair(0),
1574 node_allocator_pair(0)
1575 {
1576 reserve(fill_number);
1577 insert(end_iterator, fill_number, element);
1578 }
1579
1580
1581
1582 // Range constructor:
1583
1584 template<typename iterator_type>
1585 list(const typename plf_enable_if_c<!std::numeric_limits<iterator_type>::is_integer, iterator_type>::type &first, const iterator_type &last, const element_allocator_type &alloc = element_allocator_type()):
1586 element_allocator_type(alloc),
1587 end_node(reinterpret_cast<node_pointer_type>(&end_node), reinterpret_cast<node_pointer_type>(&end_node)),
1588 last_endpoint(NULL),
1589 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1590 begin_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1591 node_pointer_allocator_pair(0),
1592 node_allocator_pair(0)
1593 {
1594 insert<iterator_type>(end_iterator, first, last);
1595 }
1596
1597
1598
1599 // Initializer-list constructor:
1600
1601 #ifdef PLF_LIST_INITIALIZER_LIST_SUPPORT
1602 list(const std::initializer_list<element_type> &element_list, const element_allocator_type &alloc = element_allocator_type()):
1603 element_allocator_type(alloc),
1604 end_node(reinterpret_cast<node_pointer_type>(&end_node), reinterpret_cast<node_pointer_type>(&end_node)),
1605 last_endpoint(NULL),
1606 end_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1607 begin_iterator(reinterpret_cast<node_pointer_type>(&end_node)),
1608 node_pointer_allocator_pair(0),
1609 node_allocator_pair(0)
1610 {
1611 reserve(element_list.size());
1612 insert(end_iterator, element_list);
1613 }
1614
1615 #endif
1616
1617
1618
1619 ~list() PLF_LIST_NOEXCEPT
1620 {
1621 groups.destroy_all_data(last_endpoint);
1622 }
1623
1624
1625
1626 inline iterator begin() PLF_LIST_NOEXCEPT
1627 {
1628 return begin_iterator;
1629 }
1630
1631
1632
1633 inline const_iterator begin() const PLF_LIST_NOEXCEPT
1634 {
1635 return begin_iterator;
1636 }
1637
1638
1639
1640 inline iterator end() PLF_LIST_NOEXCEPT
1641 {
1642 return end_iterator;
1643 }
1644
1645
1646
1647 inline const_iterator end() const PLF_LIST_NOEXCEPT
1648 {
1649 return end_iterator;
1650 }
1651
1652
1653
1654 inline const_iterator cbegin() const PLF_LIST_NOEXCEPT
1655 {
1656 return const_iterator(begin_iterator.node_pointer);
1657 }
1658
1659
1660
1661 inline const_iterator cend() const PLF_LIST_NOEXCEPT
1662 {
1663 return const_iterator(end_iterator.node_pointer);
1664 }
1665
1666
1667
1668 inline reverse_iterator rbegin() const PLF_LIST_NOEXCEPT
1669 {
1670 return reverse_iterator(end_node.previous);
1671 }
1672
1673
1674
1675 inline reverse_iterator rend() const PLF_LIST_NOEXCEPT
1676 {
1677 return reverse_iterator(end_iterator.node_pointer);
1678 }
1679
1680
1681
1682 inline const_reverse_iterator crbegin() const PLF_LIST_NOEXCEPT
1683 {
1684 return const_reverse_iterator(end_node.previous);
1685 }
1686
1687
1688
1689 inline const_reverse_iterator crend() const PLF_LIST_NOEXCEPT
1690 {
1691 return const_reverse_iterator(end_iterator.node_pointer);
1692 }
1693
1694
1695
1696 inline reference front()
1697 {
1698 assert(begin_iterator.node_pointer != &end_node);
1699 return begin_iterator.node_pointer->element;
1700 }
1701
1702
1703
1704 inline const_reference front() const
1705 {
1706 assert(begin_iterator.node_pointer != &end_node);
1707 return begin_iterator.node_pointer->element;
1708 }
1709
1710
1711
1712 inline reference back()
1713 {
1714 assert(end_node.previous != &end_node);
1715 return end_node.previous->element;
1716 }
1717
1718
1719
1720 inline const_reference back() const
1721 {
1722 assert(end_node.previous != &end_node);
1723 return end_node.previous->element;
1724 }
1725
1726
1727
1728 void clear() PLF_LIST_NOEXCEPT
1729 {
1730 if (last_endpoint == NULL)
1731 {
1732 return;
1733 }
1734
1735 if (node_pointer_allocator_pair.total_number_of_elements != 0)
1736 {
1737 groups.clear(last_endpoint);
1738 }
1739
1740 end_node.next = reinterpret_cast<node_pointer_type>(&end_node);
1741 end_node.previous = reinterpret_cast<node_pointer_type>(&end_node);
1742 last_endpoint = NULL;
1743 begin_iterator.node_pointer = end_iterator.node_pointer;
1744 node_pointer_allocator_pair.total_number_of_elements = 0;
1745 node_allocator_pair.number_of_erased_nodes = 0;
1746 }
1747
1748
1749
1750private:
1751
1752
1753 void reset() PLF_LIST_NOEXCEPT
1754 {
1755 groups.destroy_all_data(last_endpoint);
1756 last_endpoint = NULL;
1757 end_node.next = reinterpret_cast<node_pointer_type>(&end_node);
1758 end_node.previous = reinterpret_cast<node_pointer_type>(&end_node);
1759 begin_iterator.node_pointer = end_iterator.node_pointer;
1760 node_pointer_allocator_pair.total_number_of_elements = 0;
1761 node_allocator_pair.number_of_erased_nodes = 0;
1762 }
1763
1764
1765
1766 inline void add_group_if_necessary()
1767 {
1768 if (last_endpoint == groups.last_endpoint_group->beyond_end) // last_endpoint is beyond the end of a group
1769 {
1770 if (static_cast<size_type>(groups.last_endpoint_group - groups.block_pointer) == groups.size - 1) // ie. there are no reusable groups available at the back of group vector
1771 {
1772 groups.add_new((node_pointer_allocator_pair.total_number_of_elements < PLF_LIST_BLOCK_MAX) ? static_cast<group_size_type>(node_pointer_allocator_pair.total_number_of_elements) : PLF_LIST_BLOCK_MAX);
1773 }
1774 else
1775 {
1776 ++groups.last_endpoint_group;
1777 }
1778
1779 last_endpoint = groups.last_endpoint_group->nodes;
1780 }
1781 }
1782
1783
1784
1785 inline void update_sizes_and_iterators(const const_iterator it)
1786 {
1787 ++(groups.last_endpoint_group->number_of_elements);
1788 ++node_pointer_allocator_pair.total_number_of_elements;
1789
1790 if (it.node_pointer == begin_iterator.node_pointer)
1791 {
1792 begin_iterator.node_pointer = last_endpoint;
1793 }
1794
1795 it.node_pointer->previous->next = last_endpoint;
1796 it.node_pointer->previous = last_endpoint;
1797 }
1798
1799
1800
1801 inline void insert_initialize()
1802 {
1803 if (groups.block_pointer == NULL) // In case of prior reserve/clear call as opposed to being uninitialized
1804 {
1805 groups.initialize(PLF_LIST_BLOCK_MIN);
1806 }
1807
1808 groups.last_endpoint_group->number_of_elements = 1;
1809 end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = groups.last_endpoint_group->nodes;
1810 node_pointer_allocator_pair.total_number_of_elements = 1;
1811 }
1812
1813
1814
1815public:
1816
1817
1818 iterator insert(const const_iterator it, const element_type &element)
1819 {
1820 if (last_endpoint != NULL) // ie. list is not empty
1821 {
1822 if (node_allocator_pair.number_of_erased_nodes == 0) // No erased nodes available for reuse
1823 {
1824 add_group_if_necessary();
1825
1826 #ifdef PLF_LIST_VARIADICS_SUPPORT
1827 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, it.node_pointer, it.node_pointer->previous, element);
1828 #else
1829 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, node(it.node_pointer, it.node_pointer->previous, element));
1830 #endif
1831
1832 update_sizes_and_iterators(it);
1833 return iterator(last_endpoint++);
1834 }
1835 else
1836 {
1837 group_pointer_type const node_group = groups.get_nearest_freelist_group((it.node_pointer != end_iterator.node_pointer) ? it.node_pointer : end_node.previous);
1838 node_pointer_type const selected_node = node_group->free_list_head;
1839 const node_pointer_type previous = node_group->free_list_head->previous;
1840
1841 #ifdef PLF_LIST_VARIADICS_SUPPORT
1842 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, selected_node, it.node_pointer, it.node_pointer->previous, element);
1843 #else
1844 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, selected_node, node(it.node_pointer, it.node_pointer->previous, element));
1845 #endif
1846
1847 node_group->free_list_head = previous;
1848 ++(node_group->number_of_elements);
1849 ++node_pointer_allocator_pair.total_number_of_elements;
1850 --node_allocator_pair.number_of_erased_nodes;
1851
1852 it.node_pointer->previous->next = selected_node;
1853 it.node_pointer->previous = selected_node;
1854
1855 if (it.node_pointer == begin_iterator.node_pointer)
1856 {
1857 begin_iterator.node_pointer = selected_node;
1858 }
1859
1860 return iterator(selected_node);
1861 }
1862 }
1863 else // list is empty
1864 {
1865 insert_initialize();
1866
1867 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
1868 if PLF_LIST_CONSTEXPR (std::is_nothrow_copy_constructible<node>::value) // Avoid try-catch code generation
1869 {
1870 #ifdef PLF_LIST_VARIADICS_SUPPORT
1871 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, element);
1872 #else
1873 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, node(end_iterator.node_pointer, end_iterator.node_pointer, element));
1874 #endif
1875 }
1876 else
1877 #endif
1878 {
1879 try
1880 {
1881 #ifdef PLF_LIST_VARIADICS_SUPPORT
1882 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, element);
1883 #else
1884 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, node(end_iterator.node_pointer, end_iterator.node_pointer, element));
1885 #endif
1886 }
1887 catch (...)
1888 {
1889 reset();
1890 throw;
1891 }
1892 }
1893
1894 return begin_iterator;
1895 }
1896 }
1897
1898
1899
1900 inline PLF_LIST_FORCE_INLINE void push_back(const element_type &element)
1901 {
1902 insert(end_iterator, element);
1903 }
1904
1905
1906
1907 inline PLF_LIST_FORCE_INLINE void push_front(const element_type &element)
1908 {
1909 insert(begin_iterator, element);
1910 }
1911
1912
1913
1914 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
1915 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
1916 {
1917 if (last_endpoint != NULL)
1918 {
1919 if (node_allocator_pair.number_of_erased_nodes == 0)
1920 {
1921 add_group_if_necessary();
1922
1923 #ifdef PLF_LIST_VARIADICS_SUPPORT
1924 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, it.node_pointer, it.node_pointer->previous, std::move(element));
1925 #else
1926 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, node(it.node_pointer, it.node_pointer->previous, std::move(element)));
1927 #endif
1928
1929 update_sizes_and_iterators(it);
1930 return iterator(last_endpoint++);
1931 }
1932 else
1933 {
1934 group_pointer_type const node_group = groups.get_nearest_freelist_group((it.node_pointer != end_iterator.node_pointer) ? it.node_pointer : end_node.previous);
1935 node_pointer_type const selected_node = node_group->free_list_head;
1936 const node_pointer_type previous = node_group->free_list_head->previous;
1937
1938 #ifdef PLF_LIST_VARIADICS_SUPPORT
1939 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, selected_node, it.node_pointer, it.node_pointer->previous, std::move(element));
1940 #else
1941 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, selected_node, node(it.node_pointer, it.node_pointer->previous, std::move(element)));
1942 #endif
1943
1944 node_group->free_list_head = previous;
1945 ++(node_group->number_of_elements);
1946 ++node_pointer_allocator_pair.total_number_of_elements;
1947 --node_allocator_pair.number_of_erased_nodes;
1948
1949 it.node_pointer->previous->next = selected_node;
1950 it.node_pointer->previous = selected_node;
1951
1952 if (it.node_pointer == begin_iterator.node_pointer)
1953 {
1954 begin_iterator.node_pointer = selected_node;
1955 }
1956
1957 return iterator(selected_node);
1958 }
1959 }
1960 else
1961 {
1962 insert_initialize();
1963
1964 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
1965 if PLF_LIST_CONSTEXPR (std::is_nothrow_move_constructible<node>::value)
1966 {
1967 #ifdef PLF_LIST_VARIADICS_SUPPORT
1968 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::move(element));
1969 #else
1970 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, node(end_iterator.node_pointer, end_iterator.node_pointer, std::move(element)));
1971 #endif
1972 }
1973 else
1974 #endif
1975 {
1976 try
1977 {
1978 #ifdef PLF_LIST_VARIADICS_SUPPORT
1979 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::move(element));
1980 #else
1981 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, node(end_iterator.node_pointer, end_iterator.node_pointer, std::move(element)));
1982 #endif
1983 }
1984 catch (...)
1985 {
1986 reset();
1987 throw;
1988 }
1989 }
1990
1991 return begin_iterator;
1992 }
1993 }
1994
1995
1996
1997 inline PLF_LIST_FORCE_INLINE void push_back(element_type &&element)
1998 {
1999 insert(end_iterator, std::move(element));
2000 }
2001
2002
2003
2004 inline PLF_LIST_FORCE_INLINE void push_front(element_type &&element)
2005 {
2006 insert(begin_iterator, std::move(element));
2007 }
2008 #endif
2009
2010
2011
2012
2013 #ifdef PLF_LIST_VARIADICS_SUPPORT
2014 template<typename... arguments>
2015 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
2016 {
2017 if (last_endpoint != NULL)
2018 {
2019 if (node_allocator_pair.number_of_erased_nodes == 0)
2020 {
2021 add_group_if_necessary();
2022
2023 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, it.node_pointer, it.node_pointer->previous, std::forward<arguments>(parameters)...);
2024
2025 update_sizes_and_iterators(it);
2026 return iterator(last_endpoint++);
2027 }
2028 else
2029 {
2030 group_pointer_type const node_group = groups.get_nearest_freelist_group((it.node_pointer != end_iterator.node_pointer) ? it.node_pointer : end_node.previous);
2031 node_pointer_type const selected_node = node_group->free_list_head;
2032 const node_pointer_type previous = node_group->free_list_head->previous;
2033
2034 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, selected_node, it.node_pointer, it.node_pointer->previous, std::forward<arguments>(parameters)...);
2035
2036 node_group->free_list_head = previous;
2037 ++(node_group->number_of_elements);
2038 ++node_pointer_allocator_pair.total_number_of_elements;
2039 --node_allocator_pair.number_of_erased_nodes;
2040
2041 it.node_pointer->previous->next = selected_node;
2042 it.node_pointer->previous = selected_node;
2043
2044 if (it.node_pointer == begin_iterator.node_pointer)
2045 {
2046 begin_iterator.node_pointer = selected_node;
2047 }
2048 return iterator(selected_node);
2049 }
2050 }
2051 else
2052 {
2053 insert_initialize();
2054
2055 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
2056 if PLF_LIST_CONSTEXPR (std::is_nothrow_constructible<element_type, arguments ...>::value)
2057 {
2058 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::forward<arguments>(parameters)...);
2059 }
2060 else
2061 #endif
2062 {
2063 try
2064 {
2065 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint++, end_iterator.node_pointer, end_iterator.node_pointer, std::forward<arguments>(parameters)...);
2066 }
2067 catch (...)
2068 {
2069 reset();
2070 throw;
2071 }
2072 }
2073
2074 return begin_iterator;
2075 }
2076 }
2077
2078
2079
2080 template<typename... arguments>
2081 inline PLF_LIST_FORCE_INLINE reference emplace_back(arguments &&... parameters)
2082 {
2083 return (emplace(end_iterator, std::forward<arguments>(parameters)...)).node_pointer->element;
2084 }
2085
2086
2087
2088 template<typename... arguments>
2089 inline PLF_LIST_FORCE_INLINE reference emplace_front(arguments &&... parameters)
2090 {
2091 return (emplace(begin_iterator, std::forward<arguments>(parameters)...)).node_pointer->element;
2092 }
2093
2094
2095 #endif
2096
2097
2098
2099private:
2100
2101 void group_fill_position(const element_type &element, group_size_type number_of_elements, node_pointer_type const position)
2102 {
2103 position->previous->next = last_endpoint;
2104 groups.last_endpoint_group->number_of_elements += number_of_elements;
2105 node_pointer_type previous = position->previous;
2106
2107 do
2108 {
2109 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
2110 if PLF_LIST_CONSTEXPR (std::is_nothrow_copy_constructible<element_type>::value)
2111 {
2112 #ifdef PLF_LIST_VARIADICS_SUPPORT
2113 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, last_endpoint + 1, previous, element);
2114 #else
2115 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, node(last_endpoint + 1, previous, element));
2116 #endif
2117 }
2118 else
2119 #endif
2120 {
2121 try
2122 {
2123 #ifdef PLF_LIST_VARIADICS_SUPPORT
2124 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, last_endpoint + 1, previous, element);
2125 #else
2126 PLF_LIST_CONSTRUCT(node_allocator_type, node_allocator_pair, last_endpoint, node(last_endpoint + 1, previous, element));
2127 #endif
2128 }
2129 catch (...)
2130 {
2131 previous->next = position;
2132 position->previous = --previous;
2133 groups.last_endpoint_group->number_of_elements -= static_cast<group_size_type>(number_of_elements - (last_endpoint - position));
2134 throw;
2135 }
2136 }
2137
2138 previous = last_endpoint++;
2139 } while (--number_of_elements != 0);
2140
2141 previous->next = position;
2142 position->previous = previous;
2143 }
2144
2145
2146
2147public:
2148
2149 // Fill insert
2150
2151 iterator insert(const_iterator position, const size_type number_of_elements, const element_type &element)
2152 {
2153 if (number_of_elements == 0)
2154 {
2155 return end_iterator;
2156 }
2157 else if (number_of_elements == 1)
2158 {
2159 return insert(position, element);
2160 }
2161
2162
2163 if (node_pointer_allocator_pair.total_number_of_elements == 0 && last_endpoint != NULL && (static_cast<size_type>(groups.block_pointer->beyond_end - groups.block_pointer->nodes) < number_of_elements) && (static_cast<size_type>(groups.block_pointer->beyond_end - groups.block_pointer->nodes) < PLF_LIST_BLOCK_MAX))
2164 {
2165 reset();
2166 }
2167
2168
2169 if (groups.block_pointer == NULL) // ie. Uninitialized list
2170 {
2171 if (number_of_elements > PLF_LIST_BLOCK_MAX)
2172 {
2173 size_type multiples = number_of_elements / PLF_LIST_BLOCK_MAX;
2174 const group_size_type remainder = static_cast<group_size_type>(number_of_elements - (multiples++ * PLF_LIST_BLOCK_MAX)); // ++ to aid while loop below
2175
2176 // Create and fill first group:
2177 if (remainder != 0) // make sure smallest block is first
2178 {
2179 if (remainder >= PLF_LIST_BLOCK_MIN)
2180 {
2181 groups.initialize(remainder);
2182 end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = groups.last_endpoint_group->nodes;
2183 group_fill_position(element, remainder, end_iterator.node_pointer);
2184 }
2185 else
2186 { // Create first group as BLOCK_MIN size then subtract difference between BLOCK_MIN and remainder from next group:
2187 groups.initialize(PLF_LIST_BLOCK_MIN);
2188 end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = groups.last_endpoint_group->nodes;
2189 group_fill_position(element, PLF_LIST_BLOCK_MIN, end_iterator.node_pointer);
2190
2191 groups.add_new(PLF_LIST_BLOCK_MAX - (PLF_LIST_BLOCK_MIN - remainder));
2192 end_node.previous = last_endpoint = groups.last_endpoint_group->nodes;
2193 group_fill_position(element, PLF_LIST_BLOCK_MAX - (PLF_LIST_BLOCK_MIN - remainder), end_iterator.node_pointer);
2194 --multiples;
2195 }
2196 }
2197 else
2198 {
2199 groups.initialize(PLF_LIST_BLOCK_MAX);
2200 end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = groups.last_endpoint_group->nodes;
2201 group_fill_position(element, PLF_LIST_BLOCK_MAX, end_iterator.node_pointer);
2202 --multiples;
2203 }
2204
2205 while (--multiples != 0)
2206 {
2207 groups.add_new(PLF_LIST_BLOCK_MAX);
2208 end_node.previous = last_endpoint = groups.last_endpoint_group->nodes;
2209 group_fill_position(element, PLF_LIST_BLOCK_MAX, end_iterator.node_pointer);
2210 }
2211
2212 }
2213 else
2214 {
2215 groups.initialize((number_of_elements < PLF_LIST_BLOCK_MIN) ? PLF_LIST_BLOCK_MIN : static_cast<group_size_type>(number_of_elements)); // Construct first group
2216 end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = groups.last_endpoint_group->nodes;
2217 group_fill_position(element, static_cast<group_size_type>(number_of_elements), end_iterator.node_pointer);
2218 }
2219
2220 node_pointer_allocator_pair.total_number_of_elements = number_of_elements;
2221 return begin_iterator;
2222 }
2223 else
2224 {
2225 // Insert first element, then use up any erased nodes:
2226 size_type remainder = number_of_elements - 1;
2227 const iterator return_iterator = insert(position, element);
2228
2229 while (node_allocator_pair.number_of_erased_nodes != 0)
2230 {
2231 insert(position, element);
2232 --node_allocator_pair.number_of_erased_nodes;
2233
2234 if (--remainder == 0)
2235 {
2236 return return_iterator;
2237 }
2238 }
2239
2240 node_pointer_allocator_pair.total_number_of_elements += remainder;
2241
2242 // then use up remainder of last_endpoint_group:
2243 const group_size_type remaining_nodes_in_group = static_cast<group_size_type>(groups.last_endpoint_group->beyond_end - last_endpoint);
2244
2245 if (remaining_nodes_in_group != 0)
2246 {
2247 if (remaining_nodes_in_group < remainder)
2248 {
2249 group_fill_position(element, remaining_nodes_in_group, position.node_pointer);
2250 remainder -= remaining_nodes_in_group;
2251 }
2252 else
2253 {
2254 group_fill_position(element, static_cast<group_size_type>(remainder), position.node_pointer);
2255 return return_iterator;
2256 }
2257 }
2258
2259
2260 // use up trailing groups:
2261 while ((groups.last_endpoint_group != (groups.block_pointer + groups.size - 1)) & (remainder != 0)) // Logical or seems to be faster here
2262 {
2263 last_endpoint = (++groups.last_endpoint_group)->nodes;
2264 const group_size_type group_size = static_cast<group_size_type>(groups.last_endpoint_group->beyond_end - groups.last_endpoint_group->nodes);
2265
2266 if (group_size < remainder)
2267 {
2268 group_fill_position(element, group_size, position.node_pointer);
2269 remainder -= group_size;
2270 }
2271 else
2272 {
2273 group_fill_position(element, static_cast<group_size_type>(remainder), position.node_pointer);
2274 return return_iterator;
2275 }
2276 }
2277
2278 size_type multiples = remainder / static_cast<size_type>(PLF_LIST_BLOCK_MAX);
2279 remainder -= multiples * PLF_LIST_BLOCK_MAX;
2280
2281 while (multiples-- != 0)
2282 {
2283 groups.add_new(PLF_LIST_BLOCK_MAX);
2284 last_endpoint = groups.last_endpoint_group->nodes;
2285 group_fill_position(element, PLF_LIST_BLOCK_MAX, position.node_pointer);
2286 }
2287
2288 if (remainder != 0) // Bit annoying to create a large block to house a lower number of elements, but beats the alternatives
2289 {
2290 groups.add_new(PLF_LIST_BLOCK_MAX);
2291 last_endpoint = groups.last_endpoint_group->nodes;
2292 group_fill_position(element, static_cast<group_size_type>(remainder), position.node_pointer);
2293 }
2294
2295 return return_iterator;
2296 }
2297 }
2298
2299
2300
2301 // Range insert
2302
2303 template <class iterator_type>
2304 #if defined(PLF_LIST_TYPE_TRAITS_SUPPORT)
2305 iterator insert(const const_iterator position, typename plf_enable_if_c<(!std::numeric_limits<iterator_type>::is_integer) && (!std::is_same<typename std::iterator_traits<iterator_type>::iterator_category, std::random_access_iterator_tag>::value), iterator_type>::type first, const iterator_type last)
2306 #else
2307 iterator insert(const const_iterator position, typename plf_enable_if_c<!std::numeric_limits<iterator_type>::is_integer, iterator_type>::type first, const iterator_type last)
2308 #endif
2309 {
2310 if (first == last)
2311 {
2312 return end_iterator;
2313 }
2314
2315 const iterator return_iterator = insert(position, *first);
2316
2317 while(++first != last)
2318 {
2319 insert(position, *first);
2320 }
2321
2322 return return_iterator;
2323 }
2324
2325
2326
2327 #if defined(PLF_LIST_TYPE_TRAITS_SUPPORT)
2328 template <class iterator_type>
2329 iterator insert(const const_iterator position, typename plf_enable_if_c<(!std::numeric_limits<iterator_type>::is_integer) && std::is_same<typename std::iterator_traits<iterator_type>::iterator_category, std::random_access_iterator_tag>::value, iterator_type>::type first, const iterator_type last)
2330 {
2331 reserve(node_pointer_allocator_pair.total_number_of_elements + static_cast<size_type>(last - first));
2332
2333 if (first == last)
2334 {
2335 return end_iterator;
2336 }
2337
2338 const iterator return_iterator = insert(position, *first);
2339
2340 while(++first != last)
2341 {
2342 insert(position, *first);
2343 }
2344
2345 return return_iterator;
2346 }
2347 #endif
2348
2349
2350
2351 // Initializer-list insert
2352
2353 #ifdef PLF_LIST_INITIALIZER_LIST_SUPPORT
2354 inline iterator insert(const const_iterator it, const std::initializer_list<element_type> &element_list)
2355 { // use range insert:
2356 return insert(it, element_list.begin(), element_list.end());
2357 }
2358 #endif
2359
2360
2361
2362private:
2363
2364 inline PLF_LIST_FORCE_INLINE void destroy_all_node_pointers(group_pointer_type const group_to_process, const node_pointer_type beyond_end_node) PLF_LIST_NOEXCEPT
2365 {
2366 for (node_pointer_type current_node = group_to_process->nodes; current_node != beyond_end_node; ++current_node)
2367 {
2368 PLF_LIST_DESTROY(node_pointer_allocator_type, node_pointer_allocator_pair, &(current_node->next));
2369 PLF_LIST_DESTROY(node_pointer_allocator_type, node_pointer_allocator_pair, &(current_node->previous));
2370 }
2371 }
2372
2373
2374
2375public:
2376
2377
2378 // Single erase:
2379
2380 iterator erase(const const_iterator it) // if uninitialized/invalid iterator supplied, function could generate an exception, hence no noexcept
2381 {
2382 assert(node_pointer_allocator_pair.total_number_of_elements != 0);
2383 assert(it.node_pointer != NULL);
2384 assert(it.node_pointer != end_iterator.node_pointer);
2385
2386 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
2387 if PLF_LIST_CONSTEXPR (!(std::is_trivially_destructible<element_type>::value))
2388 #endif
2389 {
2390 PLF_LIST_DESTROY(element_allocator_type, (*this), &(it.node_pointer->element)); // Destruct element
2391 }
2392
2393 --node_pointer_allocator_pair.total_number_of_elements;
2394 ++node_allocator_pair.number_of_erased_nodes;
2395
2396
2397 // 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):
2398 group_pointer_type node_group = groups.last_searched_group;
2399
2400 if ((it.node_pointer < node_group->nodes) || (it.node_pointer >= node_group->beyond_end))
2401 {
2402 // Search groups to the left and right of the last searched group, in the group vector:
2403 const group_pointer_type beyond_end_group = groups.last_endpoint_group + 1;
2404 group_pointer_type left = node_group - 1;
2405 bool right_not_beyond_back = (++node_group < beyond_end_group);
2406 bool left_not_beyond_front = (left >= groups.block_pointer);
2407
2408 while (true)
2409 {
2410 if (right_not_beyond_back)
2411 {
2412 if ((it.node_pointer < node_group->beyond_end) && (it.node_pointer >= node_group->nodes)) // element location found
2413 {
2414 break;
2415 }
2416
2417 right_not_beyond_back = (++node_group < beyond_end_group);
2418 }
2419
2420 if (left_not_beyond_front)
2421 {
2422 if ((it.node_pointer >= left->nodes) && (it.node_pointer < left->beyond_end)) // element location found
2423 {
2424 node_group = left;
2425 break;
2426 }
2427
2428 left_not_beyond_front = (--left >= groups.block_pointer);
2429 }
2430 }
2431
2432 groups.last_searched_group = node_group;
2433 }
2434
2435 // To avoid pointer aliasing and increase performance:
2436 const node_pointer_type previous = it.node_pointer->previous;
2437 const node_pointer_type next = it.node_pointer->next;
2438 next->previous = previous;
2439 previous->next = next;
2440
2441 if (it.node_pointer == begin_iterator.node_pointer)
2442 {
2443 begin_iterator.node_pointer = next;
2444 }
2445
2446
2447 const iterator return_iterator(next);
2448
2449 if (--(node_group->number_of_elements) != 0) // ie. group is not empty yet, add node to free list
2450 {
2451 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
2452 it.node_pointer->previous = node_group->free_list_head;
2453 node_group->free_list_head = it.node_pointer;
2454 return return_iterator;
2455 }
2456 else if (node_group != groups.last_endpoint_group--) // remove group (and decrement active back group)
2457 {
2458 const group_size_type group_size = static_cast<group_size_type>(node_group->beyond_end - node_group->nodes);
2459 node_allocator_pair.number_of_erased_nodes -= group_size;
2460
2461 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
2462 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
2463 #endif
2464 {
2465 destroy_all_node_pointers(node_group, node_group->beyond_end);
2466 }
2467
2468 node_group->free_list_head = NULL;
2469
2470 if ((group_size == PLF_LIST_BLOCK_MAX) | (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
2471 {
2472 groups.move_to_back(node_group);
2473 }
2474 else
2475 {
2476 groups.remove(node_group);
2477 }
2478
2479 return return_iterator;
2480 }
2481 else // clear back group, leave trailing
2482 {
2483 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
2484 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
2485 #endif
2486 {
2487 destroy_all_node_pointers(node_group, last_endpoint);
2488 }
2489
2490 node_group->free_list_head = NULL;
2491
2492 if (node_pointer_allocator_pair.total_number_of_elements != 0)
2493 {
2494 node_allocator_pair.number_of_erased_nodes -= static_cast<group_size_type>(last_endpoint - node_group->nodes);
2495 last_endpoint = groups.last_endpoint_group->beyond_end;
2496 }
2497 else
2498 {
2499 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.
2500 clear();
2501 }
2502
2503 return return_iterator;
2504 }
2505 }
2506
2507
2508
2509 // Range-erase:
2510
2511 inline iterator erase(const_iterator iterator1, const const_iterator iterator2) // if uninitialized/invalid iterator supplied, function could generate an exception
2512 {
2513 while (iterator1 != iterator2)
2514 {
2515 iterator1 = erase(iterator1);
2516 }
2517
2518 return iterator2;
2519 }
2520
2521
2522
2523 inline void pop_back() // Exception will occur on empty list
2524 {
2525 erase(iterator(end_node.previous));
2526 }
2527
2528
2529
2530 inline void pop_front() // Exception will occur on empty list
2531 {
2532 erase(begin_iterator);
2533 }
2534
2535
2536
2537 inline list & operator = (const list &source)
2538 {
2539 assert (&source != this);
2540
2541 clear();
2542 reserve(source.node_pointer_allocator_pair.total_number_of_elements);
2543 insert(end_iterator, source.begin_iterator, source.end_iterator);
2544
2545 return *this;
2546 }
2547
2548
2549
2550 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
2551 // Move assignment
2552 list & operator = (list &&source) PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT(allocator_type)
2553 {
2554 assert (&source != this);
2555
2556 // Move source values across:
2557 groups.destroy_all_data(last_endpoint);
2558
2559 groups = std::move(source.groups);
2560 end_node = std::move(source.end_node);
2561 last_endpoint = std::move(source.last_endpoint);
2562 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);
2563 node_pointer_allocator_pair.total_number_of_elements = source.node_pointer_allocator_pair.total_number_of_elements;
2564 node_allocator_pair.number_of_erased_nodes = source.node_allocator_pair.number_of_erased_nodes;
2565
2566 end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer;
2567
2568 source.groups.blank();
2569 source.reset();
2570 return *this;
2571 }
2572 #endif
2573
2574
2575
2576 #ifdef PLF_LIST_INITIALIZER_LIST_SUPPORT
2577 inline list & operator = (const std::initializer_list<element_type> &element_list)
2578 {
2579 clear();
2580 insert(begin_iterator, element_list);
2581 return *this;
2582 }
2583 #endif
2584
2585
2586
2587 bool operator == (const list &rh) const PLF_LIST_NOEXCEPT
2588 {
2589 assert (this != &rh);
2590
2591 if (node_pointer_allocator_pair.total_number_of_elements != rh.node_pointer_allocator_pair.total_number_of_elements)
2592 {
2593 return false;
2594 }
2595
2596 for (const_iterator lh_iterator = begin_iterator, rh_iterator = rh.begin_iterator; lh_iterator != end_iterator; ++lh_iterator, ++rh_iterator)
2597 {
2598 if (*lh_iterator != *rh_iterator)
2599 {
2600 return false;
2601 }
2602 }
2603
2604 return true;
2605 }
2606
2607
2608
2609 inline bool operator != (const list &rh) const PLF_LIST_NOEXCEPT
2610 {
2611 return !(*this == rh);
2612 }
2613
2614
2615
2616 #ifdef PLF_LIST_CPP20_SUPPORT
2617 [[nodiscard]]
2618 #endif
2619 inline bool empty() const PLF_LIST_NOEXCEPT
2620 {
2621 return node_pointer_allocator_pair.total_number_of_elements == 0;
2622 }
2623
2624
2625
2626 inline size_type size() const PLF_LIST_NOEXCEPT
2627 {
2628 return node_pointer_allocator_pair.total_number_of_elements;
2629 }
2630
2631
2632
2633 inline size_type max_size() const PLF_LIST_NOEXCEPT
2634 {
2635 #ifdef PLF_LIST_ALLOCATOR_TRAITS_SUPPORT
2636 return std::allocator_traits<element_allocator_type>::max_size(*this);
2637 #else
2638 return element_allocator_type::max_size();
2639 #endif
2640 }
2641
2642
2643
2644 inline size_type capacity() const PLF_LIST_NOEXCEPT
2645 {
2646 return groups.element_allocator_pair.capacity;
2647 }
2648
2649
2650
2651 inline size_type memory() const PLF_LIST_NOEXCEPT
2652 {
2653 return static_cast<size_type>(sizeof(*this) + (groups.element_allocator_pair.capacity * sizeof(node)) + (sizeof(group) * groups.group_allocator_pair.capacity));
2654 }
2655
2656
2657
2658private:
2659
2660
2661 struct less
2662 {
2663 inline bool operator() (const element_type &a, const element_type &b) const PLF_LIST_NOEXCEPT
2664 {
2665 return a < b;
2666 }
2667 };
2668
2669
2670
2671 // Function-object to redirect the sort function to sort pointers by the elements they point to, not the pointer value
2672 template <class comparison_function>
2673 struct sort_dereferencer
2674 {
2675 comparison_function stored_instance;
2676
2677 explicit sort_dereferencer(const comparison_function &function_instance):
2678 stored_instance(function_instance)
2679 {}
2680
2681 sort_dereferencer() PLF_LIST_NOEXCEPT
2682 {}
2683
2684 inline bool operator() (const node_pointer_type first, const node_pointer_type second)
2685 {
2686 return stored_instance(first->element, second->element);
2687 }
2688 };
2689
2690
2691
2692public:
2693
2694
2695 template <class comparison_function>
2696 void sort(comparison_function compare)
2697 {
2698 if (node_pointer_allocator_pair.total_number_of_elements < 2)
2699 {
2700 return;
2701 }
2702
2703 node_pointer_type * const node_pointers = PLF_LIST_ALLOCATE(node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer_allocator_pair.total_number_of_elements, NULL);
2704 node_pointer_type *node_pointer = node_pointers;
2705
2706
2707 // 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:
2708 for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group)
2709 {
2710 const node_pointer_type end = current_group->beyond_end;
2711
2712 if ((end - current_group->nodes) != current_group->number_of_elements) // If there are erased nodes present in the group
2713 {
2714 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
2715 {
2716 if (current_node->next != NULL) // is not free list node
2717 {
2718 PLF_LIST_CONSTRUCT(node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer++, current_node);
2719 }
2720 }
2721 }
2722 else // If no erased nodes present we can avoid the per-node testing
2723 {
2724 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
2725 {
2726 PLF_LIST_CONSTRUCT(node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer++, current_node);
2727 }
2728 }
2729 }
2730
2731 if ((last_endpoint - groups.last_endpoint_group->nodes) != groups.last_endpoint_group->number_of_elements) // If there are erased nodes present in the group
2732 {
2733 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
2734 {
2735 if (current_node->next != NULL)
2736 {
2737 PLF_LIST_CONSTRUCT(node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer++, current_node);
2738 }
2739 }
2740 }
2741 else
2742 {
2743 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
2744 {
2745 PLF_LIST_CONSTRUCT(node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer++, current_node);
2746 }
2747 }
2748
2749
2750 #ifdef GFX_TIMSORT_HPP
2751 gfx::timsort(node_pointers, node_pointers + node_pointer_allocator_pair.total_number_of_elements, sort_dereferencer<comparison_function>(compare));
2752 #else
2753 std::sort(node_pointers, node_pointers + node_pointer_allocator_pair.total_number_of_elements, sort_dereferencer<comparison_function>(compare));
2754 #endif
2755
2756 begin_iterator.node_pointer = node_pointers[0];
2757 begin_iterator.node_pointer->next = node_pointers[1];
2758 begin_iterator.node_pointer->previous = end_iterator.node_pointer;
2759
2760 end_node.next = node_pointers[0];
2761 end_node.previous = node_pointers[node_pointer_allocator_pair.total_number_of_elements - 1];
2762 end_node.previous->next = end_iterator.node_pointer;
2763 end_node.previous->previous = node_pointers[node_pointer_allocator_pair.total_number_of_elements - 2];
2764
2765 node_pointer_type * const back = node_pointers + node_pointer_allocator_pair.total_number_of_elements - 1;
2766
2767 for(node_pointer = node_pointers + 1; node_pointer != back; ++node_pointer)
2768 {
2769 (*node_pointer)->next = *(node_pointer + 1);
2770 (*node_pointer)->previous = *(node_pointer - 1);
2771
2772 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
2773 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
2774 #endif
2775 {
2776 PLF_LIST_DESTROY(node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer - 1);
2777 }
2778 }
2779
2780 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
2781 if PLF_LIST_CONSTEXPR (!std::is_trivially_destructible<node_pointer_type>::value)
2782 #endif
2783 {
2784 PLF_LIST_DESTROY(node_pointer_allocator_type, node_pointer_allocator_pair, back);
2785 }
2786
2787 PLF_LIST_DEALLOCATE(node_pointer_allocator_type, node_pointer_allocator_pair, node_pointers, node_pointer_allocator_pair.total_number_of_elements);
2788 }
2789
2790
2791
2792 inline void sort()
2793 {
2794 sort(less());
2795 }
2796
2797
2798
2799 void reorder(const iterator position, const iterator first, const iterator last) PLF_LIST_NOEXCEPT
2800 {
2801 // To avoid pointer aliasing and subsequently increase performance via simultaneous assignments:
2802 const node_pointer_type first_previous = first.node_pointer->previous;
2803 const node_pointer_type last_next = last.node_pointer->next;
2804 const node_pointer_type position_previous = position.node_pointer->previous;
2805
2806 last_next->previous = first_previous;
2807 first.node_pointer->previous->next = last_next;
2808
2809 last.node_pointer->next = position.node_pointer;
2810 first.node_pointer->previous = position_previous;
2811
2812 position_previous->next = first.node_pointer;
2813 position.node_pointer->previous = last.node_pointer;
2814
2815 if (begin_iterator == position)
2816 {
2817 begin_iterator = first;
2818 }
2819 }
2820
2821
2822
2823 inline void reorder(const iterator position, const iterator location) PLF_LIST_NOEXCEPT
2824 {
2825 reorder(position, location, location);
2826 }
2827
2828
2829
2830 void reserve(size_type reserve_amount)
2831 {
2832 if (reserve_amount == 0 || reserve_amount <= groups.element_allocator_pair.capacity)
2833 {
2834 return;
2835 }
2836 else if (reserve_amount < PLF_LIST_BLOCK_MIN)
2837 {
2838 reserve_amount = PLF_LIST_BLOCK_MIN;
2839 }
2840 else if (reserve_amount > max_size())
2841 {
2842 reserve_amount = max_size();
2843 }
2844
2845
2846 if (groups.block_pointer != NULL && node_pointer_allocator_pair.total_number_of_elements == 0)
2847 { // edge case: has been filled with elements then clear()'d - some groups may be smaller than would be desired, should be replaced
2848 group_size_type end_group_size = static_cast<group_size_type>((groups.block_pointer + groups.size - 1)->beyond_end - (groups.block_pointer + groups.size - 1)->nodes);
2849
2850 if (reserve_amount > end_group_size && end_group_size != PLF_LIST_BLOCK_MAX) // if last group isn't large enough, remove all groups
2851 {
2852 reset();
2853 }
2854 else
2855 {
2856 size_type number_of_full_groups_needed = reserve_amount / PLF_LIST_BLOCK_MAX;
2857 group_size_type remainder = static_cast<group_size_type>(reserve_amount - (number_of_full_groups_needed * PLF_LIST_BLOCK_MAX));
2858
2859 // Remove any max_size groups which're not needed and any groups that're smaller than remainder:
2860 for (group_pointer_type current_group = groups.block_pointer; current_group < groups.block_pointer + groups.size;)
2861 {
2862 const group_size_type current_group_size = static_cast<group_size_type>(groups.block_pointer->beyond_end - groups.block_pointer->nodes);
2863
2864 if (number_of_full_groups_needed != 0 && current_group_size == PLF_LIST_BLOCK_MAX)
2865 {
2866 --number_of_full_groups_needed;
2867 ++current_group;
2868 }
2869 else if (remainder != 0 && current_group_size >= remainder)
2870 {
2871 remainder = 0;
2872 ++current_group;
2873 }
2874 else
2875 {
2876 groups.remove(current_group);
2877 }
2878 }
2879
2880 last_endpoint = groups.block_pointer->nodes;
2881 }
2882 }
2883
2884 reserve_amount -= groups.element_allocator_pair.capacity;
2885
2886 // To correct from possible reallocation caused by add_new:
2887 const difference_type last_endpoint_group_number = groups.last_endpoint_group - groups.block_pointer;
2888
2889 size_type number_of_full_groups = (reserve_amount / PLF_LIST_BLOCK_MAX);
2890 reserve_amount -= (number_of_full_groups++ * PLF_LIST_BLOCK_MAX); // ++ to aid while loop below
2891
2892 if (groups.block_pointer == NULL) // Previously uninitialized list or reset in above if statement; most common scenario
2893 {
2894 if (reserve_amount != 0)
2895 {
2896 groups.initialize(static_cast<group_size_type>(((reserve_amount < PLF_LIST_BLOCK_MIN) ? PLF_LIST_BLOCK_MIN : reserve_amount)));
2897 }
2898 else
2899 {
2900 groups.initialize(PLF_LIST_BLOCK_MAX);
2901 --number_of_full_groups;
2902 }
2903 }
2904 else if (reserve_amount != 0)
2905 { // 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:
2906 const group_size_type last_endpoint_group_capacity = static_cast<group_size_type>(groups.last_endpoint_group->beyond_end - groups.last_endpoint_group->nodes);
2907 groups.add_new(static_cast<group_size_type>((reserve_amount < last_endpoint_group_capacity) ? last_endpoint_group_capacity : reserve_amount));
2908 }
2909
2910 while (--number_of_full_groups != 0)
2911 {
2912 groups.add_new(PLF_LIST_BLOCK_MAX);
2913 }
2914
2915 groups.last_endpoint_group = groups.block_pointer + last_endpoint_group_number;
2916 }
2917
2918
2919
2920 inline PLF_LIST_FORCE_INLINE void free_unused_memory() PLF_LIST_NOEXCEPT
2921 {
2922 groups.trim_trailing_groups();
2923 }
2924
2925
2926
2927 void shrink_to_fit()
2928 {
2929 if ((groups.block_pointer == NULL) | (node_pointer_allocator_pair.total_number_of_elements == groups.element_allocator_pair.capacity)) // if list is uninitialized or full
2930 {
2931 return;
2932 }
2933 else if (node_pointer_allocator_pair.total_number_of_elements == 0) // Edge case
2934 {
2935 reset();
2936 return;
2937 }
2938 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
2939 {
2940 groups.trim_trailing_groups();
2941 return;
2942 }
2943
2944 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
2945 list temp;
2946 temp.reserve(node_pointer_allocator_pair.total_number_of_elements);
2947
2948 #ifdef PLF_LIST_TYPE_TRAITS_SUPPORT
2949 if PLF_LIST_CONSTEXPR (std::is_move_assignable<element_type>::value && std::is_move_constructible<element_type>::value) // move elements if possible, otherwise copy them
2950 {
2951 temp.insert(temp.end_iterator, std::make_move_iterator(begin_iterator), std::make_move_iterator(end_iterator));
2952 }
2953 else
2954 #endif
2955 {
2956 temp.insert(temp.end_iterator, begin_iterator, end_iterator);
2957 }
2958
2959 *this = std::move(temp);
2960 #else
2961 list temp(*this);
2962 reset();
2963 swap(temp);
2964 #endif
2965 }
2966
2967
2968
2969private:
2970
2971 void append_process(list &source) // used by merge and splice
2972 {
2973 if (last_endpoint != groups.last_endpoint_group->beyond_end)
2974 { // Add unused nodes to group's free list
2975 const node_pointer_type back_node = last_endpoint - 1;
2976 for (node_pointer_type current_node = groups.last_endpoint_group->beyond_end - 1; current_node != back_node; --current_node)
2977 {
2978 current_node->next = NULL;
2979 current_node->previous = groups.last_endpoint_group->free_list_head;
2980 groups.last_endpoint_group->free_list_head = current_node;
2981 }
2982
2983 node_allocator_pair.number_of_erased_nodes += static_cast<size_type>(groups.last_endpoint_group->beyond_end - last_endpoint);
2984 }
2985
2986 groups.append(source.groups);
2987 last_endpoint = source.last_endpoint;
2988 node_pointer_allocator_pair.total_number_of_elements += source.node_pointer_allocator_pair.total_number_of_elements;
2989 source.reset();
2990 }
2991
2992
2993
2994
2995public:
2996
2997 void splice(iterator position, list &source)
2998 {
2999 assert(&source != this);
3000
3001 if (source.node_pointer_allocator_pair.total_number_of_elements == 0)
3002 {
3003 return;
3004 }
3005 else if (node_pointer_allocator_pair.total_number_of_elements == 0)
3006 {
3007 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
3008 *this = std::move(source);
3009 #else
3010 reset();
3011 swap(source);
3012 #endif
3013
3014 return;
3015 }
3016
3017 if (position.node_pointer == begin_iterator.node_pointer) // put source groups at front rather than back
3018 {
3019 swap(source);
3020 position.node_pointer = end_iterator.node_pointer;
3021 }
3022
3023 position.node_pointer->previous->next = source.begin_iterator.node_pointer;
3024 source.begin_iterator.node_pointer->previous = position.node_pointer->previous;
3025 position.node_pointer->previous = source.end_node.previous;
3026 source.end_node.previous->next = position.node_pointer;
3027
3028 append_process(source);
3029 }
3030
3031
3032
3033 template <class comparison_function>
3034 void merge(list &source, comparison_function compare)
3035 {
3036 assert(&source != this);
3037 splice((source.node_pointer_allocator_pair.total_number_of_elements >= node_pointer_allocator_pair.total_number_of_elements) ? end_iterator : begin_iterator, source);
3038 sort(compare);
3039 }
3040
3041
3042
3043 void merge(list &source)
3044 {
3045 assert(&source != this);
3046
3047 if (source.node_pointer_allocator_pair.total_number_of_elements == 0)
3048 {
3049 return;
3050 }
3051 else if (node_pointer_allocator_pair.total_number_of_elements == 0)
3052 {
3053 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
3054 *this = std::move(source);
3055 #else
3056 reset();
3057 swap(source);
3058 #endif
3059
3060 return;
3061 }
3062
3063 node_pointer_type current1 = begin_iterator.node_pointer->next, current2 = source.begin_iterator.node_pointer->next;
3064 node_pointer_type previous = source.begin_iterator.node_pointer;
3065 const node_pointer_type source_end = source.end_iterator.node_pointer, this_end = end_iterator.node_pointer;
3066
3067 begin_iterator.node_pointer->next = source.begin_iterator.node_pointer;
3068 source.begin_iterator.node_pointer->previous = begin_iterator.node_pointer;
3069
3070
3071 while ((current1 != this_end) & (current2 != source_end))
3072 {
3073 previous->next = current1;
3074 current1->previous = previous;
3075 previous = current1;
3076 current1 = current1->next;
3077
3078 previous->next = current2;
3079 current2->previous = previous;
3080 previous = current2;
3081 current2 = current2->next;
3082 }
3083
3084 if (current1 != this_end)
3085 {
3086 previous->next = current1;
3087 current1->previous = previous;
3088 }
3089 else
3090 {
3091 end_node.previous = source.end_node.previous;
3092 source.end_node.previous->next = end_iterator.node_pointer;
3093 }
3094
3095 append_process(source);
3096 }
3097
3098
3099
3100 void reverse() PLF_LIST_NOEXCEPT
3101 {
3102 // Note: Because current_node->next has to be read during swapping in this process anyway, 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.
3103 if (node_pointer_allocator_pair.total_number_of_elements > 1)
3104 {
3105 for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group)
3106 {
3107 const node_pointer_type end = current_group->beyond_end;
3108
3109 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3110 {
3111 if (current_node->next != NULL) // ie. is not free list node
3112 { // swap the pointers:
3113 const node_pointer_type temp = current_node->next;
3114 current_node->next = current_node->previous;
3115 current_node->previous = temp;
3116 }
3117 }
3118 }
3119
3120 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3121 {
3122 if (current_node->next != NULL)
3123 {
3124 const node_pointer_type temp = current_node->next;
3125 current_node->next = current_node->previous;
3126 current_node->previous = temp;
3127 }
3128 }
3129
3130 const node_pointer_type temp = end_node.previous;
3131 end_node.previous = begin_iterator.node_pointer;
3132 begin_iterator.node_pointer = temp;
3133
3134 end_node.previous->next = end_iterator.node_pointer;
3135 begin_iterator.node_pointer->previous = end_iterator.node_pointer;
3136 }
3137 }
3138
3139
3140
3141private:
3142
3143 // Used by unique()
3144 struct eq
3145 {
3146 inline bool operator() (const element_type &a, const element_type &b) const PLF_LIST_NOEXCEPT
3147 {
3148 return a == b;
3149 }
3150 };
3151
3152
3153
3154 // Used by remove()
3155 struct eq_to
3156 {
3157 const element_type value;
3158
3159 explicit eq_to(const element_type store_value):
3160 value(store_value)
3161 {}
3162
3163 eq_to() PLF_LIST_NOEXCEPT
3164 {}
3165
3166 inline bool operator() (const element_type compare_value) const PLF_LIST_NOEXCEPT
3167 {
3168 return value == compare_value;
3169 }
3170 };
3171
3172
3173
3174public:
3175
3176 template <class comparison_function>
3177 size_type unique(comparison_function compare)
3178 {
3179 const size_type original_number_of_elements = node_pointer_allocator_pair.total_number_of_elements;
3180
3181 if (original_number_of_elements > 1)
3182 {
3183 element_type *previous = &(begin_iterator.node_pointer->element);
3184
3185 for (iterator current = ++iterator(begin_iterator); current != end_iterator;)
3186 {
3187 if (compare(*current, *previous))
3188 {
3189 current = erase(current);
3190 }
3191 else
3192 {
3193 previous = &(current++.node_pointer->element);
3194 }
3195 }
3196 }
3197
3198 return original_number_of_elements - node_pointer_allocator_pair.total_number_of_elements;
3199 }
3200
3201
3202
3203 inline size_type unique()
3204 {
3205 return unique(eq());
3206 }
3207
3208
3209
3210 template <class predicate_function>
3211 size_type remove_if(predicate_function predicate)
3212 {
3213 const size_type original_number_of_elements = node_pointer_allocator_pair.total_number_of_elements;
3214
3215 if (original_number_of_elements != 0)
3216 {
3217 for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group)
3218 {
3219 group_size_type num_elements = current_group->number_of_elements;
3220 const node_pointer_type end = current_group->beyond_end;
3221
3222 if (end - current_group->nodes != num_elements) // If there are erased nodes present in the group
3223 {
3224 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3225 {
3226 if (current_node->next != NULL && predicate(current_node->element)) // is not free list node and validates predicate
3227 {
3228 erase(current_node);
3229
3230 if (--num_elements == 0) // ie. group will be empty (and removed) now - nothing left to iterate over
3231 {
3232 --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
3233 break;
3234 }
3235 }
3236 }
3237 }
3238 else // No erased nodes in group
3239 {
3240 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3241 {
3242 if (predicate(current_node->element))
3243 {
3244 erase(current_node);
3245
3246 if (--num_elements == 0)
3247 {
3248 --current_group;
3249 break;
3250 }
3251 }
3252 }
3253 }
3254 }
3255
3256 group_size_type num_elements = groups.last_endpoint_group->number_of_elements;
3257
3258 if (last_endpoint - groups.last_endpoint_group->nodes != num_elements) // If there are erased nodes present in the group
3259 {
3260 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3261 {
3262 if (current_node->next != NULL && predicate(current_node->element))
3263 {
3264 erase(current_node);
3265
3266 if (--num_elements == 0)
3267 {
3268 break;
3269 }
3270 }
3271 }
3272 }
3273 else
3274 {
3275 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3276 {
3277 if (predicate(current_node->element))
3278 {
3279 erase(current_node);
3280
3281 if (--num_elements == 0)
3282 {
3283 break;
3284 }
3285 }
3286 }
3287 }
3288 }
3289
3290 return original_number_of_elements - node_pointer_allocator_pair.total_number_of_elements;
3291 }
3292
3293
3294
3295 inline size_type remove(const element_type &value)
3296 {
3297 return remove_if(eq_to(value));
3298 }
3299
3300
3301
3302 void resize(const size_type number_of_elements, const element_type &value = element_type())
3303 {
3304 if (node_pointer_allocator_pair.total_number_of_elements == number_of_elements)
3305 {
3306 return;
3307 }
3308 else if (number_of_elements == 0)
3309 {
3310 clear();
3311 return;
3312 }
3313 else if (node_pointer_allocator_pair.total_number_of_elements < number_of_elements)
3314 {
3315 insert(end_iterator, number_of_elements - node_pointer_allocator_pair.total_number_of_elements, value);
3316 }
3317 else // ie. node_pointer_allocator_pair.total_number_of_elements > number_of_elements
3318 {
3319 const_iterator current(end_node.previous);
3320
3321 for (size_type number_to_remove = node_pointer_allocator_pair.total_number_of_elements - number_of_elements; number_to_remove != 0; --number_to_remove)
3322 {
3323 const node_pointer_type temp = current.node_pointer->previous;
3324 erase(current);
3325 current.node_pointer = temp;
3326 }
3327 }
3328 }
3329
3330
3331
3332 // Range assign:
3333 template <class iterator_type>
3334 inline void assign(const typename plf_enable_if_c<!std::numeric_limits<iterator_type>::is_integer, iterator_type>::type first, const iterator_type last)
3335 {
3336 clear();
3337 insert(end_iterator, first, last);
3338 groups.trim_trailing_groups();
3339 }
3340
3341
3342
3343 // Fill assign:
3344 inline void assign(const size_type number_of_elements, const element_type &value)
3345 {
3346 clear();
3347 reserve(number_of_elements); // Will return anyway if capacity already > number_of_elements
3348 insert(end_iterator, number_of_elements, value);
3349 }
3350
3351
3352
3353 #ifdef PLF_LIST_INITIALIZER_LIST_SUPPORT
3354 // Initializer-list assign:
3355 inline void assign(const std::initializer_list<element_type> &element_list)
3356 {
3357 clear();
3358 reserve(element_list.size());
3359 insert(end_iterator, element_list);
3360 }
3361 #endif
3362
3363
3364
3365 inline allocator_type get_allocator() const PLF_LIST_NOEXCEPT
3366 {
3367 return element_allocator_type();
3368 }
3369
3370
3371
3372 iterator unordered_find_single(const element_type &element_to_match) const PLF_LIST_NOEXCEPT
3373 {
3374 if (node_pointer_allocator_pair.total_number_of_elements != 0)
3375 {
3376 for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group)
3377 {
3378 const node_pointer_type end = current_group->beyond_end;
3379
3380 if (end - current_group->nodes != current_group->number_of_elements) // If there are erased nodes present in the group
3381 {
3382 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3383 {
3384 if (current_node->next != NULL && current_node->element == element_to_match) // is not free list node and matches element
3385 {
3386 return iterator(current_node);
3387 }
3388 }
3389 }
3390 else // No erased nodes in group
3391 {
3392 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3393 {
3394 if (current_node->element == element_to_match)
3395 {
3396 return iterator(current_node);
3397 }
3398 }
3399 }
3400 }
3401
3402 if (last_endpoint - groups.last_endpoint_group->nodes != groups.last_endpoint_group->number_of_elements)
3403 {
3404 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3405 {
3406 if (current_node->next != NULL && current_node->element == element_to_match)
3407 {
3408 return iterator(current_node);
3409 }
3410 }
3411 }
3412 else
3413 {
3414 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3415 {
3416 if (current_node->element == element_to_match)
3417 {
3418 return iterator(current_node);
3419 }
3420 }
3421 }
3422 }
3423
3424 return end_iterator;
3425 }
3426
3427
3428
3429 list<iterator> unordered_find_multiple(const element_type &element_to_match, const size_type number_to_find) const
3430 {
3431 list<iterator> return_list;
3432 size_type number_found = 0;
3433
3434 if (node_pointer_allocator_pair.total_number_of_elements != 0)
3435 {
3436 for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group)
3437 {
3438 const node_pointer_type end = current_group->beyond_end;
3439
3440 if (end - current_group->nodes != current_group->number_of_elements)
3441 {
3442 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3443 {
3444 if (current_node->next != NULL && current_node->element == element_to_match)
3445 {
3446 return_list.push_back(iterator(current_node));
3447
3448 if (++number_found == number_to_find)
3449 {
3450 return return_list;
3451 }
3452 }
3453 }
3454 }
3455 else // No erased nodes in group
3456 {
3457 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3458 {
3459 if (current_node->element == element_to_match)
3460 {
3461 return_list.push_back(iterator(current_node));
3462
3463 if (++number_found == number_to_find)
3464 {
3465 return return_list;
3466 }
3467 }
3468 }
3469 }
3470 }
3471
3472 if (last_endpoint - groups.last_endpoint_group->nodes != groups.last_endpoint_group->number_of_elements)
3473 {
3474 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3475 {
3476 if (current_node->next != NULL && current_node->element == element_to_match)
3477 {
3478 return_list.push_back(iterator(current_node));
3479
3480 if (++number_found == number_to_find)
3481 {
3482 return return_list;
3483 }
3484 }
3485 }
3486 }
3487 else
3488 {
3489 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3490 {
3491 if (current_node->element == element_to_match)
3492 {
3493 return_list.push_back(iterator(current_node));
3494
3495 if (++number_found == number_to_find)
3496 {
3497 return return_list;
3498 }
3499 }
3500 }
3501 }
3502 }
3503
3504 return return_list;
3505 }
3506
3507
3508
3509 list<iterator> unordered_find_all(const element_type &element_to_match) const
3510 {
3511 list<iterator> return_list;
3512
3513 if (node_pointer_allocator_pair.total_number_of_elements != 0)
3514 {
3515 for (group_pointer_type current_group = groups.block_pointer; current_group != groups.last_endpoint_group; ++current_group)
3516 {
3517 const node_pointer_type end = current_group->beyond_end;
3518
3519 if (end - current_group->nodes != current_group->number_of_elements)
3520 {
3521 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3522 {
3523 if (current_node->next != NULL && current_node->element == element_to_match)
3524 {
3525 return_list.push_back(iterator(current_node));
3526 }
3527 }
3528 }
3529 else // No erased nodes in group
3530 {
3531 for (node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node)
3532 {
3533 if (current_node->element == element_to_match)
3534 {
3535 return_list.push_back(iterator(current_node));
3536 }
3537 }
3538 }
3539 }
3540
3541 if (last_endpoint - groups.last_endpoint_group->nodes != groups.last_endpoint_group->number_of_elements)
3542 {
3543 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3544 {
3545 if (current_node->next != NULL && current_node->element == element_to_match)
3546 {
3547 return_list.push_back(iterator(current_node));
3548 }
3549 }
3550 }
3551 else
3552 {
3553 for (node_pointer_type current_node = groups.last_endpoint_group->nodes; current_node != last_endpoint; ++current_node)
3554 {
3555 if (current_node->element == element_to_match)
3556 {
3557 return_list.push_back(iterator(current_node));
3558 }
3559 }
3560 }
3561 }
3562
3563 return return_list;
3564 }
3565
3566
3567
3568 void swap(list &source) PLF_LIST_NOEXCEPT_SWAP(allocator_type)
3569 {
3570 #ifdef PLF_LIST_MOVE_SEMANTICS_SUPPORT
3571 list temp(std::move(source));
3572 source = std::move(*this);
3573 *this = std::move(temp);
3574 #else
3575 groups.swap(source.groups);
3576
3577 const node_pointer_type swap_end_node_previous = end_node.previous, swap_last_endpoint = last_endpoint;
3578 const iterator swap_begin_iterator = begin_iterator;
3579 const size_type swap_total_number_of_elements = node_pointer_allocator_pair.total_number_of_elements, swap_number_of_erased_nodes = node_allocator_pair.number_of_erased_nodes;
3580
3581 last_endpoint = source.last_endpoint;
3582 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;
3583 end_node.previous = (source.begin_iterator.node_pointer != source.end_iterator.node_pointer) ? source.end_node.previous : end_iterator.node_pointer;
3584 end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer;
3585 node_pointer_allocator_pair.total_number_of_elements = source.node_pointer_allocator_pair.total_number_of_elements;
3586 node_allocator_pair.number_of_erased_nodes = source.node_allocator_pair.number_of_erased_nodes;
3587
3588 source.last_endpoint = swap_last_endpoint;
3589 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;
3590 source.end_node.previous = (swap_begin_iterator.node_pointer != end_iterator.node_pointer) ? swap_end_node_previous : source.end_iterator.node_pointer;
3591 source.end_node.previous->next = source.begin_iterator.node_pointer->previous = source.end_iterator.node_pointer;
3592 source.node_pointer_allocator_pair.total_number_of_elements = swap_total_number_of_elements;
3593 source.node_allocator_pair.number_of_erased_nodes = swap_number_of_erased_nodes;
3594 #endif
3595 }
3596
3597}; // end of plf::list
3598
3599
3600
3601} // plf namespace
3602
3603
3604namespace std
3605{
3606
3607template <class swap_element_type, class swap_element_allocator_type>
3608inline void swap(plf::list<swap_element_type, swap_element_allocator_type> &a, plf::list<swap_element_type, swap_element_allocator_type> &b) PLF_LIST_NOEXCEPT_SWAP(swap_element_allocator_type)
3609{
3610 a.swap(b);
3611}
3612
3613}
3614
3615#undef PLF_LIST_BLOCK_MAX
3616#undef PLF_LIST_BLOCK_MIN
3617
3618#undef PLF_LIST_FORCE_INLINE
3619
3620#undef PLF_LIST_ALIGNMENT_SUPPORT
3621#undef PLF_LIST_INITIALIZER_LIST_SUPPORT
3622#undef PLF_LIST_TYPE_TRAITS_SUPPORT
3623#undef PLF_LIST_ALLOCATOR_TRAITS_SUPPORT
3624#undef PLF_LIST_VARIADICS_SUPPORT
3625#undef PLF_LIST_MOVE_SEMANTICS_SUPPORT
3626#undef PLF_LIST_NOEXCEPT
3627#undef PLF_LIST_NOEXCEPT_SWAP
3628#undef PLF_LIST_NOEXCEPT_MOVE_ASSIGNMENT
3629#undef PLF_LIST_CONSTEXPR
3630#undef PLF_LIST_CPP20_SUPPORT
3631
3632#undef PLF_LIST_CONSTRUCT
3633#undef PLF_LIST_DESTROY
3634#undef PLF_LIST_ALLOCATE
3635#undef PLF_LIST_ALLOCATE_INITIALIZATION
3636#undef PLF_LIST_DEALLOCATE
3637
3638
3639#endif // PLF_LIST_H
Definition plf_list.h:1115
Definition plf_list.h:1265
Definition plf_list.h:220
Definition plf_list.h:426
Definition plf_list.h:432