RavEngine
Loading...
Searching...
No Matches
plf_colony.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_COLONY_H
22#define PLF_COLONY_H
23
24
25// Compiler-specific defines used by colony:
26
27#if defined(_MSC_VER)
28 #define PLF_COLONY_FORCE_INLINE __forceinline
29
30 #if _MSC_VER >= 1900
31 #define PLF_COLONY_ALIGNMENT_SUPPORT
32 #define PLF_COLONY_NOEXCEPT noexcept
33 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_swap::value || std::allocator_traits<the_allocator>::is_always_equal::value)
34 #define PLF_COLONY_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)
35 #else
36 #define PLF_COLONY_NOEXCEPT throw()
37 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator)
38 #define PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) throw()
39 #endif
40
41 #if _MSC_VER >= 1600
42 #define PLF_COLONY_MOVE_SEMANTICS_SUPPORT
43 #endif
44 #if _MSC_VER >= 1700
45 #define PLF_COLONY_TYPE_TRAITS_SUPPORT
46 #define PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
47 #endif
48 #if _MSC_VER >= 1800
49 #define PLF_COLONY_VARIADICS_SUPPORT // Variadics, in this context, means both variadic templates and variadic macros are supported
50 #define PLF_COLONY_INITIALIZER_LIST_SUPPORT
51 #endif
52
53 #if defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)
54 #define PLF_COLONY_CONSTEXPR constexpr
55 #else
56 #define PLF_COLONY_CONSTEXPR
57 #endif
58 #if defined(_MSVC_LANG) && (_MSVC_LANG > 201703L)
59 #define PLF_COLONY_CPP20_SUPPORT
60 #endif
61#elif defined(__cplusplus) && __cplusplus >= 201103L // C++11 support, at least
62 #define PLF_COLONY_FORCE_INLINE // note: GCC creates faster code without forcing inline
63 #define PLF_COLONY_MOVE_SEMANTICS_SUPPORT
64
65 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__clang__) // If compiler is GCC/G++
66 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4 // 4.2 and below do not support variadic templates
67 #define PLF_COLONY_VARIADICS_SUPPORT
68 #endif
69 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4 // 4.3 and below do not support initializer lists
70 #define PLF_COLONY_INITIALIZER_LIST_SUPPORT
71 #endif
72 #if (__GNUC__ == 4 && __GNUC_MINOR__ < 6) || __GNUC__ < 4
73 #define PLF_COLONY_NOEXCEPT throw()
74 #define PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator)
75 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator)
76 #elif __GNUC__ < 6
77 #define PLF_COLONY_NOEXCEPT noexcept
78 #define PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept
79 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator) noexcept
80 #else // C++17 support
81 #define PLF_COLONY_NOEXCEPT noexcept
82 #define PLF_COLONY_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)
83 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_swap::value || std::allocator_traits<the_allocator>::is_always_equal::value)
84 #endif
85 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4
86 #define PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
87 #endif
88 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4
89 #define PLF_COLONY_ALIGNMENT_SUPPORT
90 #endif
91 #if __GNUC__ >= 5 // GCC v4.9 and below do not support std::is_trivially_copyable
92 #define PLF_COLONY_TYPE_TRAITS_SUPPORT
93 #endif
94 #elif defined(__GLIBCXX__) // Using another compiler type with libstdc++ - we are assuming full c++11 compliance for compiler - which may not be true
95 #if __GLIBCXX__ >= 20080606 // libstdc++ 4.2 and below do not support variadic templates
96 #define PLF_COLONY_VARIADICS_SUPPORT
97 #endif
98 #if __GLIBCXX__ >= 20090421 // libstdc++ 4.3 and below do not support initializer lists
99 #define PLF_COLONY_INITIALIZER_LIST_SUPPORT
100 #endif
101 #if __GLIBCXX__ >= 20160111
102 #define PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
103 #define PLF_COLONY_NOEXCEPT noexcept
104 #define PLF_COLONY_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)
105 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits<the_allocator>::propagate_on_container_swap::value || std::allocator_traits<the_allocator>::is_always_equal::value)
106 #elif __GLIBCXX__ >= 20120322
107 #define PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
108 #define PLF_COLONY_NOEXCEPT noexcept
109 #define PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept
110 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator) noexcept
111 #else
112 #define PLF_COLONY_NOEXCEPT throw()
113 #define PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator)
114 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator)
115 #endif
116 #if __GLIBCXX__ >= 20130322
117 #define PLF_COLONY_ALIGNMENT_SUPPORT
118 #endif
119 #if __GLIBCXX__ >= 20150422 // libstdc++ v4.9 and below do not support std::is_trivially_copyable
120 #define PLF_COLONY_TYPE_TRAITS_SUPPORT
121 #endif
122 #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
123 #define PLF_COLONY_NOEXCEPT throw()
124 #define PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator)
125 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator)
126 #else // Assume type traits and initializer support for other compilers and standard libraries
127 #define PLF_COLONY_VARIADICS_SUPPORT
128 #define PLF_COLONY_TYPE_TRAITS_SUPPORT
129 #define PLF_COLONY_MOVE_SEMANTICS_SUPPORT
130 #define PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
131 #define PLF_COLONY_ALIGNMENT_SUPPORT
132 #define PLF_COLONY_INITIALIZER_LIST_SUPPORT
133 #define PLF_COLONY_NOEXCEPT noexcept
134 #define PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept(std::allocator_traits<the_allocator>::is_always_equal::value)
135 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator) noexcept
136 #endif
137
138 #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
139 #define PLF_COLONY_CONSTEXPR constexpr
140 #else
141 #define PLF_COLONY_CONSTEXPR
142 #endif
143 #if __cplusplus > 201703L && ((defined(__clang__) && (__clang_major__ >= 10)) || (defined(__GNUC__) && __GNUC__ >= 10) || (!defined(__clang__) && !defined(__GNUC__))) // assume correct C++20 implementation for other compilers
144 #define PLF_COLONY_CPP20_SUPPORT
145 #endif
146#else
147 #define PLF_COLONY_FORCE_INLINE
148 #define PLF_COLONY_NOEXCEPT throw()
149 #define PLF_COLONY_NOEXCEPT_SWAP(the_allocator)
150 #define PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator)
151 #define PLF_COLONY_CONSTEXPR
152#endif
153
154
155
156
157
158#ifdef PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
159 #ifdef PLF_COLONY_VARIADICS_SUPPORT
160 #define PLF_COLONY_CONSTRUCT(the_allocator, allocator_instance, location, ...) std::allocator_traits<the_allocator>::construct(allocator_instance, location, __VA_ARGS__)
161 #else
162 #define PLF_COLONY_CONSTRUCT(the_allocator, allocator_instance, location, data) std::allocator_traits<the_allocator>::construct(allocator_instance, location, data)
163 #endif
164
165 #define PLF_COLONY_DESTROY(the_allocator, allocator_instance, location) std::allocator_traits<the_allocator>::destroy(allocator_instance, location)
166 #define PLF_COLONY_ALLOCATE(the_allocator, allocator_instance, size, hint) std::allocator_traits<the_allocator>::allocate(allocator_instance, size, hint)
167 #define PLF_COLONY_ALLOCATE_INITIALIZATION(the_allocator, size, hint) std::allocator_traits<the_allocator>::allocate(*this, size, hint)
168 #define PLF_COLONY_DEALLOCATE(the_allocator, allocator_instance, location, size) std::allocator_traits<the_allocator>::deallocate(allocator_instance, location, size)
169#else
170 #ifdef PLF_COLONY_VARIADICS_SUPPORT
171 #define PLF_COLONY_CONSTRUCT(the_allocator, allocator_instance, location, ...) allocator_instance.construct(location, __VA_ARGS__)
172 #else
173 #define PLF_COLONY_CONSTRUCT(the_allocator, allocator_instance, location, data) allocator_instance.construct(location, data)
174 #endif
175
176 #define PLF_COLONY_DESTROY(the_allocator, allocator_instance, location) allocator_instance.destroy(location)
177 #define PLF_COLONY_ALLOCATE(the_allocator, allocator_instance, size, hint) allocator_instance.allocate(size, hint)
178 #define PLF_COLONY_ALLOCATE_INITIALIZATION(the_allocator, size, hint) the_allocator::allocate(size, hint)
179 #define PLF_COLONY_DEALLOCATE(the_allocator, allocator_instance, location, size) allocator_instance.deallocate(location, size)
180#endif
181
182
183
184#include <algorithm> // std::fill_n
185
186#include <cstring> // memset, memcpy
187#include <cassert> // assert
188#include <limits> // std::numeric_limits
189#include <memory> // std::allocator
190#include <iterator> // std::bidirectional_iterator_tag, iterator_traits
191
192
193#ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
194 #include <cstddef> // offsetof, used in blank()
195 #include <type_traits> // std::is_trivially_destructible, etc
196#endif
197
198#ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
199 #include <utility> // std::move
200#endif
201
202#ifdef PLF_COLONY_INITIALIZER_LIST_SUPPORT
203 #include <initializer_list>
204#endif
205
206
207namespace plf
208{
209
210
211struct limits // for use in block_capacity setting/getting functions and constructors
212{
213 size_t min, max;
214 limits(const size_t minimum, const size_t maximum) PLF_COLONY_NOEXCEPT : min(minimum), max(maximum) {};
215};
216
217
218
219template <class element_type, class element_allocator_type = std::allocator<element_type>, typename element_skipfield_type = unsigned short > class colony : private element_allocator_type // Empty base class optimisation - inheriting allocator functions
220// Note: unsigned short is equivalent to uint_least16_t ie. Using 16-bit unsigned integer in best-case scenario, greater-than-16-bit unsigned integer where platform doesn't support 16-bit types
221{
222public:
223 // Standard container typedefs:
224 typedef element_type value_type;
225 typedef element_allocator_type allocator_type;
226 typedef element_skipfield_type skipfield_type;
227
228 #ifdef PLF_COLONY_ALIGNMENT_SUPPORT
229 typedef typename std::aligned_storage<sizeof(element_type), (sizeof(element_type) > (sizeof(element_skipfield_type) * 2)) ? alignof(element_type) : (sizeof(element_skipfield_type) * 2)>::type aligned_element_type;
230 #else
231 typedef element_type aligned_element_type;
232 #endif
233
234 #ifdef PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
235 typedef typename std::allocator_traits<element_allocator_type>::size_type size_type;
236 typedef typename std::allocator_traits<element_allocator_type>::difference_type difference_type;
237 typedef element_type & reference;
238 typedef const element_type & const_reference;
239 typedef typename std::allocator_traits<element_allocator_type>::pointer pointer;
240 typedef typename std::allocator_traits<element_allocator_type>::const_pointer const_pointer;
241 #else
242 typedef typename element_allocator_type::size_type size_type;
243 typedef typename element_allocator_type::difference_type difference_type;
244 typedef typename element_allocator_type::reference reference;
245 typedef typename element_allocator_type::const_reference const_reference;
246 typedef typename element_allocator_type::pointer pointer;
247 typedef typename element_allocator_type::const_pointer const_pointer;
248 #endif
249
250
251 // Iterator declarations:
252 template <bool is_const> class colony_iterator;
255 friend class colony_iterator<false>; // Using above typedef name here is illegal under C++03
256 friend class colony_iterator<true>;
257
258 template <bool r_is_const> class colony_reverse_iterator;
261 friend class colony_reverse_iterator<false>;
262 friend class colony_reverse_iterator<true>;
263
264
265private:
266
267
268 struct group; // forward declaration for typedefs below
269
270 #ifdef PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
271 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<aligned_element_type> aligned_element_allocator_type;
272 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<group> group_allocator_type;
273 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<skipfield_type> skipfield_allocator_type;
274 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<unsigned char> uchar_allocator_type; // Using uchar as the generic allocator type, as sizeof is always guaranteed to be 1 byte regardless of the number of bits in a byte on given computer, whereas for example, uint8_t would fail on machines where there are more than 8 bits in a byte eg. Texas Instruments C54x DSPs.
275
276 typedef typename std::allocator_traits<aligned_element_allocator_type>::pointer aligned_pointer_type; // Different typedef to 'pointer' - this is a pointer to the overaligned element type, not the original element type
277 typedef typename std::allocator_traits<group_allocator_type>::pointer group_pointer_type;
278 typedef typename std::allocator_traits<skipfield_allocator_type>::pointer skipfield_pointer_type;
279 typedef typename std::allocator_traits<uchar_allocator_type>::pointer uchar_pointer_type;
280
281 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<pointer> pointer_allocator_type;
282 #else
283 typedef typename element_allocator_type::template rebind<aligned_element_type>::other aligned_element_allocator_type; // In case compiler supports alignment but not allocator_traits
284 typedef typename element_allocator_type::template rebind<group>::other group_allocator_type;
285 typedef typename element_allocator_type::template rebind<skipfield_type>::other skipfield_allocator_type;
286 typedef typename element_allocator_type::template rebind<unsigned char>::other uchar_allocator_type;
287
288 typedef typename aligned_element_allocator_type::pointer aligned_pointer_type;
289 typedef typename group_allocator_type::pointer group_pointer_type;
290 typedef typename skipfield_allocator_type::pointer skipfield_pointer_type;
291 typedef typename uchar_allocator_type::pointer uchar_pointer_type;
292
293 typedef typename element_allocator_type::template rebind<pointer>::other pointer_allocator_type;
294 #endif
295
296
297
298 // Colony groups:
299 struct group : private uchar_allocator_type // Empty base class optimisation (EBCO) - inheriting allocator functions
300 {
301 aligned_pointer_type last_endpoint; // The address that is one past the highest cell number that's been used so far in this group - does not change with erase command but may change with insert (if no previously-erased locations are available) - is necessary because an iterator cannot access the colony's end_iterator. Most-used variable in colony use (operator ++, --) so first in struct
302 group_pointer_type next_group; // Next group in the intrusive list of all groups. NULL if no next group
303 const aligned_pointer_type elements; // Element storage
304 const skipfield_pointer_type skipfield; // Skipfield storage. The element and skipfield arrays are allocated contiguously, hence the skipfield pointer also functions as a 'one-past-end' pointer for the elements array. There will always be one additional skipfield node allocated compared to the number of elements. This is to ensure a faster ++ iterator operation (fewer checks are required when this is present). The extra node is unused and always zero, but checked, and not having it will result in out-of-bounds memory errors.
305 group_pointer_type previous_group; // previous group in the intrusive list of all groups. NULL if no preceding group
306 skipfield_type free_list_head; // The index of the last erased element in the group. The last erased element will, in turn, contain the number of the index of the next erased element, and so on. If this is == maximum skipfield_type value then free_list is empty ie. no erasures have occurred in the group (or if they have, the erased locations have then been reused via insert()).
307 const skipfield_type capacity; // The element capacity of this particular group
308 skipfield_type number_of_elements; // indicates total number of active elements in group - changes with insert and erase commands - used to check for empty group in erase function, as an indication to remove the group
309 group_pointer_type erasures_list_next_group; // The next group in the intrusive singly-linked list of groups with erasures ie. with active erased-element free lists
310 size_type group_number; // Used for comparison (> < >= <=) iterator operators (used by distance function and user)
311
312
313 #ifdef PLF_COLONY_VARIADICS_SUPPORT
314 group(const skipfield_type elements_per_group, group_pointer_type const previous = NULL):
315 last_endpoint(reinterpret_cast<aligned_pointer_type>(PLF_COLONY_ALLOCATE_INITIALIZATION(uchar_allocator_type, ((elements_per_group * (sizeof(aligned_element_type))) + ((static_cast<size_type>(elements_per_group) + 1u) * sizeof(skipfield_type))), (previous == NULL) ? 0 : previous->elements))), /* allocating to here purely because it is first in the struct sequence - actual pointer is elements, last_endpoint is only initialised to element's base value initially, then incremented by one below */
316 next_group(NULL),
317 elements(last_endpoint++),
318 skipfield(reinterpret_cast<skipfield_pointer_type>(elements + elements_per_group)),
319 previous_group(previous),
320 free_list_head(std::numeric_limits<skipfield_type>::max()),
321 capacity(elements_per_group),
322 number_of_elements(1),
323 erasures_list_next_group(NULL),
324 group_number((previous == NULL) ? 0 : previous->group_number + 1u)
325 {
326 // Static casts to unsigned int from short not necessary as C++ automatically promotes lesser types for arithmetic purposes.
327 std::memset(&*skipfield, 0, sizeof(skipfield_type) * (static_cast<size_type>(elements_per_group) + 1u)); // &* to avoid problems with non-trivial pointers
328 }
329
330 #else
331 // This is a hack around the fact that element_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 losing performance) from allocating the same block twice, we're allocating in this constructor and moving data in the copy constructor.
332 group(const skipfield_type elements_per_group, group_pointer_type const previous = NULL):
333 last_endpoint(reinterpret_cast<aligned_pointer_type>(PLF_COLONY_ALLOCATE_INITIALIZATION(uchar_allocator_type, ((elements_per_group * (sizeof(aligned_element_type))) + (static_cast<size_type>(elements_per_group + 1) * sizeof(skipfield_type))), (previous == NULL) ? 0 : previous->elements))),
334 elements(NULL),
335 skipfield(reinterpret_cast<skipfield_pointer_type>(last_endpoint + elements_per_group)),
336 previous_group(previous),
337 capacity(elements_per_group)
338 {
339 std::memset(&*skipfield, 0, sizeof(skipfield_type) * (elements_per_group + 1u));
340 }
341
342
343
344 // Not a real copy constructor ie. actually a move constructor. Only used for allocator.construct in C++03 for reasons stated above:
345 group(const group &source) PLF_COLONY_NOEXCEPT:
346 uchar_allocator_type(source),
347 last_endpoint(source.last_endpoint + 1),
348 next_group(NULL),
349 elements(source.last_endpoint),
350 skipfield(source.skipfield),
351 previous_group(source.previous_group),
352 free_list_head(std::numeric_limits<skipfield_type>::max()),
353 capacity(source.capacity),
354 number_of_elements(1),
355 erasures_list_next_group(NULL),
356 group_number((source.previous_group == NULL) ? 0 : source.previous_group->group_number + 1u)
357 {}
358 #endif
359
360
361
362 ~group() PLF_COLONY_NOEXCEPT
363 {
364 // Null check not necessary (for copied group as above) as delete will also perform a null check.
365 PLF_COLONY_DEALLOCATE(uchar_allocator_type, (*this), reinterpret_cast<uchar_pointer_type>(elements), (capacity * sizeof(aligned_element_type)) + ((static_cast<size_type>(capacity) + 1u) * sizeof(skipfield_type)));
366 }
367 };
368
369
370
371
372 // Implement const/non-const iterator switching pattern:
373 template <bool flag, class is_true, class is_false> struct choose;
374
375 template <class is_true, class is_false> struct choose<true, is_true, is_false>
376 {
377 typedef is_true type;
378 };
379
380 template <class is_true, class is_false> struct choose<false, is_true, is_false>
381 {
382 typedef is_false type;
383 };
384
385
386public:
387
388
389 // Iterators:
390 template <bool is_const> class colony_iterator
391 {
392 private:
393 group_pointer_type group_pointer;
394 aligned_pointer_type element_pointer;
395 skipfield_pointer_type skipfield_pointer;
396
397 public:
398 typedef std::bidirectional_iterator_tag iterator_category;
399 typedef typename colony::value_type value_type;
400 typedef typename colony::difference_type difference_type;
401 typedef typename choose<is_const, typename colony::const_pointer, typename colony::pointer>::type pointer;
402 typedef typename choose<is_const, typename colony::const_reference, typename colony::reference>::type reference;
403
404 friend class colony;
405 friend class colony_reverse_iterator<false>;
406 friend class colony_reverse_iterator<true>;
407
408
409
410 inline colony_iterator & operator = (const colony_iterator &source) PLF_COLONY_NOEXCEPT
411 {
412 group_pointer = source.group_pointer;
413 element_pointer = source.element_pointer;
414 skipfield_pointer = source.skipfield_pointer;
415 return *this;
416 }
417
418
419
420 inline colony_iterator & operator = (const colony_iterator<!is_const> &source) PLF_COLONY_NOEXCEPT
421 {
422 group_pointer = source.group_pointer;
423 element_pointer = source.element_pointer;
424 skipfield_pointer = source.skipfield_pointer;
425 return *this;
426 }
427
428
429
430 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
431 // Move assignment - only really necessary if the allocator uses non-standard ie. smart pointers
432 inline colony_iterator & operator = (colony_iterator &&source) PLF_COLONY_NOEXCEPT // Move is a copy in this scenario
433 {
434 assert (&source != this);
435 group_pointer = std::move(source.group_pointer);
436 element_pointer = std::move(source.element_pointer);
437 skipfield_pointer = std::move(source.skipfield_pointer);
438 return *this;
439 }
440
441
442
443 inline colony_iterator & operator = (colony_iterator<!is_const> &&source) PLF_COLONY_NOEXCEPT
444 {
445 group_pointer = std::move(source.group_pointer);
446 element_pointer = std::move(source.element_pointer);
447 skipfield_pointer = std::move(source.skipfield_pointer);
448 return *this;
449 }
450 #endif
451
452
453
454 inline PLF_COLONY_FORCE_INLINE bool operator == (const colony_iterator &rh) const PLF_COLONY_NOEXCEPT
455 {
456 return (element_pointer == rh.element_pointer);
457 }
458
459
460
461 inline PLF_COLONY_FORCE_INLINE bool operator == (const colony_iterator<!is_const> &rh) const PLF_COLONY_NOEXCEPT
462 {
463 return (element_pointer == rh.element_pointer);
464 }
465
466
467
468 inline PLF_COLONY_FORCE_INLINE bool operator != (const colony_iterator &rh) const PLF_COLONY_NOEXCEPT
469 {
470 return (element_pointer != rh.element_pointer);
471 }
472
473
474
475 inline PLF_COLONY_FORCE_INLINE bool operator != (const colony_iterator<!is_const> &rh) const PLF_COLONY_NOEXCEPT
476 {
477 return (element_pointer != rh.element_pointer);
478 }
479
480
481
482 inline PLF_COLONY_FORCE_INLINE reference operator * () const // may cause exception with uninitialized iterator
483 {
484 return *(reinterpret_cast<pointer>(element_pointer));
485 }
486
487
488
489 inline PLF_COLONY_FORCE_INLINE pointer operator -> () const PLF_COLONY_NOEXCEPT
490 {
491 return reinterpret_cast<pointer>(element_pointer);
492 }
493
494
495
496#if defined(_MSC_VER) && _MSC_VER <= 1600 // MSVC 2010 needs a bit of a helping hand when it comes to optimizing
497 inline PLF_COLONY_FORCE_INLINE colony_iterator & operator ++ ()
498#else
499 colony_iterator & operator ++ ()
500#endif
501 {
502 assert(group_pointer != NULL); // covers uninitialised colony_iterator
503 assert(!(element_pointer == group_pointer->last_endpoint && group_pointer->next_group != NULL)); // Assert that iterator is not already at end()
504
505 skipfield_type skip = *(++skipfield_pointer);
506
507 if ((element_pointer += static_cast<size_type>(skip) + 1u) == group_pointer->last_endpoint && group_pointer->next_group != NULL) // ie. beyond end of available data
508 {
509 group_pointer = group_pointer->next_group;
510 const aligned_pointer_type elements = group_pointer->elements;
511 const skipfield_pointer_type skipfield = group_pointer->skipfield;
512 skip = *skipfield;
513 element_pointer = elements + skip;
514 skipfield_pointer = skipfield;
515 }
516
517 skipfield_pointer += skip;
518 return *this;
519 }
520
521
522
523 inline colony_iterator operator ++(int)
524 {
525 const colony_iterator copy(*this);
526 ++*this;
527 return copy;
528 }
529
530
531
532 private:
533 inline PLF_COLONY_FORCE_INLINE void check_for_end_of_group_and_progress() // used by erase
534 {
535 if (element_pointer == group_pointer->last_endpoint && group_pointer->next_group != NULL)
536 {
537 group_pointer = group_pointer->next_group;
538 const aligned_pointer_type elements = group_pointer->elements;
539 const skipfield_pointer_type skipfield = group_pointer->skipfield;
540 const skipfield_type skip = *skipfield;
541 element_pointer = elements + skip;
542 skipfield_pointer = skipfield + skip;
543 }
544 }
545
546
547
548 public:
549
550 colony_iterator & operator -- ()
551 {
552 assert(group_pointer != NULL);
553 assert(!(element_pointer == group_pointer->elements && group_pointer->previous_group == NULL)); // Assert that we are not already at begin() - this is not required to be tested in the code below as we don't need a special condition to progress to begin(), like we do with end() in operator ++
554
555 if (element_pointer != group_pointer->elements) // ie. not already at beginning of group
556 {
557 const skipfield_type skip = *(--skipfield_pointer);
558 skipfield_pointer -= skip;
559
560 if ((element_pointer -= static_cast<size_type>(skip) + 1u) != group_pointer->elements - 1) // ie. iterator was not already at beginning of colony (with some previous consecutive deleted elements), and skipfield does not takes us into the previous group)
561 {
562 return *this;
563 }
564 }
565
566 group_pointer = group_pointer->previous_group;
567 const skipfield_pointer_type skipfield = group_pointer->skipfield + group_pointer->capacity - 1;
568 const skipfield_type skip = *skipfield;
569 element_pointer = (reinterpret_cast<colony::aligned_pointer_type>(group_pointer->skipfield) - 1) - skip;
570 skipfield_pointer = skipfield - skip;
571
572 return *this;
573 }
574
575
576
577 inline colony_iterator operator -- (int)
578 {
579 const colony_iterator copy(*this);
580 --*this;
581 return copy;
582 }
583
584
585
586 inline bool operator > (const colony_iterator &rh) const PLF_COLONY_NOEXCEPT
587 {
588 return ((group_pointer == rh.group_pointer) & (element_pointer > rh.element_pointer)) || (group_pointer != rh.group_pointer && group_pointer->group_number > rh.group_pointer->group_number);
589 }
590
591
592
593 inline bool operator < (const colony_iterator &rh) const PLF_COLONY_NOEXCEPT
594 {
595 return rh > *this;
596 }
597
598
599
600 inline bool operator >= (const colony_iterator &rh) const PLF_COLONY_NOEXCEPT
601 {
602 return !(rh > *this);
603 }
604
605
606
607 inline bool operator <= (const colony_iterator &rh) const PLF_COLONY_NOEXCEPT
608 {
609 return !(*this > rh);
610 }
611
612
613
614 inline bool operator > (const colony_iterator<!is_const> &rh) const PLF_COLONY_NOEXCEPT
615 {
616 return ((group_pointer == rh.group_pointer) & (element_pointer > rh.element_pointer)) || (group_pointer != rh.group_pointer && group_pointer->group_number > rh.group_pointer->group_number);
617 }
618
619
620
621 inline bool operator < (const colony_iterator<!is_const> &rh) const PLF_COLONY_NOEXCEPT
622 {
623 return rh > *this;
624 }
625
626
627
628 inline bool operator >= (const colony_iterator<!is_const> &rh) const PLF_COLONY_NOEXCEPT
629 {
630 return !(rh > *this);
631 }
632
633
634
635 inline bool operator <= (const colony_iterator<!is_const> &rh) const PLF_COLONY_NOEXCEPT
636 {
637 return !(*this > rh);
638 }
639
640
641 // C++20:
642 #ifdef PLF_COLONY_CPP20_SUPPORT
643 inline int operator <=> (const colony_iterator &rh) const PLF_COLONY_NOEXCEPT
644 {
645 return (element_pointer == rh.element_pointer) ? 0 : ((*this > rh) ? 1 : -1);
646 }
647
648
649 inline int operator <=> (const colony_iterator<!is_const> &rh) const PLF_COLONY_NOEXCEPT
650 {
651 return (element_pointer == rh.element_pointer) ? 0 : ((*this > rh) ? 1 : -1);
652 }
653 #endif
654
655
656
657 colony_iterator() PLF_COLONY_NOEXCEPT: group_pointer(NULL), element_pointer(NULL), skipfield_pointer(NULL) {}
658
659
660
661 private:
662 // Used by cend(), erase() etc:
663 colony_iterator(const group_pointer_type group_p, const aligned_pointer_type element_p, const skipfield_pointer_type skipfield_p) PLF_COLONY_NOEXCEPT: group_pointer(group_p), element_pointer(element_p), skipfield_pointer(skipfield_p) {}
664
665
666
667 public:
668
669 inline colony_iterator (const colony_iterator &source) PLF_COLONY_NOEXCEPT:
670 group_pointer(source.group_pointer),
671 element_pointer(source.element_pointer),
672 skipfield_pointer(source.skipfield_pointer)
673 {}
674
675
676 inline colony_iterator(const colony_iterator<!is_const> &source) PLF_COLONY_NOEXCEPT:
677 group_pointer(source.group_pointer),
678 element_pointer(source.element_pointer),
679 skipfield_pointer(source.skipfield_pointer)
680 {}
681
682
683
684 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
685 // move constructor
686 inline colony_iterator(colony_iterator &&source) PLF_COLONY_NOEXCEPT:
687 group_pointer(std::move(source.group_pointer)),
688 element_pointer(std::move(source.element_pointer)),
689 skipfield_pointer(std::move(source.skipfield_pointer))
690 {
691 assert (&source != this);
692 }
693
694
695 inline colony_iterator(colony_iterator<!is_const> &&source) PLF_COLONY_NOEXCEPT:
696 group_pointer(std::move(source.group_pointer)),
697 element_pointer(std::move(source.element_pointer)),
698 skipfield_pointer(std::move(source.skipfield_pointer))
699 {}
700 #endif
701
702
703 }; // colony_iterator
704
705
706
707
708
709 // Reverse iterators:
710
711 template <bool r_is_const> class colony_reverse_iterator
712 {
713 private:
714 iterator it;
715
716 public:
717 typedef std::bidirectional_iterator_tag iterator_category;
718 typedef typename colony::value_type value_type;
719 typedef typename colony::difference_type difference_type;
720 typedef typename choose<r_is_const, typename colony::const_pointer, typename colony::pointer>::type pointer;
721 typedef typename choose<r_is_const, typename colony::const_reference, typename colony::reference>::type reference;
722
723 friend class colony;
724
725
726 inline colony_reverse_iterator& operator = (const colony_reverse_iterator &source) PLF_COLONY_NOEXCEPT
727 {
728 it = source.it;
729 return *this;
730 }
731
732
733
734 inline colony_reverse_iterator& operator = (const colony_reverse_iterator<!r_is_const> &source) PLF_COLONY_NOEXCEPT
735 {
736 it = source.it;
737 return *this;
738 }
739
740
741
742 template<bool is_const>
743 inline colony_reverse_iterator& operator = (const colony_iterator<is_const> &source) PLF_COLONY_NOEXCEPT
744 {
745 it = source;
746 return *this;
747 }
748
749
750
751 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
752 // move assignment
753 inline colony_reverse_iterator& operator = (colony_reverse_iterator &&source) PLF_COLONY_NOEXCEPT
754 {
755 assert (&source != this);
756 it = std::move(source.it);
757 return *this;
758 }
759
760
761 inline colony_reverse_iterator& operator = (colony_reverse_iterator<!r_is_const> &&source) PLF_COLONY_NOEXCEPT
762 {
763 it = std::move(source.it);
764 return *this;
765 }
766 #endif
767
768
769
770 inline PLF_COLONY_FORCE_INLINE bool operator == (const colony_reverse_iterator &rh) const PLF_COLONY_NOEXCEPT
771 {
772 return (it == rh.it);
773 }
774
775
776
777 inline PLF_COLONY_FORCE_INLINE bool operator == (const colony_reverse_iterator<!r_is_const> &rh) const PLF_COLONY_NOEXCEPT
778 {
779 return (it == rh.it);
780 }
781
782
783
784 inline PLF_COLONY_FORCE_INLINE bool operator != (const colony_reverse_iterator &rh) const PLF_COLONY_NOEXCEPT
785 {
786 return (it != rh.it);
787 }
788
789
790
791 inline PLF_COLONY_FORCE_INLINE bool operator != (const colony_reverse_iterator<!r_is_const> &rh) const PLF_COLONY_NOEXCEPT
792 {
793 return (it != rh.it);
794 }
795
796
797
798 inline PLF_COLONY_FORCE_INLINE reference operator * () const PLF_COLONY_NOEXCEPT
799 {
800 return *(reinterpret_cast<pointer>(it.element_pointer));
801 }
802
803
804
805 inline PLF_COLONY_FORCE_INLINE pointer * operator -> () const PLF_COLONY_NOEXCEPT
806 {
807 return reinterpret_cast<pointer>(it.element_pointer);
808 }
809
810
811
812 // In this case we have to redefine the algorithm, rather than using the internal iterator's -- operator, in order for the reverse_iterator to be allowed to reach rend() ie. begin_iterator - 1
813 colony_reverse_iterator & operator ++ ()
814 {
815 colony::group_pointer_type &group_pointer = it.group_pointer;
816 colony::aligned_pointer_type &element_pointer = it.element_pointer;
817 colony::skipfield_pointer_type &skipfield_pointer = it.skipfield_pointer;
818
819 assert(group_pointer != NULL);
820 assert(!(element_pointer == group_pointer->elements - 1 && group_pointer->previous_group == NULL)); // Assert that we are not already at rend()
821
822 if (element_pointer != group_pointer->elements) // ie. not already at beginning of group
823 {
824 element_pointer -= static_cast<size_type>(*(--skipfield_pointer)) + 1u;
825 skipfield_pointer -= *skipfield_pointer;
826
827 if (!(element_pointer == group_pointer->elements - 1 && group_pointer->previous_group == NULL)) // ie. iterator is not == rend()
828 {
829 return *this;
830 }
831 }
832
833 if (group_pointer->previous_group != NULL) // ie. not first group in colony
834 {
835 group_pointer = group_pointer->previous_group;
836 skipfield_pointer = group_pointer->skipfield + group_pointer->capacity - 1;
837 element_pointer = (reinterpret_cast<colony::aligned_pointer_type>(group_pointer->skipfield) - 1) - *skipfield_pointer;
838 skipfield_pointer -= *skipfield_pointer;
839 }
840 else // necessary so that reverse_iterator can end up == rend(), if we were already at first element in colony
841 {
842 --element_pointer;
843 --skipfield_pointer;
844 }
845
846 return *this;
847 }
848
849
850
851 inline colony_reverse_iterator operator ++ (int)
852 {
853 const colony_reverse_iterator copy(*this);
854 ++*this;
855 return copy;
856 }
857
858
859
860 inline PLF_COLONY_FORCE_INLINE colony_reverse_iterator & operator -- ()
861 {
862 assert(!(it.element_pointer == it.group_pointer->last_endpoint - 1 && it.group_pointer->next_group == NULL)); // ie. Check that we are not already at rbegin()
863 ++it;
864 return *this;
865 }
866
867
868
869 inline colony_reverse_iterator operator -- (int)
870 {
871 const colony_reverse_iterator copy(*this);
872 --*this;
873 return copy;
874 }
875
876
877
878 inline typename colony::iterator base() const
879 {
880 return ++(typename colony::iterator(it));
881 }
882
883
884
885 inline bool operator > (const colony_reverse_iterator &rh) const PLF_COLONY_NOEXCEPT
886 {
887 return (rh.it > it);
888 }
889
890
891
892 inline bool operator < (const colony_reverse_iterator &rh) const PLF_COLONY_NOEXCEPT
893 {
894 return (it > rh.it);
895 }
896
897
898
899 inline bool operator >= (const colony_reverse_iterator &rh) const PLF_COLONY_NOEXCEPT
900 {
901 return !(it > rh.it);
902 }
903
904
905
906 inline bool operator <= (const colony_reverse_iterator &rh) const PLF_COLONY_NOEXCEPT
907 {
908 return !(rh.it > it);
909 }
910
911
912
913 inline bool operator > (const colony_reverse_iterator<!r_is_const> &rh) const PLF_COLONY_NOEXCEPT
914 {
915 return (rh.it > it);
916 }
917
918
919
920 inline bool operator < (const colony_reverse_iterator<!r_is_const> &rh) const PLF_COLONY_NOEXCEPT
921 {
922 return (it > rh.it);
923 }
924
925
926
927 inline bool operator >= (const colony_reverse_iterator<!r_is_const> &rh) const PLF_COLONY_NOEXCEPT
928 {
929 return !(it > rh.it);
930 }
931
932
933
934 inline bool operator <= (const colony_reverse_iterator<!r_is_const> &rh) const PLF_COLONY_NOEXCEPT
935 {
936 return !(rh.it > it);
937 }
938
939
940
941 // C++20:
942 #ifdef PLF_COLONY_CPP20_SUPPORT
943 inline int operator <=> (const colony_reverse_iterator &rh) const PLF_COLONY_NOEXCEPT
944 {
945 return (rh.it <=> it);
946 }
947
948
949 inline int operator <=> (const colony_reverse_iterator<!r_is_const> &rh) const PLF_COLONY_NOEXCEPT
950 {
951 return (rh.it <=> it);
952 }
953 #endif
954
955
956
957 colony_reverse_iterator () PLF_COLONY_NOEXCEPT
958 {}
959
960
961
962 colony_reverse_iterator (const colony_reverse_iterator &source) PLF_COLONY_NOEXCEPT:
963 it(source.it)
964 {}
965
966
967
968 colony_reverse_iterator (const colony_reverse_iterator<!r_is_const> &source) PLF_COLONY_NOEXCEPT:
969 it(source.it)
970 {}
971
972
973
974 template<bool is_const>
975 colony_reverse_iterator (const colony_iterator<is_const> &source) PLF_COLONY_NOEXCEPT:
976 it(source)
977 {}
978
979
980
981
982 private:
983 // Used by rend(), etc:
984 colony_reverse_iterator(const group_pointer_type group_p, const aligned_pointer_type element_p, const skipfield_pointer_type skipfield_p) PLF_COLONY_NOEXCEPT: it(group_p, element_p, skipfield_p) {}
985
986
987
988 public:
989
990 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
991 // move constructors
992 colony_reverse_iterator (colony_reverse_iterator &&source) PLF_COLONY_NOEXCEPT:
993 it(std::move(source.it))
994 {
995 assert (&source != this);
996 }
997
999 it(std::move(source.it))
1000 {}
1001 #endif
1002
1003 }; // colony_reverse_iterator
1004
1005
1006
1007
1008private:
1009
1010 // Used to prevent fill-insert/constructor calls being mistakenly resolved to range-insert/constructor calls
1011 template <bool condition, class T = void>
1012 struct plf_enable_if_c
1013 {
1014 typedef T type;
1015 };
1016
1017 template <class T>
1018 struct plf_enable_if_c<false, T>
1019 {};
1020
1021
1022 iterator end_iterator, begin_iterator;
1023 group_pointer_type groups_with_erasures_list_head; // Head of a singly-linked intrusive list of groups which have erased-element memory locations available for reuse
1024 size_type total_number_of_elements, total_capacity;
1025
1026 struct ebco_pair2 : pointer_allocator_type // Packaging the element pointer allocator with a lesser-used member variable, for empty-base-class optimisation
1027 {
1028 skipfield_type min_elements_per_group;
1029 explicit ebco_pair2(const skipfield_type min_elements) PLF_COLONY_NOEXCEPT: min_elements_per_group(min_elements) {}
1030 } pointer_allocator_pair;
1031
1032 struct ebco_pair : group_allocator_type
1033 {
1034 skipfield_type max_elements_per_group;
1035 explicit ebco_pair(const skipfield_type max_elements) PLF_COLONY_NOEXCEPT: max_elements_per_group(max_elements) {}
1036 } group_allocator_pair;
1037
1038
1039
1040 // An adaptive minimum based around sizeof(element_type) and sizeof the group metadata:
1041 #define PLF_COLONY_MIN_BLOCK_CAPACITY (sizeof(aligned_element_type) * 8 > (sizeof(plf::colony<element_type>) + sizeof(group)) * 2) ? 8 : (((sizeof(plf::colony<element_type>) + sizeof(group)) * 2) / sizeof(aligned_element_type))
1042
1043
1044public:
1045
1046 // Default constuctor:
1047
1048 colony() PLF_COLONY_NOEXCEPT:
1049 element_allocator_type(element_allocator_type()),
1050 groups_with_erasures_list_head(NULL),
1051 total_number_of_elements(0),
1052 total_capacity(0),
1053 pointer_allocator_pair(PLF_COLONY_MIN_BLOCK_CAPACITY),
1054 group_allocator_pair(std::numeric_limits<skipfield_type>::max())
1055 {
1056 assert(std::numeric_limits<skipfield_type>::is_integer & !std::numeric_limits<skipfield_type>::is_signed); // skipfield type must be of unsigned integer type (uchar, ushort, uint etc)
1057
1058 #ifndef PLF_COLONY_ALIGNMENT_SUPPORT
1059 assert(sizeof(element_type) >= sizeof(skipfield_type) * 2); // eg. under C++03, aligned_storage is not available, so sizeof(skipfield type) * 2 must be larger or equal to sizeof(element_type), otherwise the doubly-linked free lists of erased element indexes will not work correctly. So if you're storing chars, for example, and using the default skipfield type (unsigned short), the compiler will flag you with this assert. You cannot store char or unsigned char in colony under C++03, and if storing short or unsigned short you must change your skipfield type to unsigned char. Or just use C++11 and above.
1060 #endif
1061 }
1062
1063
1064
1065 colony(const plf::limits capacities) PLF_COLONY_NOEXCEPT:
1066 element_allocator_type(element_allocator_type()),
1067 groups_with_erasures_list_head(NULL),
1068 total_number_of_elements(0),
1069 total_capacity(0),
1070 pointer_allocator_pair(static_cast<skipfield_type>(capacities.min)),
1071 group_allocator_pair(static_cast<skipfield_type>(capacities.max))
1072 {
1073 assert(std::numeric_limits<skipfield_type>::is_integer & !std::numeric_limits<skipfield_type>::is_signed);
1074 assert((capacities.min > 2) & (capacities.min <= capacities.max));
1075 assert(capacities.max <= std::numeric_limits<skipfield_type>::max());
1076
1077 #ifndef PLF_COLONY_ALIGNMENT_SUPPORT
1078 assert(sizeof(element_type) >= sizeof(skipfield_type) * 2);
1079 #endif
1080 }
1081
1082
1083
1084 // Default constuctor (allocator-extended):
1085
1086 explicit colony(const element_allocator_type &alloc):
1087 element_allocator_type(alloc),
1088 groups_with_erasures_list_head(NULL),
1089 total_number_of_elements(0),
1090 total_capacity(0),
1091 pointer_allocator_pair(PLF_COLONY_MIN_BLOCK_CAPACITY),
1092 group_allocator_pair(std::numeric_limits<skipfield_type>::max())
1093 {
1094 assert(std::numeric_limits<skipfield_type>::is_integer & !std::numeric_limits<skipfield_type>::is_signed);
1095
1096 #ifndef PLF_COLONY_ALIGNMENT_SUPPORT
1097 assert(sizeof(element_type) >= sizeof(skipfield_type) * 2);
1098 #endif
1099 }
1100
1101
1102
1103 explicit colony(const plf::limits capacities, const element_allocator_type &alloc):
1104 element_allocator_type(alloc),
1105 groups_with_erasures_list_head(NULL),
1106 total_number_of_elements(0),
1107 total_capacity(0),
1108 pointer_allocator_pair(static_cast<skipfield_type>(capacities.min)),
1109 group_allocator_pair(static_cast<skipfield_type>(capacities.max))
1110 {
1111 assert(std::numeric_limits<skipfield_type>::is_integer & !std::numeric_limits<skipfield_type>::is_signed);
1112 assert((capacities.min > 2) & (capacities.min <= capacities.max));
1113 assert(capacities.max <= std::numeric_limits<skipfield_type>::max());
1114
1115 #ifndef PLF_COLONY_ALIGNMENT_SUPPORT
1116 assert(sizeof(element_type) >= sizeof(skipfield_type) * 2);
1117 #endif
1118 }
1119
1120
1121
1122 // Copy constructor:
1123
1124 colony(const colony &source):
1125 element_allocator_type(source),
1126 groups_with_erasures_list_head(NULL),
1127 total_number_of_elements(0),
1128 total_capacity(0),
1129 pointer_allocator_pair(static_cast<skipfield_type>((source.pointer_allocator_pair.min_elements_per_group > source.total_number_of_elements) ? source.pointer_allocator_pair.min_elements_per_group : ((source.total_number_of_elements > source.group_allocator_pair.max_elements_per_group) ? source.group_allocator_pair.max_elements_per_group : source.total_number_of_elements))), // min group size is set to value closest to total number of elements in source colony in order to not create unnecessary small groups in the range-insert below, then reverts to the original min group size afterwards. This effectively saves a call to reserve.
1130 group_allocator_pair(source.group_allocator_pair.max_elements_per_group)
1131 {
1132 insert(source.begin_iterator, source.end_iterator);
1133 pointer_allocator_pair.min_elements_per_group = source.pointer_allocator_pair.min_elements_per_group; // reset to correct value for future clear() or erasures
1134 }
1135
1136
1137
1138 // Copy constructor (allocator-extended):
1139
1140 colony(const colony &source, const allocator_type &alloc):
1141 element_allocator_type(alloc),
1142 groups_with_erasures_list_head(NULL),
1143 total_number_of_elements(0),
1144 total_capacity(0),
1145 pointer_allocator_pair(static_cast<skipfield_type>((source.pointer_allocator_pair.min_elements_per_group > source.total_number_of_elements) ? source.pointer_allocator_pair.min_elements_per_group : ((source.total_number_of_elements > source.group_allocator_pair.max_elements_per_group) ? source.group_allocator_pair.max_elements_per_group : source.total_number_of_elements))),
1146 group_allocator_pair(source.group_allocator_pair.max_elements_per_group)
1147 {
1148 insert(source.begin_iterator, source.end_iterator);
1149 pointer_allocator_pair.min_elements_per_group = source.pointer_allocator_pair.min_elements_per_group;
1150 }
1151
1152
1153
1154
1155private:
1156
1157 inline void blank() PLF_COLONY_NOEXCEPT
1158 {
1159 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1160 if PLF_COLONY_CONSTEXPR (std::is_trivial<group_pointer_type>::value && std::is_trivial<aligned_pointer_type>::value && std::is_trivial<skipfield_pointer_type>::value) // if all pointer types are trivial, we can just nuke it from orbit with memset (NULL is always 0 in C++):
1161 {
1162 std::memset(static_cast<void *>(this), 0, offsetof(colony, pointer_allocator_pair));
1163 }
1164 else
1165 #endif
1166 {
1167 end_iterator.group_pointer = NULL;
1168 end_iterator.element_pointer = NULL;
1169 end_iterator.skipfield_pointer = NULL;
1170 begin_iterator.group_pointer = NULL;
1171 begin_iterator.element_pointer = NULL;
1172 begin_iterator.skipfield_pointer = NULL;
1173 groups_with_erasures_list_head = NULL;
1174 total_number_of_elements = 0;
1175 total_capacity = 0;
1176 }
1177 }
1178
1179
1180
1181public:
1182
1183
1184
1185 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
1186 // Move constructor:
1187
1188 colony(colony &&source) PLF_COLONY_NOEXCEPT:
1189 element_allocator_type(source),
1190 end_iterator(std::move(source.end_iterator)),
1191 begin_iterator(std::move(source.begin_iterator)),
1192 groups_with_erasures_list_head(std::move(source.groups_with_erasures_list_head)),
1193 total_number_of_elements(source.total_number_of_elements),
1194 total_capacity(source.total_capacity),
1195 pointer_allocator_pair(source.pointer_allocator_pair.min_elements_per_group),
1196 group_allocator_pair(source.group_allocator_pair.max_elements_per_group)
1197 {
1198 assert (&source != this);
1199 source.blank();
1200 }
1201
1202
1203 // Move constructor (allocator-extended):
1204
1205 colony(colony &&source, const allocator_type &alloc):
1206 element_allocator_type(alloc),
1207 end_iterator(std::move(source.end_iterator)),
1208 begin_iterator(std::move(source.begin_iterator)),
1209 groups_with_erasures_list_head(std::move(source.groups_with_erasures_list_head)),
1210 total_number_of_elements(source.total_number_of_elements),
1211 total_capacity(source.total_capacity),
1212 pointer_allocator_pair(source.pointer_allocator_pair.min_elements_per_group),
1213 group_allocator_pair(source.group_allocator_pair.max_elements_per_group)
1214 {
1215 assert (&source != this);
1216 source.blank();
1217 }
1218 #endif
1219
1220
1221
1222 // Fill constructor:
1223
1224 colony(const size_type fill_number, const element_type &element, const plf::limits capacities = plf::limits(PLF_COLONY_MIN_BLOCK_CAPACITY, std::numeric_limits<skipfield_type>::max()), const element_allocator_type &alloc = element_allocator_type()):
1225 element_allocator_type(alloc),
1226 groups_with_erasures_list_head(NULL),
1227 total_number_of_elements(0),
1228 total_capacity(0),
1229 pointer_allocator_pair(static_cast<skipfield_type>(capacities.min)),
1230 group_allocator_pair(static_cast<skipfield_type>(capacities.max))
1231 {
1232 assert(std::numeric_limits<skipfield_type>::is_integer & !std::numeric_limits<skipfield_type>::is_signed);
1233 assert((capacities.min > 2) & (capacities.min <= capacities.max));
1234 assert(capacities.max <= std::numeric_limits<skipfield_type>::max());
1235
1236 #ifndef PLF_COLONY_ALIGNMENT_SUPPORT
1237 assert(sizeof(element_type) >= sizeof(skipfield_type) * 2); // see default constructor explanation
1238 #endif
1239
1240 insert(fill_number, element);
1241 }
1242
1243
1244
1245 // Range constructor:
1246
1247 template<typename iterator_type>
1248 colony(const typename plf_enable_if_c<!std::numeric_limits<iterator_type>::is_integer, iterator_type>::type &first, const iterator_type &last, const plf::limits capacities = plf::limits(PLF_COLONY_MIN_BLOCK_CAPACITY, std::numeric_limits<skipfield_type>::max()), const element_allocator_type &alloc = element_allocator_type()):
1249 element_allocator_type(alloc),
1250 groups_with_erasures_list_head(NULL),
1251 total_number_of_elements(0),
1252 total_capacity(0),
1253 pointer_allocator_pair(static_cast<skipfield_type>(capacities.min)),
1254 group_allocator_pair(static_cast<skipfield_type>(capacities.max))
1255 {
1256 assert(std::numeric_limits<skipfield_type>::is_integer & !std::numeric_limits<skipfield_type>::is_signed);
1257 assert((capacities.min > 2) & (capacities.min <= capacities.max));
1258 assert(capacities.max <= std::numeric_limits<skipfield_type>::max());
1259
1260 #ifndef PLF_COLONY_ALIGNMENT_SUPPORT
1261 assert(sizeof(element_type) >= sizeof(skipfield_type) * 2); // see default constructor explanation
1262 #endif
1263
1264 insert<iterator_type>(first, last);
1265 }
1266
1267
1268
1269 // Initializer-list constructor:
1270
1271 #ifdef PLF_COLONY_INITIALIZER_LIST_SUPPORT
1272 colony(const std::initializer_list<element_type> &element_list, const plf::limits capacities = plf::limits(PLF_COLONY_MIN_BLOCK_CAPACITY, std::numeric_limits<skipfield_type>::max()), const element_allocator_type &alloc = element_allocator_type()):
1273 element_allocator_type(alloc),
1274 groups_with_erasures_list_head(NULL),
1275 total_number_of_elements(0),
1276 total_capacity(0),
1277 pointer_allocator_pair(static_cast<skipfield_type>(capacities.min)),
1278 group_allocator_pair(static_cast<skipfield_type>(capacities.max))
1279 {
1280 assert(std::numeric_limits<skipfield_type>::is_integer & !std::numeric_limits<skipfield_type>::is_signed);
1281 assert((capacities.min > 2) & (capacities.min <= capacities.max));
1282 assert(capacities.max <= std::numeric_limits<skipfield_type>::max());
1283
1284 #ifndef PLF_COLONY_ALIGNMENT_SUPPORT
1285 assert(sizeof(element_type) >= sizeof(skipfield_type) * 2); // see default constructor explanation
1286 #endif
1287
1288 insert(element_list);
1289 }
1290
1291 #endif
1292
1293
1294
1295 inline PLF_COLONY_FORCE_INLINE iterator begin() PLF_COLONY_NOEXCEPT
1296 {
1297 return begin_iterator;
1298 }
1299
1300
1301
1302 inline PLF_COLONY_FORCE_INLINE const_iterator begin() const PLF_COLONY_NOEXCEPT // To allow for functions which only take const colony & as a source eg. copy constructor
1303 {
1304 return begin_iterator;
1305 }
1306
1307
1308
1309 inline PLF_COLONY_FORCE_INLINE iterator end() PLF_COLONY_NOEXCEPT
1310 {
1311 return end_iterator;
1312 }
1313
1314
1315
1316 inline PLF_COLONY_FORCE_INLINE const_iterator end() const PLF_COLONY_NOEXCEPT
1317 {
1318 return end_iterator;
1319 }
1320
1321
1322
1323 inline PLF_COLONY_FORCE_INLINE const_iterator cbegin() const PLF_COLONY_NOEXCEPT
1324 {
1325 return begin_iterator;
1326 }
1327
1328
1329
1330 inline PLF_COLONY_FORCE_INLINE const_iterator cend() const PLF_COLONY_NOEXCEPT
1331 {
1332 return end_iterator;
1333 }
1334
1335
1336
1337 inline reverse_iterator rbegin() const PLF_COLONY_NOEXCEPT
1338 {
1339 return (end_iterator.group_pointer != NULL) ? ++reverse_iterator(end_iterator) : reverse_iterator(begin_iterator.group_pointer, begin_iterator.element_pointer - 1, begin_iterator.skipfield_pointer - 1);
1340 }
1341
1342
1343
1344 inline reverse_iterator rend() const PLF_COLONY_NOEXCEPT
1345 {
1346 return reverse_iterator(begin_iterator.group_pointer, begin_iterator.element_pointer - 1, begin_iterator.skipfield_pointer - 1);
1347 }
1348
1349
1350
1351 inline const_reverse_iterator crbegin() const PLF_COLONY_NOEXCEPT
1352 {
1353 return (end_iterator.group_pointer != NULL) ? ++const_reverse_iterator(end_iterator) : const_reverse_iterator(begin_iterator.group_pointer, begin_iterator.element_pointer - 1, begin_iterator.skipfield_pointer - 1);
1354 }
1355
1356
1357
1358 inline const_reverse_iterator crend() const PLF_COLONY_NOEXCEPT
1359 {
1360 return const_reverse_iterator(begin_iterator.group_pointer, begin_iterator.element_pointer - 1, begin_iterator.skipfield_pointer - 1);
1361 }
1362
1363
1364
1365 ~colony() PLF_COLONY_NOEXCEPT
1366 {
1367 destroy_all_data();
1368 }
1369
1370
1371
1372private:
1373
1374 void destroy_all_data() PLF_COLONY_NOEXCEPT
1375 {
1376 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1377 if PLF_COLONY_CONSTEXPR (!(std::is_trivially_destructible<element_type>::value))
1378 #endif // If compiler doesn't support traits, iterate regardless - trivial destructors will not be called, hopefully compiler will optimise the 'destruct' loop out for POD types
1379 {
1380 if (total_number_of_elements != 0)
1381 {
1382 total_number_of_elements = 0; // to avoid double-destruction
1383
1384 while (true)
1385 {
1386 const aligned_pointer_type end_pointer = begin_iterator.group_pointer->last_endpoint;
1387
1388 do
1389 {
1390 PLF_COLONY_DESTROY(element_allocator_type, (*this), reinterpret_cast<pointer>(begin_iterator.element_pointer));
1391 ++begin_iterator.skipfield_pointer;
1392 begin_iterator.element_pointer += static_cast<size_type>(*begin_iterator.skipfield_pointer) + 1u;
1393 begin_iterator.skipfield_pointer += static_cast<size_type>(*begin_iterator.skipfield_pointer);
1394 } while(begin_iterator.element_pointer != end_pointer); // ie. beyond end of available data
1395
1396 const group_pointer_type next_group = begin_iterator.group_pointer->next_group;
1397 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer);
1398 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, 1);
1399 begin_iterator.group_pointer = next_group; // required to be before if statement in order for first_group to be NULL and avoid potential double-destruction in future
1400
1401 if (next_group == NULL)
1402 {
1403 return;
1404 }
1405
1406 begin_iterator.element_pointer = next_group->elements + *(next_group->skipfield);
1407 begin_iterator.skipfield_pointer = next_group->skipfield + *(next_group->skipfield);
1408 }
1409 }
1410 }
1411
1412 // If either of the if statements above were false, this point will never be reached
1413 // Technically under a type-traits-supporting compiler total_number_of_elements could be non-zero at this point, but since begin_iterator.group_pointer would already be NULL in the case of double-destruction, it's unnecessary to zero total_number_of_elements
1414 while (begin_iterator.group_pointer != NULL)
1415 {
1416 const group_pointer_type next_group = begin_iterator.group_pointer->next_group;
1417 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer);
1418 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, 1);
1419 begin_iterator.group_pointer = next_group;
1420 }
1421 }
1422
1423
1424
1425 void initialize(const skipfield_type first_group_size)
1426 {
1427 begin_iterator.group_pointer = PLF_COLONY_ALLOCATE(group_allocator_type, group_allocator_pair, 1, 0);
1428
1429 try
1430 {
1431 #ifdef PLF_COLONY_VARIADICS_SUPPORT
1432 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, first_group_size);
1433 #else
1434 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, group(first_group_size));
1435 #endif
1436 }
1437 catch (...)
1438 {
1439 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, 1);
1440 begin_iterator.group_pointer = NULL;
1441 throw;
1442 }
1443
1444 end_iterator.group_pointer = begin_iterator.group_pointer;
1445 end_iterator.element_pointer = begin_iterator.element_pointer = begin_iterator.group_pointer->elements;
1446 end_iterator.skipfield_pointer = begin_iterator.skipfield_pointer = begin_iterator.group_pointer->skipfield;
1447 total_capacity = first_group_size;
1448 }
1449
1450
1451
1452 void update_skipblock(const iterator &new_location, const skipfield_type prev_free_list_index)
1453 {
1454 const skipfield_type new_value = static_cast<skipfield_type>(*(new_location.skipfield_pointer) - 1);
1455
1456 if (new_value != 0) // ie. skipfield was not 1, ie. a single-node skipblock, with no additional nodes to update
1457 {
1458 // set (new) start and (original) end of skipblock to new value:
1459 *(new_location.skipfield_pointer + new_value) = *(new_location.skipfield_pointer + 1) = new_value;
1460
1461 // transfer free list node to new start node:
1462 ++(groups_with_erasures_list_head->free_list_head);
1463
1464 if (prev_free_list_index != std::numeric_limits<skipfield_type>::max()) // ie. not the tail free list node
1465 {
1466 *(reinterpret_cast<skipfield_pointer_type>(new_location.group_pointer->elements + prev_free_list_index) + 1) = groups_with_erasures_list_head->free_list_head;
1467 }
1468
1469 *(reinterpret_cast<skipfield_pointer_type>(new_location.element_pointer + 1)) = prev_free_list_index;
1470 *(reinterpret_cast<skipfield_pointer_type>(new_location.element_pointer + 1) + 1) = std::numeric_limits<skipfield_type>::max();
1471 }
1472 else
1473 {
1474 groups_with_erasures_list_head->free_list_head = prev_free_list_index;
1475
1476 if (prev_free_list_index != std::numeric_limits<skipfield_type>::max()) // ie. not the last free list node
1477 {
1478 *(reinterpret_cast<skipfield_pointer_type>(new_location.group_pointer->elements + prev_free_list_index) + 1) = std::numeric_limits<skipfield_type>::max();
1479 }
1480 else
1481 {
1482 groups_with_erasures_list_head = groups_with_erasures_list_head->erasures_list_next_group;
1483 }
1484 }
1485
1486 *(new_location.skipfield_pointer) = 0;
1487 ++(new_location.group_pointer->number_of_elements);
1488
1489 if (new_location.group_pointer == begin_iterator.group_pointer && new_location.element_pointer < begin_iterator.element_pointer)
1490 { /* ie. begin_iterator was moved forwards as the result of an erasure at some point, this erased element is before the current begin, hence, set current begin iterator to this element */
1491 begin_iterator = new_location;
1492 }
1493
1494 ++total_number_of_elements;
1495 }
1496
1497
1498
1499public:
1500
1501
1502 iterator insert(const element_type &element)
1503 {
1504 if (end_iterator.element_pointer != NULL)
1505 {
1506 switch(((groups_with_erasures_list_head != NULL) << 1) | (end_iterator.element_pointer == reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield)))
1507 {
1508 case 0: // ie. there are no erased elements and end_iterator is not at end of current final group
1509 {
1510 const iterator return_iterator = end_iterator; /* Make copy for return before modifying end_iterator */
1511
1512 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1513 if PLF_COLONY_CONSTEXPR (std::is_nothrow_copy_constructible<element_type>::value)
1514 { // For no good reason this compiles to ridiculously faster code under GCC 5-9 in raw small struct tests with large N:
1515 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), element);
1516 end_iterator.group_pointer->last_endpoint = end_iterator.element_pointer;
1517 }
1518 else
1519 #endif
1520 {
1521 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer), element);
1522 end_iterator.group_pointer->last_endpoint = ++end_iterator.element_pointer; // Shift the addition to the second operation, avoiding a try-catch block if an exception is thrown during construction
1523 }
1524
1525 ++(end_iterator.group_pointer->number_of_elements);
1526 ++end_iterator.skipfield_pointer;
1527 ++total_number_of_elements;
1528
1529 return return_iterator; // return value before incrementation
1530 }
1531 case 1: // ie. there are no erased elements and end_iterator is at end of current final group - ie. colony is full - create new group
1532 {
1533 end_iterator.group_pointer->next_group = PLF_COLONY_ALLOCATE(group_allocator_type, group_allocator_pair, 1, end_iterator.group_pointer);
1534 group &next_group = *(end_iterator.group_pointer->next_group);
1535 const skipfield_type new_group_size = (total_number_of_elements < static_cast<size_type>(group_allocator_pair.max_elements_per_group)) ? static_cast<skipfield_type>(total_number_of_elements) : group_allocator_pair.max_elements_per_group;
1536
1537 try
1538 {
1539 #ifdef PLF_COLONY_VARIADICS_SUPPORT
1540 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, &next_group, new_group_size, end_iterator.group_pointer);
1541 #else
1542 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, &next_group, group(new_group_size, end_iterator.group_pointer));
1543 #endif
1544 }
1545 catch (...)
1546 {
1547 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, &next_group, 1);
1548 end_iterator.group_pointer->next_group = NULL;
1549 throw;
1550 }
1551
1552 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1553 if PLF_COLONY_CONSTEXPR (std::is_nothrow_copy_constructible<element_type>::value)
1554 {
1555 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(next_group.elements), element);
1556 }
1557 else
1558 #endif
1559 {
1560 try
1561 {
1562 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(next_group.elements), element);
1563 }
1564 catch (...)
1565 {
1566 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, &next_group);
1567 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, &next_group, 1);
1568 end_iterator.group_pointer->next_group = NULL;
1569 throw;
1570 }
1571 }
1572
1573 end_iterator.group_pointer = &next_group;
1574 end_iterator.element_pointer = next_group.last_endpoint;
1575 end_iterator.skipfield_pointer = next_group.skipfield + 1;
1576 ++total_number_of_elements;
1577 total_capacity += new_group_size;
1578
1579 return iterator(end_iterator.group_pointer, next_group.elements, next_group.skipfield); /* returns value before incrementation */
1580 }
1581 default: // ie. there are erased elements, reuse previous-erased element locations
1582 {
1583 iterator new_location(groups_with_erasures_list_head, groups_with_erasures_list_head->elements + groups_with_erasures_list_head->free_list_head, groups_with_erasures_list_head->skipfield + groups_with_erasures_list_head->free_list_head);
1584
1585 // We always reuse the element at the start of the skipblock, this is also where the free-list information for that skipblock is stored. Get the previous free-list node's index from this memory space, before we write to our element to it. 'Next' index is always the free_list_head (as represented by the maximum value of the skipfield type) here so we don't need to get it:
1586 const skipfield_type prev_free_list_index = *(reinterpret_cast<skipfield_pointer_type>(new_location.element_pointer));
1587 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(new_location.element_pointer), element);
1588
1589 update_skipblock(new_location, prev_free_list_index);
1590
1591 return new_location;
1592 }
1593 }
1594 }
1595 else // ie. newly-constructed colony, no insertions yet and no groups
1596 {
1597 initialize(pointer_allocator_pair.min_elements_per_group);
1598
1599 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1600 if PLF_COLONY_CONSTEXPR (std::is_nothrow_copy_constructible<element_type>::value)
1601 {
1602 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), element);
1603 }
1604 else
1605 #endif
1606 {
1607 try
1608 {
1609 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), element);
1610 }
1611 catch (...)
1612 {
1613 clear();
1614 throw;
1615 }
1616 }
1617
1618 ++end_iterator.skipfield_pointer;
1619 total_number_of_elements = 1;
1620 return begin_iterator;
1621 }
1622 }
1623
1624
1625
1626 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
1627 iterator insert(element_type &&element) // The move-insert function is near-identical to the regular insert function, with the exception of the element construction method and is_nothrow tests.
1628 {
1629 if (end_iterator.element_pointer != NULL)
1630 {
1631 switch(((groups_with_erasures_list_head != NULL) << 1) | (end_iterator.element_pointer == reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield)))
1632 {
1633 case 0:
1634 {
1635 const iterator return_iterator = end_iterator;
1636
1637 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1638 if PLF_COLONY_CONSTEXPR (std::is_nothrow_move_constructible<element_type>::value)
1639 {
1640 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), std::move(element));
1641 end_iterator.group_pointer->last_endpoint = end_iterator.element_pointer;
1642 }
1643 else
1644 #endif
1645 {
1646 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer), std::move(element));
1647 end_iterator.group_pointer->last_endpoint = ++end_iterator.element_pointer;
1648 }
1649
1650 ++(end_iterator.group_pointer->number_of_elements);
1651 ++end_iterator.skipfield_pointer;
1652 ++total_number_of_elements;
1653
1654 return return_iterator;
1655 }
1656 case 1:
1657 {
1658 end_iterator.group_pointer->next_group = PLF_COLONY_ALLOCATE(group_allocator_type, group_allocator_pair, 1, end_iterator.group_pointer);
1659 group &next_group = *(end_iterator.group_pointer->next_group);
1660 const skipfield_type new_group_size = (total_number_of_elements < static_cast<size_type>(group_allocator_pair.max_elements_per_group)) ? static_cast<skipfield_type>(total_number_of_elements) : group_allocator_pair.max_elements_per_group;
1661
1662 try
1663 {
1664 #ifdef PLF_COLONY_VARIADICS_SUPPORT
1665 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, &next_group, new_group_size, end_iterator.group_pointer);
1666 #else
1667 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, &next_group, group(new_group_size, end_iterator.group_pointer));
1668 #endif
1669 }
1670 catch (...)
1671 {
1672 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, &next_group, 1);
1673 end_iterator.group_pointer->next_group = NULL;
1674 throw;
1675 }
1676
1677 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1678 if PLF_COLONY_CONSTEXPR (std::is_nothrow_move_constructible<element_type>::value)
1679 {
1680 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(next_group.elements), std::move(element));
1681 }
1682 else
1683 #endif
1684 {
1685 try
1686 {
1687 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(next_group.elements), std::move(element));
1688 }
1689 catch (...)
1690 {
1691 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, &next_group);
1692 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, &next_group, 1);
1693 end_iterator.group_pointer->next_group = NULL;
1694 throw;
1695 }
1696 }
1697
1698 end_iterator.group_pointer = &next_group;
1699 end_iterator.element_pointer = next_group.last_endpoint;
1700 end_iterator.skipfield_pointer = next_group.skipfield + 1;
1701 ++total_number_of_elements;
1702 total_capacity += new_group_size;
1703
1704 return iterator(end_iterator.group_pointer, next_group.elements, next_group.skipfield);
1705 }
1706 default:
1707 {
1708 iterator new_location(groups_with_erasures_list_head, groups_with_erasures_list_head->elements + groups_with_erasures_list_head->free_list_head, groups_with_erasures_list_head->skipfield + groups_with_erasures_list_head->free_list_head);
1709
1710 const skipfield_type prev_free_list_index = *(reinterpret_cast<skipfield_pointer_type>(new_location.element_pointer));
1711 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(new_location.element_pointer), std::move(element));
1712
1713 update_skipblock(new_location, prev_free_list_index);
1714
1715 return new_location;
1716 }
1717 }
1718 }
1719 else
1720 {
1721 initialize(pointer_allocator_pair.min_elements_per_group);
1722
1723 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1724 if PLF_COLONY_CONSTEXPR (std::is_nothrow_move_constructible<element_type>::value)
1725 {
1726 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), std::move(element));
1727 }
1728 else
1729 #endif
1730 {
1731 try
1732 {
1733 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), std::move(element));
1734 }
1735 catch (...)
1736 {
1737 clear();
1738 throw;
1739 }
1740 }
1741
1742 ++end_iterator.skipfield_pointer;
1743 total_number_of_elements = 1;
1744 return begin_iterator;
1745 }
1746 }
1747 #endif
1748
1749
1750
1751
1752 #ifdef PLF_COLONY_VARIADICS_SUPPORT
1753 template<typename... arguments>
1754 iterator emplace(arguments &&... parameters) // The emplace function is near-identical to the regular insert function, with the exception of the element construction method, removal of internal VARIADICS support checks, and change to is_nothrow tests.
1755 {
1756 if (end_iterator.element_pointer != NULL)
1757 {
1758 switch(((groups_with_erasures_list_head != NULL) << 1) | (end_iterator.element_pointer == reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield)))
1759 {
1760 case 0:
1761 {
1762 const iterator return_iterator = end_iterator;
1763
1764 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1765 if PLF_COLONY_CONSTEXPR (std::is_nothrow_constructible<element_type, arguments ...>::value)
1766 {
1767 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), std::forward<arguments>(parameters)...);
1768 end_iterator.group_pointer->last_endpoint = end_iterator.element_pointer;
1769 }
1770 else
1771 #endif
1772 {
1773 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer), std::forward<arguments>(parameters)...);
1774 end_iterator.group_pointer->last_endpoint = ++end_iterator.element_pointer;
1775 }
1776
1777 ++(end_iterator.group_pointer->number_of_elements);
1778 ++end_iterator.skipfield_pointer;
1779 ++total_number_of_elements;
1780
1781 return return_iterator;
1782 }
1783 case 1:
1784 {
1785 end_iterator.group_pointer->next_group = PLF_COLONY_ALLOCATE(group_allocator_type, group_allocator_pair, 1, end_iterator.group_pointer);
1786 group &next_group = *(end_iterator.group_pointer->next_group);
1787 const skipfield_type new_group_size = (total_number_of_elements < static_cast<size_type>(group_allocator_pair.max_elements_per_group)) ? static_cast<skipfield_type>(total_number_of_elements) : group_allocator_pair.max_elements_per_group;
1788
1789 try
1790 {
1791 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, &next_group, new_group_size, end_iterator.group_pointer);
1792 }
1793 catch (...)
1794 {
1795 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, &next_group, 1);
1796 end_iterator.group_pointer->next_group = NULL;
1797 throw;
1798 }
1799
1800 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1801 if PLF_COLONY_CONSTEXPR (std::is_nothrow_constructible<element_type, arguments ...>::value)
1802 {
1803 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(next_group.elements), std::forward<arguments>(parameters)...);
1804 }
1805 else
1806 #endif
1807 {
1808 try
1809 {
1810 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(next_group.elements), std::forward<arguments>(parameters)...);
1811 }
1812 catch (...)
1813 {
1814 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, &next_group);
1815 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, &next_group, 1);
1816 end_iterator.group_pointer->next_group = NULL;
1817 throw;
1818 }
1819 }
1820
1821 end_iterator.group_pointer = &next_group;
1822 end_iterator.element_pointer = next_group.last_endpoint;
1823 end_iterator.skipfield_pointer = next_group.skipfield + 1;
1824 ++total_number_of_elements;
1825 total_capacity += new_group_size;
1826
1827 return iterator(end_iterator.group_pointer, next_group.elements, next_group.skipfield);
1828 }
1829 default:
1830 {
1831 iterator new_location(groups_with_erasures_list_head, groups_with_erasures_list_head->elements + groups_with_erasures_list_head->free_list_head, groups_with_erasures_list_head->skipfield + groups_with_erasures_list_head->free_list_head);
1832
1833 const skipfield_type prev_free_list_index = *(reinterpret_cast<skipfield_pointer_type>(new_location.element_pointer));
1834 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(new_location.element_pointer), std::forward<arguments>(parameters) ...);
1835
1836 update_skipblock(new_location, prev_free_list_index);
1837
1838 return new_location;
1839 }
1840 }
1841 }
1842 else
1843 {
1844 initialize(pointer_allocator_pair.min_elements_per_group);
1845
1846 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1847 if PLF_COLONY_CONSTEXPR (std::is_nothrow_constructible<element_type, arguments ...>::value)
1848 {
1849 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), std::forward<arguments>(parameters) ...);
1850 }
1851 else
1852 #endif
1853 {
1854 try
1855 {
1856 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), std::forward<arguments>(parameters) ...);
1857 }
1858 catch (...)
1859 {
1860 clear();
1861 throw;
1862 }
1863 }
1864
1865 ++end_iterator.skipfield_pointer;
1866 total_number_of_elements = 1;
1867 return begin_iterator;
1868 }
1869 }
1870 #endif
1871
1872
1873
1874
1875private:
1876
1877 // Internal functions for fill insert:
1878
1879 void group_create(const skipfield_type number_of_elements)
1880 {
1881 const group_pointer_type next_group = end_iterator.group_pointer->next_group = PLF_COLONY_ALLOCATE(group_allocator_type, group_allocator_pair, 1, end_iterator.group_pointer);
1882
1883 try
1884 {
1885 #ifdef PLF_COLONY_VARIADICS_SUPPORT
1886 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, next_group, number_of_elements, end_iterator.group_pointer);
1887 #else
1888 PLF_COLONY_CONSTRUCT(group_allocator_type, group_allocator_pair, next_group, group(number_of_elements, end_iterator.group_pointer));
1889 #endif
1890 }
1891 catch (...)
1892 {
1893 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, next_group);
1894 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, next_group, 1);
1895 end_iterator.group_pointer->next_group = NULL;
1896 throw;
1897 }
1898
1899 end_iterator.group_pointer = next_group;
1900 end_iterator.element_pointer = next_group->elements;
1901 next_group->number_of_elements = 0; // group constructor sets this to 1 by default to allow for faster insertion during insertion/emplace in other cases
1902 total_capacity += number_of_elements;
1903 }
1904
1905
1906
1907 void group_fill(const element_type &element, const skipfield_type number_of_elements)
1908 {
1909 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1910 if PLF_COLONY_CONSTEXPR (std::is_trivially_copyable<element_type>::value && std::is_trivially_copy_constructible<element_type>::value && std::is_nothrow_copy_constructible<element_type>::value) // ie. we can get away with using the cheaper fill_n here if there is no chance of an exception being thrown:
1911 {
1912 #ifdef PLF_COLONY_ALIGNMENT_SUPPORT
1913 if PLF_COLONY_CONSTEXPR (sizeof(aligned_element_type) != sizeof(element_type))
1914 {
1915 alignas (alignof(aligned_element_type)) element_type aligned_copy = element; // to avoid potentially violating memory boundaries in line below, create an initial copy object of same (but aligned) type
1916 std::fill_n(end_iterator.element_pointer, number_of_elements, *(reinterpret_cast<aligned_pointer_type>(&aligned_copy)));
1917 }
1918 else
1919 #endif
1920 { // This also covers C++03, where the type cannot be aligned to anything so it's safe to use fill_n:
1921 std::fill_n(reinterpret_cast<pointer>(end_iterator.element_pointer), number_of_elements, element);
1922 }
1923
1924 end_iterator.element_pointer += number_of_elements;
1925 }
1926 else
1927 #endif
1928 {
1929 const aligned_pointer_type fill_end = end_iterator.element_pointer + number_of_elements;
1930
1931 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1932 if PLF_COLONY_CONSTEXPR (std::is_nothrow_copy_constructible<element_type>::value) // If nothrow_constructible, can remove the large block of 'catch' code below
1933 {
1934 do
1935 {
1936 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), element);
1937 } while (end_iterator.element_pointer != fill_end);
1938 }
1939 else
1940 #endif
1941 {
1942 do
1943 {
1944 try
1945 {
1946 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(end_iterator.element_pointer++), element);
1947 }
1948 catch (...)
1949 {
1950 end_iterator.group_pointer->last_endpoint = --end_iterator.element_pointer;
1951 const skipfield_type elements_constructed_before_exception = static_cast<skipfield_type>(end_iterator.element_pointer - end_iterator.group_pointer->elements);
1952 end_iterator.group_pointer->number_of_elements = elements_constructed_before_exception;
1953 end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + elements_constructed_before_exception;
1954 throw;
1955 }
1956 } while (end_iterator.element_pointer != fill_end);
1957 }
1958 }
1959
1960 end_iterator.group_pointer->last_endpoint = end_iterator.element_pointer;
1961 end_iterator.group_pointer->number_of_elements = static_cast<skipfield_type>(end_iterator.group_pointer->number_of_elements + number_of_elements);
1962 }
1963
1964
1965
1966 void fill_skipblock(const element_type &element, aligned_pointer_type const location, skipfield_pointer_type const skipfield_pointer, const skipfield_type number_of_elements)
1967 {
1968 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1969 if PLF_COLONY_CONSTEXPR (std::is_trivially_copyable<element_type>::value && std::is_trivially_copy_constructible<element_type>::value && std::is_nothrow_copy_constructible<element_type>::value) // ie. we can get away with using the cheaper fill_n here if there is no chance of an exception being thrown:
1970 {
1971 #ifdef PLF_COLONY_ALIGNMENT_SUPPORT
1972 if PLF_COLONY_CONSTEXPR (sizeof(aligned_element_type) != sizeof(element_type))
1973 {
1974 alignas (alignof(aligned_element_type)) element_type aligned_copy = element; // to avoid potentially violating memory boundaries in line below, create an initial copy object of same (but aligned) type
1975 std::fill_n(location, number_of_elements, *(reinterpret_cast<aligned_pointer_type>(&aligned_copy)));
1976 }
1977 else
1978 #endif
1979 { // This also covers C++03, where the type cannot be aligned to anything so it's safe to use fill_n:
1980 std::fill_n(reinterpret_cast<pointer>(location), number_of_elements, element);
1981 }
1982 }
1983 else
1984 #endif
1985 {
1986 const aligned_pointer_type fill_end = location + number_of_elements;
1987
1988 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
1989 if PLF_COLONY_CONSTEXPR (std::is_nothrow_copy_constructible<element_type>::value) // If nothrow_constructible, can remove the large block of 'catch' code below
1990 {
1991 for (aligned_pointer_type current_location = location; current_location != fill_end; ++current_location)
1992 {
1993 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(current_location), element);
1994 }
1995 }
1996 else
1997 #endif
1998 {
1999 const skipfield_type prev_free_list_node = *(reinterpret_cast<skipfield_pointer_type>(location)); // in case of exception, grabbing indexes before free_list node is reused
2000
2001 for (aligned_pointer_type current_location = location; current_location != fill_end; ++current_location)
2002 {
2003 try
2004 {
2005 PLF_COLONY_CONSTRUCT(element_allocator_type, (*this), reinterpret_cast<pointer>(current_location), element);
2006 }
2007 catch (...)
2008 {
2009 // Reconstruct existing skipblock and free-list indexes to reflect partially-reused skipblock:
2010 const skipfield_type elements_constructed_before_exception = static_cast<skipfield_type>((current_location - 1) - location);
2011 groups_with_erasures_list_head->number_of_elements = static_cast<skipfield_type>(groups_with_erasures_list_head->number_of_elements + elements_constructed_before_exception);
2012 total_number_of_elements += elements_constructed_before_exception;
2013
2014 std::memset(skipfield_pointer, 0, elements_constructed_before_exception * sizeof(skipfield_type));
2015
2016 *(reinterpret_cast<skipfield_pointer_type>(location + elements_constructed_before_exception)) = prev_free_list_node;
2017 *(reinterpret_cast<skipfield_pointer_type>(location + elements_constructed_before_exception) + 1) = std::numeric_limits<skipfield_type>::max();
2018
2019 const skipfield_type new_skipblock_head_index = static_cast<skipfield_type>(static_cast<skipfield_type>(location - groups_with_erasures_list_head->elements) + elements_constructed_before_exception);
2020 groups_with_erasures_list_head->free_list_head = new_skipblock_head_index;
2021
2022 if (prev_free_list_node != std::numeric_limits<skipfield_type>::max())
2023 {
2024 *(reinterpret_cast<skipfield_pointer_type>(groups_with_erasures_list_head->elements + prev_free_list_node) + 1) = new_skipblock_head_index;
2025 }
2026
2027 throw;
2028 }
2029 }
2030 }
2031 }
2032
2033 std::memset(skipfield_pointer, 0, number_of_elements * sizeof(skipfield_type)); // reset skipfield nodes within skipblock to 0
2034 groups_with_erasures_list_head->number_of_elements = static_cast<skipfield_type>(groups_with_erasures_list_head->number_of_elements + number_of_elements);
2035 total_number_of_elements += number_of_elements;
2036 }
2037
2038
2039
2040public:
2041
2042 // Fill insert
2043
2044 void insert(size_type number_of_elements, const element_type &element)
2045 {
2046 if (number_of_elements == 0)
2047 {
2048 return;
2049 }
2050 else if (number_of_elements == 1)
2051 {
2052 insert(element);
2053 return;
2054 }
2055
2056 if (begin_iterator.group_pointer == NULL) // Empty colony, no groups created yet
2057 {
2058 initialize((number_of_elements > group_allocator_pair.max_elements_per_group) ? group_allocator_pair.max_elements_per_group : (number_of_elements < pointer_allocator_pair.min_elements_per_group) ? pointer_allocator_pair.min_elements_per_group : static_cast<skipfield_type>(number_of_elements)); // Construct first group
2059 begin_iterator.group_pointer->number_of_elements = 0;
2060 }
2061
2062 if (total_number_of_elements != 0) // ie. not an uninitialized colony nor a situation where reserve has been called
2063 {
2064 // Use up erased locations if available:
2065 if (groups_with_erasures_list_head != NULL)
2066 {
2067 do // skipblock loop: breaks when group is exhausted of reusable skipblocks, or returns if number_of_elements == 0
2068 {
2069 aligned_pointer_type const element_pointer = groups_with_erasures_list_head->elements + groups_with_erasures_list_head->free_list_head;
2070 skipfield_pointer_type const skipfield_pointer = groups_with_erasures_list_head->skipfield + groups_with_erasures_list_head->free_list_head;
2071 const skipfield_type skipblock_size = *skipfield_pointer;
2072
2073 if (groups_with_erasures_list_head == begin_iterator.group_pointer && element_pointer < begin_iterator.element_pointer)
2074 {
2075 begin_iterator.element_pointer = element_pointer;
2076 begin_iterator.skipfield_pointer = skipfield_pointer;
2077 }
2078
2079 if (skipblock_size <= number_of_elements)
2080 {
2081 groups_with_erasures_list_head->free_list_head = *(reinterpret_cast<skipfield_pointer_type>(element_pointer)); // set free list head to previous free list node
2082 fill_skipblock(element, element_pointer, skipfield_pointer, skipblock_size);
2083 number_of_elements -= skipblock_size;
2084
2085 if (groups_with_erasures_list_head->free_list_head != std::numeric_limits<skipfield_type>::max())
2086 {
2087 *(reinterpret_cast<skipfield_pointer_type>(groups_with_erasures_list_head->elements + groups_with_erasures_list_head->free_list_head) + 1) = std::numeric_limits<skipfield_type>::max(); // set 'next' index of new free list head to 'end' (numeric max)
2088 }
2089 else
2090 {
2091 groups_with_erasures_list_head = groups_with_erasures_list_head->erasures_list_next_group; // change groups
2092
2093 if (groups_with_erasures_list_head == NULL)
2094 {
2095 break;
2096 }
2097 }
2098 }
2099 else // skipblock is larger than remaining number of elements
2100 {
2101 const skipfield_type prev_index = *(reinterpret_cast<skipfield_pointer_type>(element_pointer)); // save before element location is overwritten
2102 fill_skipblock(element, element_pointer, skipfield_pointer, static_cast<skipfield_type>(number_of_elements));
2103 const skipfield_type new_skipblock_size = static_cast<skipfield_type>(skipblock_size - number_of_elements);
2104
2105 // Update skipfield (earlier nodes already memset'd in fill_skipblock function):
2106 *(skipfield_pointer + number_of_elements) = new_skipblock_size;
2107 *(skipfield_pointer + skipblock_size - 1) = new_skipblock_size;
2108 groups_with_erasures_list_head->free_list_head = static_cast<skipfield_type>(groups_with_erasures_list_head->free_list_head + number_of_elements); // set free list head to new start node
2109
2110 // Update free list with new head:
2111 *(reinterpret_cast<skipfield_pointer_type>(element_pointer + number_of_elements)) = prev_index;
2112 *(reinterpret_cast<skipfield_pointer_type>(element_pointer + number_of_elements) + 1) = std::numeric_limits<skipfield_type>::max();
2113
2114 if (prev_index != std::numeric_limits<skipfield_type>::max())
2115 {
2116 *(reinterpret_cast<skipfield_pointer_type>(groups_with_erasures_list_head->elements + prev_index) + 1) = groups_with_erasures_list_head->free_list_head; // set 'next' index of previous skipblock to new start of skipblock
2117 }
2118
2119 return;
2120 }
2121 } while(number_of_elements != 0);
2122 }
2123
2124
2125 // Use up remaining available element locations in end group:
2126 const skipfield_type group_remainder = (static_cast<skipfield_type>(reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield) - end_iterator.element_pointer) > number_of_elements) ? static_cast<skipfield_type>(number_of_elements) : static_cast<skipfield_type>(reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield) - end_iterator.element_pointer);
2127
2128 if (group_remainder != 0)
2129 {
2130 group_fill(element, group_remainder);
2131 total_number_of_elements += group_remainder;
2132 number_of_elements -= group_remainder;
2133 }
2134 }
2135 else if (end_iterator.group_pointer->capacity >= number_of_elements)
2136 {
2137 group_fill(element, static_cast<skipfield_type>(number_of_elements));
2138 end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + number_of_elements;
2139 total_number_of_elements = number_of_elements;
2140 return;
2141 }
2142 else
2143 {
2144 group_fill(element, end_iterator.group_pointer->capacity);
2145 total_number_of_elements = end_iterator.group_pointer->capacity;
2146 number_of_elements -= end_iterator.group_pointer->capacity;
2147 }
2148
2149
2150 // If there's some elements left that need to be created, create new groups and fill:
2151 if (number_of_elements > group_allocator_pair.max_elements_per_group)
2152 {
2153 size_type multiples = (number_of_elements / static_cast<size_type>(group_allocator_pair.max_elements_per_group));
2154 const skipfield_type element_remainder = static_cast<skipfield_type>(number_of_elements - (multiples * static_cast<size_type>(group_allocator_pair.max_elements_per_group)));
2155
2156 while (multiples-- != 0)
2157 {
2158 group_create(group_allocator_pair.max_elements_per_group);
2159 group_fill(element, group_allocator_pair.max_elements_per_group);
2160 }
2161
2162 if (element_remainder != 0)
2163 {
2164 group_create(group_allocator_pair.max_elements_per_group);
2165 group_fill(element, element_remainder);
2166 }
2167 }
2168 else if (number_of_elements != 0)
2169 {
2170 group_create(static_cast<skipfield_type>((number_of_elements > total_number_of_elements) ? number_of_elements : total_number_of_elements));
2171 group_fill(element, static_cast<skipfield_type>(number_of_elements));
2172 }
2173
2174 total_number_of_elements += number_of_elements; // Adds the remainder from the last if-block - the insert functions in the first if/else block will already have incremented total_number_of_elements
2175 end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + (end_iterator.element_pointer - end_iterator.group_pointer->elements);
2176 }
2177
2178
2179
2180 // Range insert
2181
2182 template <class iterator_type>
2183 #if defined(PLF_COLONY_TYPE_TRAITS_SUPPORT)
2184 inline void insert (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)
2185 #else
2186 inline void insert (typename plf_enable_if_c<!std::numeric_limits<iterator_type>::is_integer, iterator_type>::type first, const iterator_type last)
2187 #endif
2188 {
2189 while (first != last)
2190 {
2191 insert(*first++);
2192 }
2193 }
2194
2195
2196
2197 #if defined(PLF_COLONY_TYPE_TRAITS_SUPPORT)
2198 template <class iterator_type>
2199 inline void insert (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)
2200 {
2201 if (total_number_of_elements == 0) // Otherwise reserve could automatically consolidate existing elements to a larger group, reallocating them and breaking the 'no iterator invalidation on insert' promise
2202 {
2203 reserve(static_cast<size_type>(last - first));
2204 }
2205
2206 while (first != last)
2207 {
2208 insert(*first++);
2209 }
2210 }
2211 #endif
2212
2213
2214
2215 // Initializer-list insert
2216
2217 #ifdef PLF_COLONY_INITIALIZER_LIST_SUPPORT
2218 inline void insert (const std::initializer_list<element_type> &element_list)
2219 { // use range insert:
2220 insert(element_list.begin(), element_list.end());
2221 }
2222 #endif
2223
2224
2225
2226private:
2227
2228 inline PLF_COLONY_FORCE_INLINE void update_subsequent_group_numbers(group_pointer_type current_group) PLF_COLONY_NOEXCEPT
2229 {
2230 do
2231 {
2232 --(current_group->group_number);
2233 current_group = current_group->next_group;
2234 } while (current_group != NULL);
2235 }
2236
2237
2238
2239 inline PLF_COLONY_FORCE_INLINE void consolidate() // get all elements contiguous in memory and shrink to fit, remove erasures and erasure free lists. Invalidates all iterators and pointers to elements
2240 {
2241 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
2242 colony temp;
2243
2244 // Make first allocated group as large total number of elements, where possible:
2245 temp.pointer_allocator_pair.min_elements_per_group = static_cast<skipfield_type>((pointer_allocator_pair.min_elements_per_group > total_number_of_elements) ? pointer_allocator_pair.min_elements_per_group : ((total_number_of_elements > group_allocator_pair.max_elements_per_group) ? group_allocator_pair.max_elements_per_group : total_number_of_elements));
2246 temp.group_allocator_pair.max_elements_per_group = group_allocator_pair.max_elements_per_group;
2247
2248 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
2249 if PLF_COLONY_CONSTEXPR (std::is_move_assignable<element_type>::value && std::is_move_constructible<element_type>::value)
2250 {
2251 temp.insert(std::make_move_iterator(begin_iterator), std::make_move_iterator(end_iterator));
2252 }
2253 else
2254 #endif
2255 {
2256 temp.insert(begin_iterator, end_iterator);
2257 }
2258
2259 temp.pointer_allocator_pair.min_elements_per_group = pointer_allocator_pair.min_elements_per_group; // reset to correct value for future clear() or erasures
2260 *this = std::move(temp); // Avoid generating 2nd temporary
2261 #else
2262 colony temp(*this);
2263 swap(temp);
2264 #endif
2265 }
2266
2267
2268
2269 void remove_from_groups_with_erasures_list(const group_pointer_type group_to_remove) PLF_COLONY_NOEXCEPT
2270 {
2271 if (group_to_remove == groups_with_erasures_list_head)
2272 {
2273 groups_with_erasures_list_head = groups_with_erasures_list_head->erasures_list_next_group;
2274 return;
2275 }
2276
2277 group_pointer_type previous_group = groups_with_erasures_list_head, current_group = groups_with_erasures_list_head->erasures_list_next_group;
2278
2279 while (group_to_remove != current_group)
2280 {
2281 previous_group = current_group;
2282 current_group = current_group->erasures_list_next_group;
2283 }
2284
2285 previous_group->erasures_list_next_group = current_group->erasures_list_next_group;
2286 }
2287
2288
2289
2290public:
2291
2292 // must return iterator to subsequent non-erased element (or end()), in case the group containing the element which the iterator points to becomes empty after the erasure, and is thereafter removed from the colony chain, making the current iterator invalid and unusable in a ++ operation:
2293 iterator erase(const const_iterator &it) // if uninitialized/invalid iterator supplied, function could generate an exception
2294 {
2295 assert(!empty());
2296 const group_pointer_type group_pointer = it.group_pointer;
2297 assert(group_pointer != NULL); // ie. not uninitialized iterator
2298 assert(it.element_pointer != group_pointer->last_endpoint); // ie. != end()
2299 assert(*(it.skipfield_pointer) == 0); // ie. element pointed to by iterator has not been erased previously
2300
2301 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
2302 if PLF_COLONY_CONSTEXPR (!std::is_trivially_destructible<element_type>::value) // This if-statement should be removed by the compiler on resolution of element_type. For some optimizing compilers this step won't be necessary (for MSVC 2013 it makes a difference)
2303 #endif
2304 {
2305 PLF_COLONY_DESTROY(element_allocator_type, (*this), reinterpret_cast<pointer>(it.element_pointer)); // Destruct element
2306 }
2307
2308 --total_number_of_elements;
2309
2310 if (group_pointer->number_of_elements-- != 1) // ie. non-empty group at this point in time, don't consolidate - optimization note: GCC optimizes postfix + 1 comparison better than prefix + 1 comparison in many cases.
2311 {
2312 // Code logic for following section:
2313 // ---------------------------------
2314 // If current skipfield node has no skipped node on either side, continue as usual
2315 // If node only has skipped node on left, set current node and start node of the skipblock to left node value + 1.
2316 // If node only has skipped node on right, make this node the start node of the skipblock and update end node
2317 // If node has skipped nodes on left and right, set start node of left skipblock and end node of right skipblock to the values of the left + right nodes + 1
2318
2319 // Optimization explanation:
2320 // The contextual logic below is the same as that in the insert() functions but in this case the value of the current skipfield node will always be
2321 // zero (since it is not yet erased), meaning no additional manipulations are necessary for the previous skipfield node comparison - we only have to check against zero
2322 const char prev_skipfield = *(it.skipfield_pointer - (it.skipfield_pointer != group_pointer->skipfield)) != 0;
2323 const char after_skipfield = *(it.skipfield_pointer + 1) != 0; // NOTE: boundary test (checking against end-of-elements) is able to be skipped due to the extra skipfield node (compared to element field) - which is present to enable faster iterator operator ++ operations
2324 skipfield_type update_value = 1;
2325
2326 switch ((after_skipfield << 1) | prev_skipfield)
2327 {
2328 case 0: // no consecutive erased elements
2329 {
2330 *it.skipfield_pointer = 1; // solo skipped node
2331 const skipfield_type index = static_cast<skipfield_type>(it.element_pointer - group_pointer->elements);
2332
2333 if (group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max()) // ie. if this group already has some erased elements
2334 {
2335 *(reinterpret_cast<skipfield_pointer_type>(group_pointer->elements + group_pointer->free_list_head) + 1) = index; // set prev free list head's 'next index' number to the index of the current element
2336 }
2337 else
2338 {
2339 group_pointer->erasures_list_next_group = groups_with_erasures_list_head; // add it to the groups-with-erasures free list
2340 groups_with_erasures_list_head = group_pointer;
2341 }
2342
2343 *(reinterpret_cast<skipfield_pointer_type>(it.element_pointer)) = group_pointer->free_list_head;
2344 *(reinterpret_cast<skipfield_pointer_type>(it.element_pointer) + 1) = std::numeric_limits<skipfield_type>::max();
2345 group_pointer->free_list_head = index;
2346 break;
2347 }
2348 case 1: // previous erased consecutive elements, none following
2349 {
2350 *(it.skipfield_pointer - *(it.skipfield_pointer - 1)) = *it.skipfield_pointer = static_cast<skipfield_type>(*(it.skipfield_pointer - 1) + 1);
2351 break;
2352 }
2353 case 2: // following erased consecutive elements, none preceding
2354 {
2355 const skipfield_type following_value = static_cast<skipfield_type>(*(it.skipfield_pointer + 1) + 1);
2356 *(it.skipfield_pointer + following_value - 1) = *(it.skipfield_pointer) = following_value;
2357
2358 const skipfield_type following_previous = *(reinterpret_cast<skipfield_pointer_type>(it.element_pointer + 1));
2359 const skipfield_type following_next = *(reinterpret_cast<skipfield_pointer_type>(it.element_pointer + 1) + 1);
2360 *(reinterpret_cast<skipfield_pointer_type>(it.element_pointer)) = following_previous;
2361 *(reinterpret_cast<skipfield_pointer_type>(it.element_pointer) + 1) = following_next;
2362
2363 const skipfield_type index = static_cast<skipfield_type>(it.element_pointer - group_pointer->elements);
2364
2365 if (following_previous != std::numeric_limits<skipfield_type>::max())
2366 {
2367 *(reinterpret_cast<skipfield_pointer_type>(group_pointer->elements + following_previous) + 1) = index; // Set next index of previous free list node to this node's 'next' index
2368 }
2369
2370 if (following_next != std::numeric_limits<skipfield_type>::max())
2371 {
2372 *(reinterpret_cast<skipfield_pointer_type>(group_pointer->elements + following_next)) = index; // Set previous index of next free list node to this node's 'previous' index
2373 }
2374 else
2375 {
2376 group_pointer->free_list_head = index;
2377 }
2378
2379 update_value = following_value;
2380 break;
2381 }
2382 case 3: // both preceding and following consecutive erased elements - erased element is between two skipblocks
2383 {
2384 *(it.skipfield_pointer) = 1; // modification to allow skipfield to be used for SIMD-gather masking
2385 const skipfield_type preceding_value = *(it.skipfield_pointer - 1);
2386 const skipfield_type following_value = static_cast<skipfield_type>(*(it.skipfield_pointer + 1) + 1);
2387
2388 // Join the skipblocks
2389 *(it.skipfield_pointer - preceding_value) = *(it.skipfield_pointer + following_value - 1) = static_cast<skipfield_type>(preceding_value + following_value);
2390
2391 // Remove the following skipblock's entry from the free list
2392 const skipfield_type following_previous = *(reinterpret_cast<skipfield_pointer_type>(it.element_pointer + 1));
2393 const skipfield_type following_next = *(reinterpret_cast<skipfield_pointer_type>(it.element_pointer + 1) + 1);
2394
2395 if (following_previous != std::numeric_limits<skipfield_type>::max())
2396 {
2397 *(reinterpret_cast<skipfield_pointer_type>(group_pointer->elements + following_previous) + 1) = following_next; // Set next index of previous free list node to this node's 'next' index
2398 }
2399
2400 if (following_next != std::numeric_limits<skipfield_type>::max())
2401 {
2402 *(reinterpret_cast<skipfield_pointer_type>(group_pointer->elements + following_next)) = following_previous; // Set previous index of next free list node to this node's 'previous' index
2403 }
2404 else
2405 {
2406 group_pointer->free_list_head = following_previous;
2407 }
2408
2409 update_value = following_value;
2410 break;
2411 }
2412 }
2413
2414 iterator return_iterator(it.group_pointer, it.element_pointer + update_value, it.skipfield_pointer + update_value);
2415 return_iterator.check_for_end_of_group_and_progress();
2416
2417 if (it.element_pointer == begin_iterator.element_pointer) // If original iterator was first element in colony, update it's value with the next non-erased element:
2418 {
2419 begin_iterator = return_iterator;
2420 }
2421
2422 return return_iterator;
2423 }
2424
2425 // else: group is empty, consolidate groups
2426 switch((group_pointer->next_group != NULL) | ((group_pointer != begin_iterator.group_pointer) << 1))
2427 {
2428 case 0: // ie. group_pointer == begin_iterator.group_pointer && group_pointer->next_group == NULL; only group in colony
2429 {
2430 // Reset skipfield and free list rather than clearing - leads to fewer allocations/deallocations:
2431 std::memset(&*(group_pointer->skipfield), 0, sizeof(skipfield_type) * group_pointer->capacity); // &* to avoid problems with non-trivial pointers. Although there is one more skipfield than group_pointer->capacity, capacity + 1 is not necessary here as the end skipfield is never written to after initialization
2432 group_pointer->free_list_head = std::numeric_limits<skipfield_type>::max();
2433 groups_with_erasures_list_head = NULL;
2434
2435 // Reset begin and end iterators:
2436 end_iterator.element_pointer = begin_iterator.element_pointer = group_pointer->last_endpoint = group_pointer->elements;
2437 end_iterator.skipfield_pointer = begin_iterator.skipfield_pointer = group_pointer->skipfield;
2438
2439 return end_iterator;
2440 }
2441 case 1: // ie. group_pointer == begin_iterator.group_pointer && group_pointer->next_group != NULL. Remove first group, change first group to next group
2442 {
2443 group_pointer->next_group->previous_group = NULL; // Cut off this group from the chain
2444 begin_iterator.group_pointer = group_pointer->next_group; // Make the next group the first group
2445
2446 update_subsequent_group_numbers(begin_iterator.group_pointer);
2447
2448 if (group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max()) // Erasures present within the group, ie. was part of the intrusive list of groups with erasures.
2449 {
2450 remove_from_groups_with_erasures_list(group_pointer);
2451 }
2452
2453 total_capacity -= group_pointer->capacity;
2454 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, group_pointer);
2455 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, group_pointer, 1);
2456
2457 // note: end iterator only needs to be changed if the deleted group was the final group in the chain ie. not in this case
2458 begin_iterator.element_pointer = begin_iterator.group_pointer->elements + *(begin_iterator.group_pointer->skipfield); // If the beginning index has been erased (ie. skipfield != 0), skip to next non-erased element
2459 begin_iterator.skipfield_pointer = begin_iterator.group_pointer->skipfield + *(begin_iterator.group_pointer->skipfield);
2460
2461 return begin_iterator;
2462 }
2463 case 3: // this is a non-first group but not final group in chain: delete the group, then link previous group to the next group in the chain:
2464 {
2465 group_pointer->next_group->previous_group = group_pointer->previous_group;
2466 const group_pointer_type return_group = group_pointer->previous_group->next_group = group_pointer->next_group; // close the chain, removing this group from it
2467
2468 update_subsequent_group_numbers(return_group);
2469
2470 if (group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max())
2471 {
2472 remove_from_groups_with_erasures_list(group_pointer);
2473 }
2474
2475 total_capacity -= group_pointer->capacity;
2476 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, group_pointer);
2477 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, group_pointer, 1);
2478
2479 // Return next group's first non-erased element:
2480 return iterator(return_group, return_group->elements + *(return_group->skipfield), return_group->skipfield + *(return_group->skipfield));
2481 }
2482 default: // this is a non-first group and the final group in the chain
2483 {
2484 if (group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max())
2485 {
2486 remove_from_groups_with_erasures_list(group_pointer);
2487 }
2488
2489 group_pointer->previous_group->next_group = NULL;
2490 end_iterator.group_pointer = group_pointer->previous_group; // end iterator needs to be changed as element supplied was the back element of the colony
2491 end_iterator.element_pointer = reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield);
2492 end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + end_iterator.group_pointer->capacity;
2493
2494 total_capacity -= group_pointer->capacity;
2495 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, group_pointer);
2496 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, group_pointer, 1);
2497
2498 return end_iterator;
2499 }
2500 }
2501 }
2502
2503
2504
2505 // Range erase:
2506
2507 void erase(const const_iterator &iterator1, const const_iterator &iterator2) // if uninitialized/invalid iterators supplied, function could generate an exception. If iterator1 > iterator2, behaviour is undefined.
2508 {
2509 assert(iterator1 <= iterator2);
2510
2511 const_iterator current = iterator1;
2512
2513 if (current.group_pointer != iterator2.group_pointer) // ie. if start and end iterators are in separate groups
2514 {
2515 if (current.element_pointer != current.group_pointer->elements + *(current.group_pointer->skipfield)) // if iterator1 is not the first non-erased element in it's group - most common case
2516 {
2517 size_type number_of_group_erasures = 0;
2518
2519 // Now update skipfield:
2520 const aligned_pointer_type end = iterator1.group_pointer->last_endpoint;
2521
2522 // Schema: first erase all non-erased elements until end of group & remove all skipblocks post-iterator1 from the free_list. Then, either update preceding skipblock or create new one:
2523
2524 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT // if trivially-destructible, and C++11 or higher, and no erasures in group, skip while loop below and just jump straight to the location
2525 if ((std::is_trivially_destructible<element_type>::value) & (current.group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()))
2526 {
2527 number_of_group_erasures += static_cast<size_type>(end - current.element_pointer);
2528 }
2529 else
2530 #endif
2531 {
2532 while (current.element_pointer != end)
2533 {
2534 if (*current.skipfield_pointer == 0)
2535 {
2536 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
2537 if PLF_COLONY_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
2538 #endif
2539 {
2540 PLF_COLONY_DESTROY(element_allocator_type, (*this), reinterpret_cast<pointer>(current.element_pointer)); // Destruct element
2541 }
2542
2543 ++number_of_group_erasures;
2544 ++current.element_pointer;
2545 ++current.skipfield_pointer;
2546 }
2547 else // remove skipblock from group:
2548 {
2549 const skipfield_type prev_free_list_index = *(reinterpret_cast<skipfield_pointer_type>(current.element_pointer));
2550 const skipfield_type next_free_list_index = *(reinterpret_cast<skipfield_pointer_type>(current.element_pointer) + 1);
2551
2552 current.element_pointer += *(current.skipfield_pointer);
2553 current.skipfield_pointer += *(current.skipfield_pointer);
2554
2555 if (next_free_list_index == std::numeric_limits<skipfield_type>::max() && prev_free_list_index == std::numeric_limits<skipfield_type>::max()) // if this is the last skipblock in the free list
2556 {
2557 remove_from_groups_with_erasures_list(iterator1.group_pointer); // remove group from list of free-list groups - will be added back in down below, but not worth optimizing for
2558 iterator1.group_pointer->free_list_head = std::numeric_limits<skipfield_type>::max();
2559 number_of_group_erasures += static_cast<size_type>(end - current.element_pointer);
2560
2561 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
2562 if PLF_COLONY_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
2563 #endif
2564 {
2565 while (current.element_pointer != end) // miniloop - avoid checking skipfield for rest of elements in group, as there are no more skipped elements now
2566 {
2567 PLF_COLONY_DESTROY(element_allocator_type, (*this), reinterpret_cast<pointer>(current.element_pointer++)); // Destruct element
2568 }
2569 }
2570
2571 break; // end overall while loop
2572 }
2573 else if (next_free_list_index == std::numeric_limits<skipfield_type>::max()) // if this is the head of the free list
2574 {
2575 current.group_pointer->free_list_head = prev_free_list_index; // make free list head equal to next free list node
2576 *(reinterpret_cast<skipfield_pointer_type>(current.group_pointer->elements + prev_free_list_index) + 1) = std::numeric_limits<skipfield_type>::max();
2577 }
2578 else // either a tail or middle free list node
2579 {
2580 *(reinterpret_cast<skipfield_pointer_type>(current.group_pointer->elements + next_free_list_index)) = prev_free_list_index;
2581
2582 if (prev_free_list_index != std::numeric_limits<skipfield_type>::max()) // ie. not the tail free list node
2583 {
2584 *(reinterpret_cast<skipfield_pointer_type>(current.group_pointer->elements + prev_free_list_index) + 1) = next_free_list_index;
2585 }
2586 }
2587 }
2588 }
2589 }
2590
2591
2592 const skipfield_type previous_node_value = *(iterator1.skipfield_pointer - 1);
2593 const skipfield_type distance_to_end = static_cast<skipfield_type>(end - iterator1.element_pointer);
2594
2595 std::memset(&*(iterator1.skipfield_pointer), 1, sizeof(skipfield_type) * (static_cast<size_type>(distance_to_end) - 1)); // modification to allow skipfield to be used for SIMD-gather masking
2596
2597
2598 if (previous_node_value == 0) // no previous skipblock
2599 {
2600 *iterator1.skipfield_pointer = distance_to_end; // set start node value
2601 *(iterator1.skipfield_pointer + distance_to_end - 1) = distance_to_end; // set end node value
2602
2603 const skipfield_type index = static_cast<skipfield_type>(iterator1.element_pointer - iterator1.group_pointer->elements);
2604
2605 if (iterator1.group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max()) // ie. if this group already has some erased elements
2606 {
2607 *(reinterpret_cast<skipfield_pointer_type>(iterator1.group_pointer->elements + iterator1.group_pointer->free_list_head) + 1) = index; // set prev free list head's 'next index' number to the index of the iterator1 element
2608 }
2609 else
2610 {
2611 iterator1.group_pointer->erasures_list_next_group = groups_with_erasures_list_head; // add it to the groups-with-erasures free list
2612 groups_with_erasures_list_head = iterator1.group_pointer;
2613 }
2614
2615 *(reinterpret_cast<skipfield_pointer_type>(iterator1.element_pointer)) = iterator1.group_pointer->free_list_head;
2616 *(reinterpret_cast<skipfield_pointer_type>(iterator1.element_pointer) + 1) = std::numeric_limits<skipfield_type>::max();
2617 iterator1.group_pointer->free_list_head = index;
2618 }
2619 else
2620 { // update previous skipblock, no need to update free list:
2621 *(iterator1.skipfield_pointer - previous_node_value) = *(iterator1.skipfield_pointer + distance_to_end - 1) = static_cast<skipfield_type>(previous_node_value + distance_to_end);
2622 }
2623
2624 iterator1.group_pointer->number_of_elements = static_cast<skipfield_type>(iterator1.group_pointer->number_of_elements - number_of_group_erasures);
2625 total_number_of_elements -= number_of_group_erasures;
2626
2627 current.group_pointer = current.group_pointer->next_group;
2628 }
2629
2630
2631 // Intermediate groups:
2632 const group_pointer_type previous_group = current.group_pointer->previous_group;
2633
2634 while (current.group_pointer != iterator2.group_pointer)
2635 {
2636 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
2637 if PLF_COLONY_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
2638 #endif
2639 {
2640 current.element_pointer = current.group_pointer->elements + *(current.group_pointer->skipfield);
2641 current.skipfield_pointer = current.group_pointer->skipfield + *(current.group_pointer->skipfield);
2642 const aligned_pointer_type end = current.group_pointer->last_endpoint;
2643
2644 do
2645 {
2646 PLF_COLONY_DESTROY(element_allocator_type, (*this), reinterpret_cast<pointer>(current.element_pointer)); // Destruct element
2647 const skipfield_type skip = *(++current.skipfield_pointer);
2648 current.element_pointer += static_cast<size_type>(skip) + 1u;
2649 current.skipfield_pointer += skip;
2650 } while (current.element_pointer != end);
2651 }
2652
2653 if (current.group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max())
2654 {
2655 remove_from_groups_with_erasures_list(current.group_pointer);
2656 }
2657
2658 total_number_of_elements -= current.group_pointer->number_of_elements;
2659 const group_pointer_type current_group = current.group_pointer;
2660 current.group_pointer = current.group_pointer->next_group;
2661
2662 total_capacity -= current_group->capacity;
2663 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, current_group);
2664 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, current_group, 1);
2665 }
2666
2667 current.element_pointer = current.group_pointer->elements + *(current.group_pointer->skipfield); // TODO
2668 current.skipfield_pointer = current.group_pointer->skipfield + *(current.group_pointer->skipfield);
2669 current.group_pointer->previous_group = previous_group;
2670
2671 if (previous_group != NULL)
2672 {
2673 previous_group->next_group = current.group_pointer;
2674 }
2675 else
2676 {
2677 begin_iterator = iterator2; // This line is included here primarily to avoid a secondary if statement within the if block below - it will not be needed in any other situation
2678 }
2679 }
2680
2681 if (current.element_pointer == iterator2.element_pointer) // in case iterator2 was at beginning of it's group - also covers empty range case (first == last)
2682 {
2683 return;
2684 }
2685
2686 // Final group:
2687 // Code explanation:
2688 // If not erasing entire final group, 1. Destruct elements (if non-trivial destructor) and add locations to group free list. 2. process skipfield.
2689 // If erasing entire group, 1. Destruct elements (if non-trivial destructor), 2. if no elements left in colony, clear() 3. otherwise reset end_iterator and remove group from groups-with-erasures list (if free list of erasures present)
2690
2691 // Two scenarios at this point: either (a) we have processed several groups above and are now at the final group, or (b) iterator1 and iterator2 are in the same group, so we need to check whether the current element pointer is at the first unerased element in the group (second conditional below):
2692 if (iterator2.element_pointer != end_iterator.element_pointer || current.element_pointer != current.group_pointer->elements + *(current.group_pointer->skipfield)) // ie. not erasing entire group
2693 {
2694 size_type number_of_group_erasures = 0;
2695 // Schema: first erased all non-erased elements until end of group & remove all skipblocks post-iterator2 from the free_list. Then, either update preceding skipblock or create new one:
2696
2697 const const_iterator current_saved = current;
2698
2699 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT // if trivially-destructible, and C++11 or higher, and no erasures in group, skip while loop below and just jump straight to the location
2700 if ((std::is_trivially_destructible<element_type>::value) & (current.group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()))
2701 {
2702 number_of_group_erasures += static_cast<size_type>(iterator2.element_pointer - current.element_pointer);
2703 }
2704 else
2705 #endif
2706 {
2707 while (current.element_pointer != iterator2.element_pointer)
2708 {
2709 if (*current.skipfield_pointer == 0)
2710 {
2711 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
2712 if PLF_COLONY_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
2713 #endif
2714 {
2715 PLF_COLONY_DESTROY(element_allocator_type, (*this), reinterpret_cast<pointer>(current.element_pointer)); // Destruct element
2716 }
2717
2718 ++number_of_group_erasures;
2719 ++current.element_pointer;
2720 ++current.skipfield_pointer;
2721 }
2722 else // remove skipblock from group:
2723 {
2724 const skipfield_type prev_free_list_index = *(reinterpret_cast<skipfield_pointer_type>(current.element_pointer));
2725 const skipfield_type next_free_list_index = *(reinterpret_cast<skipfield_pointer_type>(current.element_pointer) + 1);
2726
2727 current.element_pointer += *(current.skipfield_pointer);
2728 current.skipfield_pointer += *(current.skipfield_pointer);
2729
2730 if (next_free_list_index == std::numeric_limits<skipfield_type>::max() && prev_free_list_index == std::numeric_limits<skipfield_type>::max()) // if this is the last skipblock in the free list
2731 {
2732 remove_from_groups_with_erasures_list(iterator2.group_pointer); // remove group from list of free-list groups - will be added back in down below, but not worth optimizing for
2733 iterator2.group_pointer->free_list_head = std::numeric_limits<skipfield_type>::max();
2734 number_of_group_erasures += static_cast<size_type>(iterator2.element_pointer - current.element_pointer);
2735
2736 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
2737 if PLF_COLONY_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
2738 #endif
2739 {
2740 while (current.element_pointer != iterator2.element_pointer)
2741 {
2742 PLF_COLONY_DESTROY(element_allocator_type, (*this), reinterpret_cast<pointer>(current.element_pointer++)); // Destruct element
2743 }
2744 }
2745
2746 break; // end overall while loop
2747 }
2748 else if (next_free_list_index == std::numeric_limits<skipfield_type>::max()) // if this is the head of the free list
2749 {
2750 current.group_pointer->free_list_head = prev_free_list_index;
2751 *(reinterpret_cast<skipfield_pointer_type>(current.group_pointer->elements + prev_free_list_index) + 1) = std::numeric_limits<skipfield_type>::max();
2752 }
2753 else
2754 {
2755 *(reinterpret_cast<skipfield_pointer_type>(current.group_pointer->elements + next_free_list_index)) = prev_free_list_index;
2756
2757 if (prev_free_list_index != std::numeric_limits<skipfield_type>::max()) // ie. not the tail free list node
2758 {
2759 *(reinterpret_cast<skipfield_pointer_type>(current.group_pointer->elements + prev_free_list_index) + 1) = next_free_list_index;
2760 }
2761 }
2762 }
2763 }
2764 }
2765
2766
2767 const skipfield_type distance_to_iterator2 = static_cast<skipfield_type>(iterator2.element_pointer - current_saved.element_pointer);
2768 const skipfield_type index = static_cast<skipfield_type>(current_saved.element_pointer - iterator2.group_pointer->elements);
2769
2770 std::memset(&*(current_saved.skipfield_pointer + 1), 1, sizeof(skipfield_type) * (static_cast<size_type>(distance_to_iterator2) - 1)); // modification to allow skipfield to be used for SIMD-gather masking
2771
2772
2773 if (index == 0 || *(current_saved.skipfield_pointer - 1) == 0) // element is either at start of group or previous skipfield node is 0
2774 {
2775 *(current_saved.skipfield_pointer) = distance_to_iterator2;
2776 *(iterator2.skipfield_pointer - 1) = distance_to_iterator2;
2777
2778 if (iterator2.group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max()) // ie. if this group already has some erased elements
2779 {
2780 *(reinterpret_cast<skipfield_pointer_type>(iterator2.group_pointer->elements + iterator2.group_pointer->free_list_head) + 1) = index;
2781 }
2782 else
2783 {
2784 iterator2.group_pointer->erasures_list_next_group = groups_with_erasures_list_head; // add it to the groups-with-erasures free list
2785 groups_with_erasures_list_head = iterator2.group_pointer;
2786 }
2787
2788 *(reinterpret_cast<skipfield_pointer_type>(current_saved.element_pointer)) = iterator2.group_pointer->free_list_head;
2789 *(reinterpret_cast<skipfield_pointer_type>(current_saved.element_pointer) + 1) = std::numeric_limits<skipfield_type>::max();
2790 iterator2.group_pointer->free_list_head = index;
2791 }
2792 else // If iterator 1 & 2 are in same group, but iterator 1 was not at start of group, and previous skipfield node is an end node in a skipblock:
2793 {
2794 // Just update existing skipblock, no need to create new free list node:
2795 const skipfield_type prev_node_value = *(current_saved.skipfield_pointer - 1);
2796 *(current_saved.skipfield_pointer - prev_node_value) = static_cast<skipfield_type>(prev_node_value + distance_to_iterator2);
2797 *(iterator2.skipfield_pointer - 1) = static_cast<skipfield_type>(prev_node_value + distance_to_iterator2);
2798 }
2799
2800
2801 if (iterator1.element_pointer == begin_iterator.element_pointer)
2802 {
2803 begin_iterator = iterator2;
2804 }
2805
2806 iterator2.group_pointer->number_of_elements = static_cast<skipfield_type>(iterator2.group_pointer->number_of_elements - number_of_group_erasures);
2807 total_number_of_elements -= number_of_group_erasures;
2808 }
2809 else // ie. full group erasure
2810 {
2811 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
2812 if PLF_COLONY_CONSTEXPR (!std::is_trivially_destructible<element_type>::value)
2813 #endif
2814 {
2815 while(current.element_pointer != iterator2.element_pointer)
2816 {
2817 PLF_COLONY_DESTROY(element_allocator_type, (*this), reinterpret_cast<pointer>(current.element_pointer));
2818 current.element_pointer += static_cast<size_type>(*(++current.skipfield_pointer)) + 1u;
2819 current.skipfield_pointer += *current.skipfield_pointer;
2820 }
2821 }
2822
2823
2824 if ((total_number_of_elements -= current.group_pointer->number_of_elements) != 0) // ie. previous_group != NULL or next_group != NULL
2825 {
2826 current.group_pointer->previous_group->next_group = current.group_pointer->next_group;
2827
2828 if (current.group_pointer == end_iterator.group_pointer)
2829 {
2830 end_iterator.group_pointer = current.group_pointer->previous_group;
2831 end_iterator.element_pointer = end_iterator.group_pointer->last_endpoint;
2832 end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + end_iterator.group_pointer->capacity;
2833 }
2834 else if (current.group_pointer == begin_iterator.group_pointer)
2835 {
2836 begin_iterator.group_pointer = current.group_pointer->next_group;
2837 const skipfield_type skip = *(begin_iterator.group_pointer->skipfield);
2838 begin_iterator.element_pointer = begin_iterator.group_pointer->elements + skip;
2839 begin_iterator.skipfield_pointer = begin_iterator.group_pointer->skipfield + skip;
2840 }
2841
2842 total_capacity -= current.group_pointer->capacity;
2843
2844 if (current.group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max())
2845 {
2846 remove_from_groups_with_erasures_list(current.group_pointer);
2847 }
2848 }
2849 else // ie. colony is now empty
2850 {
2851 blank();
2852 }
2853
2854 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, current.group_pointer);
2855 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, current.group_pointer, 1);
2856 }
2857 }
2858
2859
2860
2861 #ifdef PLF_COLONY_CPP20_SUPPORT
2862 [[nodiscard]]
2863 #endif
2864 inline PLF_COLONY_FORCE_INLINE bool empty() const PLF_COLONY_NOEXCEPT
2865 {
2866 return total_number_of_elements == 0;
2867 }
2868
2869
2870
2871 inline size_type size() const PLF_COLONY_NOEXCEPT
2872 {
2873 return total_number_of_elements;
2874 }
2875
2876
2877
2878 #ifdef PLF_COLONY_TEST_DEBUG // used for debugging during internal testing only:
2879 inline size_type group_size_sum() const PLF_COLONY_NOEXCEPT
2880 {
2881 size_type temp = 0;
2882
2883 for (group_pointer_type current = begin_iterator.group_pointer; current != NULL; current = current->next_group)
2884 {
2885 temp += current->number_of_elements;
2886 }
2887
2888 return temp;
2889 }
2890 #endif
2891
2892
2893 inline size_type max_size() const PLF_COLONY_NOEXCEPT
2894 {
2895 #ifdef PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
2896 return std::allocator_traits<element_allocator_type>::max_size(*this);
2897 #else
2898 return element_allocator_type::max_size();
2899 #endif
2900 }
2901
2902
2903
2904 inline size_type capacity() const PLF_COLONY_NOEXCEPT
2905 {
2906 return total_capacity;
2907 }
2908
2909
2910
2911 inline size_type approximate_memory_use() const PLF_COLONY_NOEXCEPT
2912 {
2913 return
2914 sizeof(*this) + // sizeof colony basic structure
2915 (total_capacity * (sizeof(aligned_element_type) + sizeof(skipfield_type))) + // sizeof current colony data capacity + skipfields
2916 ((end_iterator.group_pointer == NULL) ? 0 : ((end_iterator.group_pointer->group_number + 1) * (sizeof(group) + sizeof(skipfield_type)))); // if colony not empty, add the memory use of the group structures themselves, adding the extra skipfield node
2917 }
2918
2919
2920
2921 void set_block_capacity_limits(const plf::limits capacities)
2922 {
2923 assert((capacities.min > 2) & (capacities.min <= capacities.max));
2924 assert(capacities.max <= std::numeric_limits<skipfield_type>::max());
2925
2926 pointer_allocator_pair.min_elements_per_group = static_cast<skipfield_type>(capacities.min);
2927 group_allocator_pair.max_elements_per_group = static_cast<skipfield_type>(capacities.max);
2928
2929 // Need to check all group sizes here, because splice might append smaller blocks to the end of a larger block:
2930 for (group_pointer_type current = begin_iterator.group_pointer; current != NULL; current = current->next_group)
2931 {
2932 if (current->capacity < capacities.min || current->capacity > capacities.max)
2933 {
2934 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT // If type is non-copyable/movable, cannot be consolidated, throw exception:
2935 if PLF_COLONY_CONSTEXPR (!((std::is_copy_constructible<element_type>::value && std::is_copy_assignable<element_type>::value) || (std::is_move_constructible<element_type>::value && std::is_move_assignable<element_type>::value)))
2936 {
2937 throw;
2938 }
2939 else
2940 #endif
2941 {
2942 consolidate();
2943 }
2944
2945 return;
2946 }
2947 }
2948 }
2949
2950
2951
2952 inline void set_minimum_block_capacity(const size_t min_allocation_amount)
2953 {
2954 set_block_capacity_limits(plf::limits(min_allocation_amount, static_cast<size_t>(group_allocator_pair.max_elements_per_group)));
2955 }
2956
2957
2958
2959 inline void set_maximum_block_capacity(const size_t max_allocation_amount)
2960 {
2961 set_block_capacity_limits(plf::limits(static_cast<size_t>(pointer_allocator_pair.min_elements_per_group), max_allocation_amount));
2962 }
2963
2964
2965
2966 inline plf::limits get_block_capacity_limits() const PLF_COLONY_NOEXCEPT
2967 {
2968 return plf::limits(static_cast<size_t>(pointer_allocator_pair.min_elements_per_group), static_cast<size_t>(group_allocator_pair.max_elements_per_group));
2969 }
2970
2971
2972
2973 inline PLF_COLONY_FORCE_INLINE void clear() PLF_COLONY_NOEXCEPT
2974 {
2975 destroy_all_data();
2976 blank();
2977 }
2978
2979
2980
2981 inline colony & operator = (const colony &source)
2982 {
2983 if (&source == this){
2984 return *this;
2985 }
2986
2987 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
2988 destroy_all_data();
2989 colony temp(source);
2990 *this = std::move(temp); // Avoid generating 2nd temporary
2991 #else
2992 clear();
2993 colony temp(source);
2994 swap(temp);
2995 #endif
2996
2997 return *this;
2998 }
2999
3000
3001
3002 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
3003 // Move assignment
3004 colony & operator = (colony &&source) PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT(allocator_type)
3005 {
3006 assert (&source != this);
3007 destroy_all_data();
3008
3009 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
3010 if PLF_COLONY_CONSTEXPR (std::is_trivial<group_pointer_type>::value && std::is_trivial<aligned_pointer_type>::value && std::is_trivial<skipfield_pointer_type>::value)
3011 {
3012 std::memcpy(static_cast<void *>(this), &source, sizeof(colony));
3013 }
3014 else
3015 #endif
3016 {
3017 end_iterator = std::move(source.end_iterator);
3018 begin_iterator = std::move(source.begin_iterator);
3019 groups_with_erasures_list_head = std::move(source.groups_with_erasures_list_head);
3020 total_number_of_elements = source.total_number_of_elements;
3021 total_capacity = source.total_capacity;
3022 pointer_allocator_pair.min_elements_per_group = source.pointer_allocator_pair.min_elements_per_group;
3023 group_allocator_pair.max_elements_per_group = source.group_allocator_pair.max_elements_per_group;
3024 }
3025
3026 source.blank();
3027 return *this;
3028 }
3029 #endif
3030
3031
3032
3033 #ifdef PLF_COLONY_INITIALIZER_LIST_SUPPORT
3034 inline colony & operator = (const std::initializer_list<element_type> &element_list)
3035 {
3036 clear();
3037 insert(element_list);
3038 return *this;
3039 }
3040 #endif
3041
3042
3043
3044 bool operator == (const colony &rh) const PLF_COLONY_NOEXCEPT
3045 {
3046 assert (this != &rh);
3047
3048 if (total_number_of_elements != rh.total_number_of_elements)
3049 {
3050 return false;
3051 }
3052
3053 for (const_iterator lh_iterator = begin_iterator, rh_iterator = rh.begin_iterator; lh_iterator != end_iterator; ++lh_iterator, ++rh_iterator)
3054 {
3055 if (*lh_iterator != *rh_iterator)
3056 {
3057 return false;
3058 }
3059 }
3060
3061 return true;
3062 }
3063
3064
3065
3066 inline bool operator != (const colony &rh) const PLF_COLONY_NOEXCEPT
3067 {
3068 return !(*this == rh);
3069 }
3070
3071
3072
3073 void shrink_to_fit()
3074 {
3075 if (total_number_of_elements == total_capacity)
3076 {
3077 return;
3078 }
3079 else if (total_number_of_elements == 0) // Edge case
3080 {
3081 clear();
3082 return;
3083 }
3084
3085 consolidate();
3086 }
3087
3088
3089
3090// TODO: re-write so it only reallocates contents if copyable/movable type and returns -1 otherwise
3091 void reserve(const size_type original_reserve_amount)
3092 {
3093 if (original_reserve_amount == 0 || original_reserve_amount <= total_capacity) // We already have enough space allocated
3094 {
3095 return;
3096 }
3097
3098 skipfield_type reserve_amount;
3099
3100 if (original_reserve_amount > static_cast<size_type>(group_allocator_pair.max_elements_per_group))
3101 {
3102 reserve_amount = group_allocator_pair.max_elements_per_group;
3103 }
3104 else if (original_reserve_amount < static_cast<size_type>(pointer_allocator_pair.min_elements_per_group))
3105 {
3106 reserve_amount = pointer_allocator_pair.min_elements_per_group;
3107 }
3108 else if (original_reserve_amount > max_size())
3109 {
3110 reserve_amount = static_cast<skipfield_type>(max_size());
3111 }
3112 else
3113 {
3114 reserve_amount = static_cast<skipfield_type>(original_reserve_amount);
3115 }
3116
3117 if (total_number_of_elements == 0) // Most common scenario - empty colony
3118 {
3119 if (begin_iterator.group_pointer != NULL) // Edge case - empty colony but first group is initialized ie. had some insertions but all elements got subsequently erased
3120 {
3121 PLF_COLONY_DESTROY(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer);
3122 PLF_COLONY_DEALLOCATE(group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, 1);
3123 } // else: Empty colony, no insertions yet, time to allocate
3124
3125 initialize(reserve_amount);
3126 begin_iterator.group_pointer->last_endpoint = begin_iterator.group_pointer->elements; // last_endpoint initially == elements + 1 via default constructor
3127 begin_iterator.group_pointer->number_of_elements = 0; // 1 by default
3128 }
3129 else // Non-empty colony, don't have enough space allocated
3130 {
3131 const skipfield_type original_min_elements = pointer_allocator_pair.min_elements_per_group;
3132 pointer_allocator_pair.min_elements_per_group = static_cast<skipfield_type>(reserve_amount); // Make sure all groups are at maximum appropriate capacity (this amount already rounded down to a skipfield type earlier in function)
3133 consolidate();
3134 pointer_allocator_pair.min_elements_per_group = original_min_elements;
3135 }
3136 }
3137
3138
3139
3140 // Advance implementation for iterator and const_iterator:
3141 template <bool is_const>
3142 void advance(colony_iterator<is_const> &it, difference_type distance) const // Cannot be noexcept due to the possibility of an uninitialized iterator
3143 {
3144 // For code simplicity - should hopefully be optimized out by compiler:
3145 group_pointer_type &group_pointer = it.group_pointer;
3146 aligned_pointer_type &element_pointer = it.element_pointer;
3147 skipfield_pointer_type &skipfield_pointer = it.skipfield_pointer;
3148
3149 assert(group_pointer != NULL); // covers uninitialized colony_iterator && empty group
3150
3151 // Now, run code based on the nature of the distance type - negative, positive or zero:
3152 if (distance > 0) // ie. +=
3153 {
3154 // Code explanation:
3155 // For the initial state of the iterator, we don't know how what elements have been erased before that element in that group.
3156 // So for the first group, we follow the following logic:
3157 // 1. If no elements have been erased in the group, we do simple addition to progress either to within the group (if the distance is small enough) or the end of the group and subtract from distance accordingly.
3158 // 2. If any of the first group elements have been erased, we manually iterate, as we don't know whether the erased elements occur before or after the initial iterator position, and we subtract 1 from the distance amount each time. Iteration continues until either distance becomes zero, or we reach the end of the group.
3159
3160 // For all subsequent groups, we follow this logic:
3161 // 1. If distance is larger than the total number of non-erased elements in a group, we skip that group and subtract the number of elements in that group from distance
3162 // 2. If distance is smaller than the total number of non-erased elements in a group, then:
3163 // a. if there're no erased elements in the group we simply add distance to group->elements to find the new location for the iterator
3164 // b. if there are erased elements in the group, we manually iterate and subtract 1 from distance on each iteration, until the new iterator location is found ie. distance = 0
3165
3166 // Note: incrementing element_pointer is avoided until necessary to avoid needless calculations
3167
3168 assert (!(element_pointer == group_pointer->last_endpoint && group_pointer->next_group == NULL)); // Check that we're not already at end()
3169
3170 // Special case for initial element pointer and initial group (we don't know how far into the group the element pointer is)
3171 if (element_pointer != group_pointer->elements + *(group_pointer->skipfield)) // ie. != first non-erased element in group
3172 {
3173 const difference_type distance_from_end = static_cast<difference_type>(group_pointer->last_endpoint - element_pointer);
3174
3175 if (group_pointer->number_of_elements == static_cast<skipfield_type>(distance_from_end)) // ie. if there are no erasures in the group (using endpoint - elements_start to determine number of elements in group just in case this is the last group of the colony, in which case group->last_endpoint != group->elements + group->capacity)
3176 {
3177 if (distance < distance_from_end)
3178 {
3179 element_pointer += distance;
3180 skipfield_pointer += distance;
3181 return;
3182 }
3183 else if (group_pointer->next_group == NULL) // either we've reached end() or gone beyond it, so bound to end()
3184 {
3185 element_pointer = group_pointer->last_endpoint;
3186 skipfield_pointer += distance_from_end;
3187 return;
3188 }
3189 else
3190 {
3191 distance -= distance_from_end;
3192 }
3193 }
3194 else
3195 {
3196 const skipfield_pointer_type endpoint = skipfield_pointer + distance_from_end;
3197
3198 while(true)
3199 {
3200 ++skipfield_pointer;
3201 skipfield_pointer += *skipfield_pointer;
3202 --distance;
3203
3204 if (skipfield_pointer == endpoint)
3205 {
3206 break;
3207 }
3208 else if (distance == 0)
3209 {
3210 element_pointer = group_pointer->elements + (skipfield_pointer - group_pointer->skipfield);
3211 return;
3212 }
3213 }
3214
3215 if (group_pointer->next_group == NULL) // either we've reached end() or gone beyond it, so bound to end()
3216 {
3217 element_pointer = group_pointer->last_endpoint;
3218 return;
3219 }
3220 }
3221
3222 group_pointer = group_pointer->next_group;
3223
3224 if (distance == 0)
3225 {
3226 element_pointer = group_pointer->elements + *(group_pointer->skipfield);
3227 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3228 return;
3229 }
3230 }
3231
3232
3233 // Intermediary groups - at the start of this code block and the subsequent block, the position of the iterator is assumed to be the first non-erased element in the current group:
3234 while (static_cast<difference_type>(group_pointer->number_of_elements) <= distance)
3235 {
3236 if (group_pointer->next_group == NULL) // either we've reached end() or gone beyond it, so bound to end()
3237 {
3238 element_pointer = group_pointer->last_endpoint;
3239 skipfield_pointer = group_pointer->skipfield + (group_pointer->last_endpoint - group_pointer->elements);
3240 return;
3241 }
3242 else if ((distance -= group_pointer->number_of_elements) == 0)
3243 {
3244 group_pointer = group_pointer->next_group;
3245 element_pointer = group_pointer->elements + *(group_pointer->skipfield);
3246 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3247 return;
3248 }
3249 else
3250 {
3251 group_pointer = group_pointer->next_group;
3252 }
3253 }
3254
3255
3256 // Final group (if not already reached):
3257 if (group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()) // No erasures in this group, use straight pointer addition
3258 {
3259 element_pointer = group_pointer->elements + distance;
3260 skipfield_pointer = group_pointer->skipfield + distance;
3261 return;
3262 }
3263 else // ie. number_of_elements > distance - safe to ignore endpoint check condition while incrementing:
3264 {
3265 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3266
3267 do
3268 {
3269 ++skipfield_pointer;
3270 skipfield_pointer += *skipfield_pointer;
3271 } while(--distance != 0);
3272
3273 element_pointer = group_pointer->elements + (skipfield_pointer - group_pointer->skipfield);
3274 return;
3275 }
3276
3277 return;
3278 }
3279 else if (distance < 0) // for negative change
3280 {
3281 // Code logic is very similar to += above
3282 assert(!((element_pointer == group_pointer->elements + *(group_pointer->skipfield)) && group_pointer->previous_group == NULL)); // check that we're not already at begin()
3283 distance = -distance;
3284
3285 // Special case for initial element pointer and initial group (we don't know how far into the group the element pointer is)
3286 if (element_pointer != group_pointer->last_endpoint) // ie. != end()
3287 {
3288 if (group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()) // ie. no prior erasures have occurred in this group
3289 {
3290 const difference_type distance_from_beginning = static_cast<difference_type>(element_pointer - group_pointer->elements);
3291
3292 if (distance <= distance_from_beginning)
3293 {
3294 element_pointer -= distance;
3295 skipfield_pointer -= distance;
3296 return;
3297 }
3298 else if (group_pointer->previous_group == NULL) // ie. we've gone before begin(), so bound to begin()
3299 {
3300 element_pointer = group_pointer->elements;
3301 skipfield_pointer = group_pointer->skipfield;
3302 return;
3303 }
3304 else
3305 {
3306 distance -= distance_from_beginning;
3307 }
3308 }
3309 else
3310 {
3311 const skipfield_pointer_type beginning_point = group_pointer->skipfield + *(group_pointer->skipfield);
3312
3313 while(skipfield_pointer != beginning_point)
3314 {
3315 --skipfield_pointer;
3316 skipfield_pointer -= *skipfield_pointer;
3317
3318 if (--distance == 0)
3319 {
3320 element_pointer = group_pointer->elements + (skipfield_pointer - group_pointer->skipfield);
3321 return;
3322 }
3323 }
3324
3325 if (group_pointer->previous_group == NULL)
3326 {
3327 element_pointer = group_pointer->elements + *(group_pointer->skipfield); // This is first group, so bound to begin() (just in case final decrement took us before begin())
3328 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3329 return;
3330 }
3331 }
3332
3333 group_pointer = group_pointer->previous_group;
3334 }
3335
3336
3337 // Intermediary groups - at the start of this code block and the subsequent block, the position of the iterator is assumed to be either the first non-erased element in the next group over, or end():
3338 while(static_cast<difference_type>(group_pointer->number_of_elements) < distance)
3339 {
3340 if (group_pointer->previous_group == NULL) // we've gone beyond begin(), so bound to it
3341 {
3342 element_pointer = group_pointer->elements + *(group_pointer->skipfield);
3343 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3344 return;
3345 }
3346
3347 distance -= group_pointer->number_of_elements;
3348 group_pointer = group_pointer->previous_group;
3349 }
3350
3351
3352 // Final group (if not already reached):
3353 if (static_cast<difference_type>(group_pointer->number_of_elements) == distance)
3354 {
3355 element_pointer = group_pointer->elements + *(group_pointer->skipfield);
3356 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3357 return;
3358 }
3359 else if (group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()) // ie. no erased elements in this group
3360 {
3361 element_pointer = reinterpret_cast<aligned_pointer_type>(group_pointer->skipfield) - distance;
3362 skipfield_pointer = (group_pointer->skipfield + group_pointer->capacity) - distance;
3363 return;
3364 }
3365 else // ie. no more groups to traverse but there are erased elements in this group
3366 {
3367 skipfield_pointer = group_pointer->skipfield + group_pointer->capacity;
3368
3369 do
3370 {
3371 --skipfield_pointer;
3372 skipfield_pointer -= *skipfield_pointer;
3373 } while(--distance != 0);
3374
3375 element_pointer = group_pointer->elements + (skipfield_pointer - group_pointer->skipfield);
3376 return;
3377 }
3378 }
3379
3380 // Only distance == 0 reaches here
3381 }
3382
3383
3384
3385
3386 // Advance for reverse_iterator and const_reverse_iterator - this needs to be implemented slightly differently to forward-iterator's advance, as it needs to be able to reach rend() (ie. begin() - 1) and to be bounded by rbegin():
3387 template <bool is_const>
3388 void advance(colony_reverse_iterator<is_const> &reverse_it, difference_type distance) const // could cause exception if iterator is uninitialized
3389 {
3390 group_pointer_type &group_pointer = reverse_it.it.group_pointer;
3391 aligned_pointer_type &element_pointer = reverse_it.it.element_pointer;
3392 skipfield_pointer_type &skipfield_pointer = reverse_it.it.skipfield_pointer;
3393
3394 assert(element_pointer != NULL);
3395
3396 if (distance > 0)
3397 {
3398 assert (!(element_pointer == group_pointer->elements - 1 && group_pointer->previous_group == NULL)); // Check that we're not already at rend()
3399 // Special case for initial element pointer and initial group (we don't know how far into the group the element pointer is)
3400 // Since a reverse_iterator cannot == last_endpoint (ie. before rbegin()) we don't need to check for that like with iterator
3401 if (group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max())
3402 {
3403 difference_type distance_from_beginning = static_cast<difference_type>(element_pointer - group_pointer->elements);
3404
3405 if (distance <= distance_from_beginning)
3406 {
3407 element_pointer -= distance;
3408 skipfield_pointer -= distance;
3409 return;
3410 }
3411 else if (group_pointer->previous_group == NULL) // Either we've reached rend() or gone beyond it, so bound to rend()
3412 {
3413 element_pointer = group_pointer->elements - 1;
3414 skipfield_pointer = group_pointer->skipfield - 1;
3415 return;
3416 }
3417 else
3418 {
3419 distance -= distance_from_beginning;
3420 }
3421 }
3422 else
3423 {
3424 const skipfield_pointer_type beginning_point = group_pointer->skipfield + *(group_pointer->skipfield);
3425
3426 while(skipfield_pointer != beginning_point)
3427 {
3428 --skipfield_pointer;
3429 skipfield_pointer -= *skipfield_pointer;
3430
3431 if (--distance == 0)
3432 {
3433 element_pointer = group_pointer->elements + (skipfield_pointer - group_pointer->skipfield);
3434 return;
3435 }
3436 }
3437
3438 if (group_pointer->previous_group == NULL)
3439 {
3440 element_pointer = group_pointer->elements - 1; // If we've reached rend(), bound to that
3441 skipfield_pointer = group_pointer->skipfield - 1;
3442 return;
3443 }
3444 }
3445
3446 group_pointer = group_pointer->previous_group;
3447
3448
3449 // Intermediary groups - at the start of this code block and the subsequent block, the position of the iterator is assumed to be the first non-erased element in the next group:
3450 while(static_cast<difference_type>(group_pointer->number_of_elements) < distance)
3451 {
3452 if (group_pointer->previous_group == NULL) // bound to rend()
3453 {
3454 element_pointer = group_pointer->elements - 1;
3455 skipfield_pointer = group_pointer->skipfield - 1;
3456 return;
3457 }
3458
3459 distance -= static_cast<difference_type>(group_pointer->number_of_elements);
3460 group_pointer = group_pointer->previous_group;
3461 }
3462
3463
3464 // Final group (if not already reached)
3465 if (static_cast<difference_type>(group_pointer->number_of_elements) == distance)
3466 {
3467 element_pointer = group_pointer->elements + *(group_pointer->skipfield);
3468 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3469 return;
3470 }
3471 else if (group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max())
3472 {
3473 element_pointer = reinterpret_cast<aligned_pointer_type>(group_pointer->skipfield) - distance;
3474 skipfield_pointer = (group_pointer->skipfield + group_pointer->capacity) - distance;
3475 return;
3476 }
3477 else
3478 {
3479 skipfield_pointer = group_pointer->skipfield + group_pointer->capacity;
3480
3481 do
3482 {
3483 --skipfield_pointer;
3484 skipfield_pointer -= *skipfield_pointer;
3485 } while(--distance != 0);
3486
3487 element_pointer = group_pointer->elements + (skipfield_pointer - group_pointer->skipfield);
3488 return;
3489 }
3490 }
3491 else if (distance < 0)
3492 {
3493 assert (!((element_pointer == (group_pointer->last_endpoint - 1) - *(group_pointer->skipfield + (group_pointer->last_endpoint - group_pointer->elements) - 1)) && group_pointer->next_group == NULL)); // Check that we're not already at rbegin()
3494
3495 if (element_pointer != group_pointer->elements + *(group_pointer->skipfield)) // ie. != first non-erased element in group
3496 {
3497 if (group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()) // ie. if there are no erasures in the group
3498 {
3499 const difference_type distance_from_end = static_cast<difference_type>(group_pointer->last_endpoint - element_pointer);
3500
3501 if (distance < distance_from_end)
3502 {
3503 element_pointer += distance;
3504 skipfield_pointer += distance;
3505 return;
3506 }
3507 else if (group_pointer->next_group == NULL) // bound to rbegin()
3508 {
3509 element_pointer = group_pointer->last_endpoint - 1; // no erasures so we don't have to subtract skipfield value as we do below
3510 skipfield_pointer += distance_from_end - 1;
3511 return;
3512 }
3513 else
3514 {
3515 distance -= distance_from_end;
3516 }
3517 }
3518 else
3519 {
3520 const skipfield_pointer_type endpoint = skipfield_pointer + (group_pointer->last_endpoint - element_pointer);
3521
3522 while(true)
3523 {
3524 ++skipfield_pointer;
3525 skipfield_pointer += *skipfield_pointer;
3526 --distance;
3527
3528 if (skipfield_pointer == endpoint)
3529 {
3530 break;
3531 }
3532 else if (distance == 0)
3533 {
3534 element_pointer = group_pointer->elements + (skipfield_pointer - group_pointer->skipfield);
3535 return;
3536 }
3537 }
3538
3539 if (group_pointer->next_group == NULL) // bound to rbegin()
3540 {
3541 --skipfield_pointer;
3542 element_pointer = (group_pointer->last_endpoint - 1) - *skipfield_pointer;
3543 skipfield_pointer -= *skipfield_pointer;
3544 return;
3545 }
3546 }
3547
3548 group_pointer = group_pointer->next_group;
3549
3550 if (distance == 0)
3551 {
3552 element_pointer = group_pointer->elements + *(group_pointer->skipfield);
3553 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3554 return;
3555 }
3556 }
3557
3558
3559 // Intermediary groups - at the start of this code block and the subsequent block, the position of the iterator is assumed to be the first non-erased element in the current group, as a result of the previous code blocks:
3560 while(static_cast<difference_type>(group_pointer->number_of_elements) <= distance)
3561 {
3562 if (group_pointer->next_group == NULL) // bound to rbegin()
3563 {
3564 skipfield_pointer = group_pointer->skipfield + (group_pointer->last_endpoint - group_pointer->elements) - 1;
3565 element_pointer = (group_pointer->last_endpoint - 1) - *skipfield_pointer;
3566 skipfield_pointer -= *skipfield_pointer;
3567 return;
3568 }
3569 else if ((distance -= group_pointer->number_of_elements) == 0)
3570 {
3571 group_pointer = group_pointer->next_group;
3572 element_pointer = group_pointer->elements + *(group_pointer->skipfield);
3573 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3574 return;
3575 }
3576 else
3577 {
3578 group_pointer = group_pointer->next_group;
3579 }
3580 }
3581
3582
3583 // Final group (if not already reached):
3584 if (group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()) // No erasures in this group, use straight pointer addition
3585 {
3586 element_pointer = group_pointer->elements + distance;
3587 skipfield_pointer = group_pointer->skipfield + distance;
3588 return;
3589 }
3590 else // ie. number_of_elements > distance - safe to ignore endpoint check condition while incrementing:
3591 {
3592 skipfield_pointer = group_pointer->skipfield + *(group_pointer->skipfield);
3593
3594 do
3595 {
3596 ++skipfield_pointer;
3597 skipfield_pointer += *skipfield_pointer;
3598 } while(--distance != 0);
3599
3600 element_pointer = group_pointer->elements + (skipfield_pointer - group_pointer->skipfield);
3601 return;
3602 }
3603
3604 return;
3605 }
3606 }
3607
3608
3609
3610
3611 // Next implementations:
3612 template <bool is_const>
3613 inline colony_iterator<is_const> next(const colony_iterator<is_const> &it, const typename colony_iterator<is_const>::difference_type distance = 1) const
3614 {
3615 colony_iterator<is_const> return_iterator(it);
3616 advance(return_iterator, distance);
3617 return return_iterator;
3618 }
3619
3620
3621
3622 template <bool is_const>
3623 inline colony_reverse_iterator<is_const> next(const colony_reverse_iterator<is_const> &it, const typename colony_reverse_iterator<is_const>::difference_type distance = 1) const
3624 {
3625 colony_reverse_iterator<is_const> return_iterator(it);
3626 advance(return_iterator, distance);
3627 return return_iterator;
3628 }
3629
3630
3631
3632 // Prev implementations:
3633 template <bool is_const>
3634 inline colony_iterator<is_const> prev(const colony_iterator<is_const> &it, const typename colony_iterator<is_const>::difference_type distance = 1) const
3635 {
3636 colony_iterator<is_const> return_iterator(it);
3637 advance(return_iterator, -distance);
3638 return return_iterator;
3639 }
3640
3641
3642
3643 template <bool is_const>
3644 inline colony_reverse_iterator<is_const> prev(const colony_reverse_iterator<is_const> &it, const typename colony_reverse_iterator<is_const>::difference_type distance = 1) const
3645 {
3646 colony_reverse_iterator<is_const> return_iterator(it);
3647 advance(return_iterator, -distance);
3648 return return_iterator;
3649 }
3650
3651
3652
3653 // distance implementation:
3654
3655 template <bool is_const>
3656 typename colony_iterator<is_const>::difference_type distance(const colony_iterator<is_const> &first, const colony_iterator<is_const> &last) const
3657 {
3658 // Code logic:
3659 // If iterators are the same, return 0
3660 // Otherwise, find which iterator is later in colony, copy that to iterator2. Copy the lower to iterator1.
3661 // If they are not pointing to elements in the same group, process the intermediate groups and add distances,
3662 // skipping manual incrementation in all but the initial and final groups.
3663 // In the initial and final groups, manual incrementation must be used to calculate distance, if there have been no prior erasures in those groups.
3664 // If there are no prior erasures in either of those groups, we can use pointer arithmetic to calculate the distances for those groups.
3665
3666 assert(!(first.group_pointer == NULL) && !(last.group_pointer == NULL)); // Check that they are initialized
3667
3668 if (last.element_pointer == first.element_pointer)
3669 {
3670 return 0;
3671 }
3672
3673 typedef colony_iterator<is_const> iterator_type;
3674 typedef typename iterator_type::difference_type diff_type;
3675 diff_type distance = 0;
3676
3677 iterator_type iterator1 = first, iterator2 = last;
3678 const bool swap = first > last;
3679
3680 if (swap) // Less common case
3681 {
3682 iterator1 = last;
3683 iterator2 = first;
3684 }
3685
3686 if (iterator1.group_pointer != iterator2.group_pointer) // if not in same group, process intermediate groups
3687 {
3688 // Process initial group:
3689 if (iterator1.group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()) // If no prior erasures have occured in this group we can do simple addition
3690 {
3691 distance += static_cast<diff_type>(iterator1.group_pointer->last_endpoint - iterator1.element_pointer);
3692 }
3693 else if (iterator1.element_pointer == iterator1.group_pointer->elements + *(iterator1.group_pointer->skipfield)) // ie. element is at start of group - rare case
3694 {
3695 distance += static_cast<diff_type>(iterator1.group_pointer->number_of_elements);
3696 }
3697 else // Manually iterate to find distance to end of group:
3698 {
3699 const skipfield_pointer_type endpoint = iterator1.skipfield_pointer + (iterator1.group_pointer->last_endpoint - iterator1.element_pointer);
3700
3701 while (iterator1.skipfield_pointer != endpoint)
3702 {
3703 ++iterator1.skipfield_pointer;
3704 iterator1.skipfield_pointer += *(iterator1.skipfield_pointer);
3705 ++distance;
3706 }
3707 }
3708
3709 // Process all other intermediate groups:
3710 iterator1.group_pointer = iterator1.group_pointer->next_group;
3711
3712 while (iterator1.group_pointer != iterator2.group_pointer)
3713 {
3714 distance += static_cast<diff_type>(iterator1.group_pointer->number_of_elements);
3715 iterator1.group_pointer = iterator1.group_pointer->next_group;
3716 }
3717
3718 iterator1.skipfield_pointer = iterator1.group_pointer->skipfield;
3719 }
3720
3721
3722 if (iterator2.group_pointer->free_list_head == std::numeric_limits<skipfield_type>::max()) // ie. no erasures in this group, direct subtraction is possible
3723 {
3724 distance += static_cast<diff_type>(iterator2.skipfield_pointer - iterator1.skipfield_pointer);
3725 }
3726 else if (iterator2.group_pointer->last_endpoint - 1 >= iterator2.element_pointer || iterator2.element_pointer + *(iterator2.skipfield_pointer + 1) == iterator2.group_pointer->last_endpoint) // ie. if iterator2 is .end() or the last element in the block
3727 {
3728 distance += static_cast<diff_type>(iterator2.group_pointer->number_of_elements - (iterator2.group_pointer->last_endpoint - iterator2.element_pointer));
3729 }
3730 else
3731 {
3732 while (iterator1.skipfield_pointer != iterator2.skipfield_pointer)
3733 {
3734 ++iterator1.skipfield_pointer;
3735 iterator1.skipfield_pointer += *(iterator1.skipfield_pointer);
3736 ++distance;
3737 }
3738 }
3739
3740
3741 if (swap)
3742 {
3743 distance = -distance;
3744 }
3745
3746 return distance;
3747 }
3748
3749
3750
3751 template <bool is_const>
3752 inline typename colony_reverse_iterator<is_const>::difference_type distance(const colony_reverse_iterator<is_const> &iterator1, const colony_reverse_iterator<is_const> &iterator2) const
3753 {
3754 return distance(iterator2.it, iterator1.it);
3755 }
3756
3757
3758
3759 iterator get_iterator_from_pointer(const pointer element_pointer) const PLF_COLONY_NOEXCEPT
3760 {
3761 if (total_number_of_elements != 0)
3762 {
3763 // Start with last group first, as will be the largest group in most cases:
3764 for (group_pointer_type current_group = end_iterator.group_pointer; current_group != NULL; current_group = current_group->previous_group)
3765 {
3766 if (reinterpret_cast<aligned_pointer_type>(element_pointer) >= current_group->elements && reinterpret_cast<aligned_pointer_type>(element_pointer) < reinterpret_cast<aligned_pointer_type>(current_group->skipfield))
3767 {
3768 const skipfield_pointer_type skipfield_pointer = current_group->skipfield + (reinterpret_cast<aligned_pointer_type>(element_pointer) - current_group->elements);
3769 return (*skipfield_pointer == 0) ? iterator(current_group, reinterpret_cast<aligned_pointer_type>(element_pointer), skipfield_pointer) : end_iterator; // If element has been erased, return end()
3770 }
3771 }
3772 }
3773
3774 return end_iterator;
3775 }
3776
3777
3778
3779 inline allocator_type get_allocator() const PLF_COLONY_NOEXCEPT
3780 {
3781 return element_allocator_type();
3782 }
3783
3784
3785
3786 void splice(colony &source) PLF_COLONY_NOEXCEPT_SWAP(allocator_type)
3787 {
3788 // Process: if there are unused memory spaces at the end of the current back group of the chain, convert them
3789 // to skipped elements and add the locations to the group's free list.
3790 // Then link the destination's groups to the source's groups and nullify the source.
3791 // If the source has more unused memory spaces in the back group than the destination, swap them before processing to reduce the number of locations added to a free list and also subsequent jumps during iteration.
3792
3793 assert(&source != this);
3794
3795 if (source.total_number_of_elements == 0)
3796 {
3797 return;
3798 }
3799 else if (total_number_of_elements == 0)
3800 {
3801 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
3802 *this = std::move(source);
3803 #else
3804 clear();
3805 swap(source);
3806 #endif
3807
3808 return;
3809 }
3810
3811 // If there's more unused element locations at end of destination than source, swap with source to reduce number of skipped elements and size of free-list:
3812 if ((reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield) - end_iterator.element_pointer) > (reinterpret_cast<aligned_pointer_type>(source.end_iterator.group_pointer->skipfield) - source.end_iterator.element_pointer))
3813 {
3814 swap(source);
3815 }
3816
3817
3818 // Correct group sizes if necessary:
3819 if (source.pointer_allocator_pair.min_elements_per_group < pointer_allocator_pair.min_elements_per_group)
3820 {
3821 pointer_allocator_pair.min_elements_per_group = source.pointer_allocator_pair.min_elements_per_group;
3822 }
3823
3824 if (source.group_allocator_pair.max_elements_per_group > group_allocator_pair.max_elements_per_group)
3825 {
3826 group_allocator_pair.max_elements_per_group = source.group_allocator_pair.max_elements_per_group;
3827 }
3828
3829 // Add source list of groups-with-erasures to destination list of groups-with-erasures:
3830 if (source.groups_with_erasures_list_head != NULL)
3831 {
3832 if (groups_with_erasures_list_head != NULL)
3833 {
3834 group_pointer_type tail_group = groups_with_erasures_list_head;
3835
3836 while (tail_group->erasures_list_next_group != NULL)
3837 {
3838 tail_group = tail_group->erasures_list_next_group;
3839 }
3840
3841 tail_group->erasures_list_next_group = source.groups_with_erasures_list_head;
3842 }
3843 else
3844 {
3845 groups_with_erasures_list_head = source.groups_with_erasures_list_head;
3846 }
3847 }
3848
3849
3850 const skipfield_type distance_to_end = static_cast<skipfield_type>(reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield) - end_iterator.element_pointer);
3851
3852 if (distance_to_end != 0) // 0 == edge case
3853 { // Mark unused element memory locations from back group as skipped/erased:
3854
3855 // Update skipfield:
3856 const skipfield_type previous_node_value = *(end_iterator.skipfield_pointer - 1);
3857 end_iterator.group_pointer->last_endpoint = reinterpret_cast<aligned_pointer_type>(end_iterator.group_pointer->skipfield);
3858
3859 if (previous_node_value == 0) // no previous skipblock
3860 {
3861 *end_iterator.skipfield_pointer = distance_to_end;
3862 *(end_iterator.skipfield_pointer + distance_to_end - 1) = distance_to_end;
3863
3864 const skipfield_type index = static_cast<skipfield_type>(end_iterator.element_pointer - end_iterator.group_pointer->elements);
3865
3866 if (end_iterator.group_pointer->free_list_head != std::numeric_limits<skipfield_type>::max()) // ie. if this group already has some erased elements
3867 {
3868 *(reinterpret_cast<skipfield_pointer_type>(end_iterator.group_pointer->elements + end_iterator.group_pointer->free_list_head) + 1) = index; // set prev free list head's 'next index' number to the index of the current element
3869 }
3870 else
3871 {
3872 end_iterator.group_pointer->erasures_list_next_group = groups_with_erasures_list_head; // add it to the groups-with-erasures free list
3873 groups_with_erasures_list_head = end_iterator.group_pointer;
3874 }
3875
3876 *(reinterpret_cast<skipfield_pointer_type>(end_iterator.element_pointer)) = end_iterator.group_pointer->free_list_head;
3877 *(reinterpret_cast<skipfield_pointer_type>(end_iterator.element_pointer) + 1) = std::numeric_limits<skipfield_type>::max();
3878 end_iterator.group_pointer->free_list_head = index;
3879 }
3880 else
3881 { // update previous skipblock, no need to update free list:
3882 *(end_iterator.skipfield_pointer - previous_node_value) = *(end_iterator.skipfield_pointer + distance_to_end - 1) = static_cast<skipfield_type>(previous_node_value + distance_to_end);
3883 }
3884 }
3885
3886
3887 // Update subsequent group numbers:
3888 group_pointer_type current_group = source.begin_iterator.group_pointer;
3889 size_type current_group_number = end_iterator.group_pointer->group_number;
3890
3891 do
3892 {
3893 current_group->group_number = ++current_group_number;
3894 current_group = current_group->next_group;
3895 } while (current_group != NULL);
3896
3897
3898 // Join the destination and source group chains:
3899 end_iterator.group_pointer->next_group = source.begin_iterator.group_pointer;
3900 source.begin_iterator.group_pointer->previous_group = end_iterator.group_pointer;
3901 end_iterator = source.end_iterator;
3902 total_number_of_elements += source.total_number_of_elements;
3903 total_capacity += source.total_capacity;
3904 source.blank();
3905 }
3906
3907
3908
3909 struct raw_memory_block_pointers : private uchar_allocator_type
3910 {
3911 aligned_pointer_type *element_memory_block_pointers; // array of pointers to element memory blocks (allow for scatter back to memory blocks)
3912 skipfield_pointer_type *skipfield_memory_block_pointers; // array of pointers to skipfield memory blocks
3913 skipfield_type *block_capacities; // array of the number of elements in each memory block
3914 size_type number_of_blocks; // size of each array
3915
3916 raw_memory_block_pointers(const size_type size) :
3917 element_memory_block_pointers(reinterpret_cast<aligned_pointer_type *>(PLF_COLONY_ALLOCATE_INITIALIZATION(uchar_allocator_type, size * sizeof(aligned_pointer_type), NULL))),
3918 skipfield_memory_block_pointers(reinterpret_cast<skipfield_pointer_type *>(PLF_COLONY_ALLOCATE_INITIALIZATION(uchar_allocator_type, size * sizeof(skipfield_pointer_type), NULL))),
3919 block_capacities(reinterpret_cast<skipfield_type *>(PLF_COLONY_ALLOCATE_INITIALIZATION(uchar_allocator_type, size * sizeof(skipfield_type *), NULL))),
3920 number_of_blocks(size)
3921 {}
3922
3924 {
3925 PLF_COLONY_DEALLOCATE(uchar_allocator_type, (*this), reinterpret_cast<uchar_pointer_type>(element_memory_block_pointers), number_of_blocks * sizeof(colony::pointer));
3926 PLF_COLONY_DEALLOCATE(uchar_allocator_type, (*this), reinterpret_cast<uchar_pointer_type>(skipfield_memory_block_pointers), number_of_blocks * sizeof(skipfield_pointer_type));
3927 PLF_COLONY_DEALLOCATE(uchar_allocator_type, (*this), reinterpret_cast<uchar_pointer_type>(block_capacities), number_of_blocks * sizeof(size_type));
3928 }
3929 };
3930
3931
3932
3934 {
3935 raw_memory_block_pointers *data = new raw_memory_block_pointers(end_iterator.group_pointer->group_number + 1);
3936 size_type group_number = 0;
3937
3938 for (group_pointer_type current_group = begin_iterator.group_pointer; current_group != end_iterator.group_pointer; current_group = current_group->next_group)
3939 {
3940 data->element_memory_block_pointers[group_number] = current_group->elements;
3941 data->skipfield_memory_block_pointers[group_number] = current_group->skipfield;
3942 data->block_capacities[group_number] = current_group->capacity;
3943 ++group_number;
3944 }
3945
3946 // Special case for end group:
3947 data->element_memory_block_pointers[group_number] = end_iterator.group_pointer->elements;
3948 data->skipfield_memory_block_pointers[group_number] = end_iterator.group_pointer->skipfield;
3949 data->block_capacities[group_number] = static_cast<skipfield_type>(end_iterator.group_pointer->last_endpoint - end_iterator.group_pointer->elements);
3950
3951 return data;
3952 }
3953
3954
3955
3956private:
3957
3958 struct less
3959 {
3960 bool operator() (const element_type &a, const element_type &b) const PLF_COLONY_NOEXCEPT
3961 {
3962 return a < b;
3963 }
3964 };
3965
3966
3967
3968 struct item_index_tuple
3969 {
3970 pointer original_location;
3971 size_type original_index;
3972
3973 item_index_tuple(const pointer _item, const size_type _index) PLF_COLONY_NOEXCEPT:
3974 original_location(_item),
3975 original_index(_index)
3976 {}
3977 };
3978
3979
3980
3981 template <class comparison_function>
3982 struct sort_dereferencer
3983 {
3984 comparison_function stored_instance;
3985
3986 explicit sort_dereferencer(const comparison_function &function_instance):
3987 stored_instance(function_instance)
3988 {}
3989
3990 sort_dereferencer() PLF_COLONY_NOEXCEPT
3991 {}
3992
3993 bool operator() (const item_index_tuple first, const item_index_tuple second)
3994 {
3995 return stored_instance(*(first.original_location), *(second.original_location));
3996 }
3997 };
3998
3999
4000
4001public:
4002
4003
4004 template <class comparison_function>
4005 void sort(comparison_function compare)
4006 {
4007 if (total_number_of_elements < 2)
4008 {
4009 return;
4010 }
4011
4012 #ifdef PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
4013 typedef typename std::allocator_traits<element_allocator_type>::template rebind_alloc<item_index_tuple> tuple_allocator_type;
4014 #else
4015 typedef typename element_allocator_type::template rebind<item_index_tuple>::other tuple_allocator_type;
4016 #endif
4017
4018 tuple_allocator_type tuple_allocator;
4019
4020 item_index_tuple * const sort_array = PLF_COLONY_ALLOCATE(tuple_allocator_type, tuple_allocator, total_number_of_elements, NULL);
4021 item_index_tuple *tuple_pointer = sort_array;
4022
4023 // Construct pointers to all elements in the sequence:
4024 size_type index = 0;
4025
4026 for (iterator current_element = begin_iterator; current_element != end_iterator; ++current_element, ++tuple_pointer, ++index)
4027 {
4028 #ifdef PLF_COLONY_VARIADICS_SUPPORT
4029 PLF_COLONY_CONSTRUCT(tuple_allocator_type, tuple_allocator, tuple_pointer, &*current_element, index);
4030 #else
4031 PLF_COLONY_CONSTRUCT(tuple_allocator_type, tuple_allocator, tuple_pointer, item_index_tuple(&*current_element, index));
4032 #endif
4033 }
4034
4035
4036 // Now, sort the pointers by the values they point to (std::sort is default sort function if the macro below is not defined):
4037 #ifndef PLF_COLONY_SORT_FUNCTION
4038 std::sort(sort_array, sort_array + total_number_of_elements, sort_dereferencer<comparison_function>(compare));
4039 #else
4040 PLF_COLONY_SORT_FUNCTION(sort_array, sort_array + total_number_of_elements, sort_dereferencer<comparison_function>(compare));
4041 #endif
4042
4043
4044 // Sort the actual elements via the tuple array:
4045 index = 0;
4046
4047 for (item_index_tuple *current_tuple = sort_array; current_tuple != tuple_pointer; ++current_tuple, ++index)
4048 {
4049 if (current_tuple->original_index != index)
4050 {
4051 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
4052 element_type end_value = std::move(*(current_tuple->original_location));
4053 #else
4054 element_type end_value = *(current_tuple->original_location);
4055 #endif
4056
4057 size_type destination_index = index;
4058 size_type source_index = current_tuple->original_index;
4059
4060 do
4061 {
4062 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
4063 *(sort_array[destination_index].original_location) = std::move(*(sort_array[source_index].original_location));
4064 #else
4065 *(sort_array[destination_index].original_location) = *(sort_array[source_index].original_location);
4066 #endif
4067
4068 destination_index = source_index;
4069 source_index = sort_array[destination_index].original_index;
4070 sort_array[destination_index].original_index = destination_index;
4071 } while (source_index != index);
4072
4073 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
4074 *(sort_array[destination_index].original_location) = std::move(end_value);
4075 #else
4076 *(sort_array[destination_index].original_location) = end_value;
4077 #endif
4078 }
4079 }
4080
4081 PLF_COLONY_DEALLOCATE(tuple_allocator_type, tuple_allocator, sort_array, total_number_of_elements);
4082 }
4083
4084
4085
4086 inline void sort()
4087 {
4088 sort(less());
4089 }
4090
4091
4092
4093 void swap(colony &source) PLF_COLONY_NOEXCEPT_SWAP(allocator_type)
4094 {
4095 assert(&source != this);
4096
4097 #ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
4098 if PLF_COLONY_CONSTEXPR (std::is_trivial<group_pointer_type>::value && std::is_trivial<aligned_pointer_type>::value && std::is_trivial<skipfield_pointer_type>::value) // if all pointer types are trivial we can just copy using memcpy - avoids constructors/destructors etc and is faster
4099 {
4100 char temp[sizeof(colony)];
4101 std::memcpy(&temp, static_cast<void *>(this), sizeof(colony));
4102 std::memcpy(static_cast<void *>(this), static_cast<void *>(&source), sizeof(colony));
4103 std::memcpy(static_cast<void *>(&source), &temp, sizeof(colony));
4104 }
4105 #ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT // If pointer types are not trivial, moving them is probably going to be more efficient than copying them below
4106 else if PLF_COLONY_CONSTEXPR (std::is_move_assignable<group_pointer_type>::value && std::is_move_assignable<aligned_pointer_type>::value && std::is_move_assignable<skipfield_pointer_type>::value && std::is_move_constructible<group_pointer_type>::value && std::is_move_constructible<aligned_pointer_type>::value && std::is_move_constructible<skipfield_pointer_type>::value)
4107 {
4108 colony temp(std::move(source));
4109 source = std::move(*this);
4110 *this = std::move(temp);
4111 }
4112 #endif
4113 else
4114 #endif
4115 {
4116 const iterator swap_end_iterator = end_iterator, swap_begin_iterator = begin_iterator;
4117 const group_pointer_type swap_groups_with_erasures_list_head = groups_with_erasures_list_head;
4118 const size_type swap_total_number_of_elements = total_number_of_elements, swap_total_capacity = total_capacity;
4119 const skipfield_type swap_min_elements_per_group = pointer_allocator_pair.min_elements_per_group, swap_max_elements_per_group = group_allocator_pair.max_elements_per_group;
4120
4121 end_iterator = source.end_iterator;
4122 begin_iterator = source.begin_iterator;
4123 groups_with_erasures_list_head = source.groups_with_erasures_list_head;
4124 total_number_of_elements = source.total_number_of_elements;
4125 total_capacity = source.total_capacity;
4126 pointer_allocator_pair.min_elements_per_group = source.pointer_allocator_pair.min_elements_per_group;
4127 group_allocator_pair.max_elements_per_group = source.group_allocator_pair.max_elements_per_group;
4128
4129 source.end_iterator = swap_end_iterator;
4130 source.begin_iterator = swap_begin_iterator;
4131 source.groups_with_erasures_list_head = swap_groups_with_erasures_list_head;
4132 source.total_number_of_elements = swap_total_number_of_elements;
4133 source.total_capacity = swap_total_capacity;
4134 source.pointer_allocator_pair.min_elements_per_group = swap_min_elements_per_group;
4135 source.group_allocator_pair.max_elements_per_group = swap_max_elements_per_group;
4136 }
4137 }
4138
4139}; // colony
4140
4141
4142
4143
4144template <class element_type, class element_allocator_type, typename element_skipfield_type>
4145inline void swap (colony<element_type, element_allocator_type, element_skipfield_type> &a, colony<element_type, element_allocator_type, element_skipfield_type> &b) PLF_COLONY_NOEXCEPT_SWAP(element_allocator_type)
4146{
4147 a.swap(b);
4148}
4149
4150
4151
4152} // plf namespace
4153
4154
4155
4156
4157#undef PLF_COLONY_FORCE_INLINE
4158
4159#undef PLF_COLONY_ALIGNMENT_SUPPORT
4160#undef PLF_COLONY_INITIALIZER_LIST_SUPPORT
4161#undef PLF_COLONY_TYPE_TRAITS_SUPPORT
4162#undef PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
4163#undef PLF_COLONY_VARIADICS_SUPPORT
4164#undef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
4165#undef PLF_COLONY_NOEXCEPT
4166#undef PLF_COLONY_NOEXCEPT_SWAP
4167#undef PLF_COLONY_NOEXCEPT_MOVE_ASSIGNMENT
4168#undef PLF_COLONY_CONSTEXPR
4169#undef PLF_COLONY_CPP20_SUPPORT
4170#undef PLF_COLONY_MIN_BLOCK_CAPACITY
4171
4172#undef PLF_COLONY_CONSTRUCT
4173#undef PLF_COLONY_DESTROY
4174#undef PLF_COLONY_ALLOCATE
4175#undef PLF_COLONY_ALLOCATE_INITIALIZATION
4176#undef PLF_COLONY_DEALLOCATE
4177
4178
4179#endif // PLF_COLONY_H
Definition plf_colony.h:391
Definition plf_colony.h:712
Definition plf_colony.h:221
Definition plf_colony.h:3910
Definition plf_colony.h:212