dispenso
A library for task parallelism
 
Loading...
Searching...
No Matches
concurrent_vector.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
46#pragma once
47
48#include <algorithm>
49#include <cassert>
50#include <climits>
51#include <cstring>
52#include <initializer_list>
53#include <stdexcept>
54#include <utility>
55
56#include <dispenso/detail/math.h>
57#include <dispenso/platform.h>
59
60namespace dispenso {
61
73enum class ConcurrentVectorReallocStrategy { kFullBufferAhead, kHalfBufferAhead, kAsNeeded };
74
75struct ReserveTagS {};
81
82// Textual inclusion. Includes undocumented implementation details, e.g. iterators.
83#include <dispenso/detail/concurrent_vector_impl.h>
84
89template <typename T>
95 static constexpr size_t kDefaultCapacity = (sizeof(T) >= 256) ? 2 : 512 / sizeof(T);
96
104 static constexpr size_t kMaxVectorSize =
105 (size_t{1} << (sizeof(size_t) * CHAR_BIT > 47 ? 47 : sizeof(size_t) * CHAR_BIT - 1)) /
106 sizeof(T);
107};
108
121 static constexpr bool kPreferBuffersInline = true;
122
134 ConcurrentVectorReallocStrategy::kAsNeeded;
135
145 static constexpr bool kIteratorPreferSpeed = true;
146};
147
153template <
154 typename T,
155 typename Traits = DefaultConcurrentVectorTraits,
156 typename SizeTraits = DefaultConcurrentVectorSizeTraits<T>>
158 public:
159 using value_type = T;
160 using reference = T&;
161 using const_reference = const T&;
162 using size_type = size_t;
163 using difference_type = ssize_t;
164 using reference_type = T&;
165 using const_reference_type = const T&;
166 using pointer = T*;
167 using const_pointer = const T*;
168 using iterator = std::conditional_t<
169 Traits::kIteratorPreferSpeed,
170 cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, false>,
171 cv::CompactCVecIterator<ConcurrentVector<T, Traits>, T, false>>;
172 using const_iterator = std::conditional_t<
173 Traits::kIteratorPreferSpeed,
174 cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, true>,
175 cv::CompactCVecIterator<ConcurrentVector<T, Traits>, T, true>>;
176 using reverse_iterator = std::reverse_iterator<iterator>;
177 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
178
182 ConcurrentVector() : ConcurrentVector(SizeTraits::kDefaultCapacity / 2, ReserveTag) {}
183
189 ConcurrentVector(size_t startCapacity, ReserveTagS)
190 : firstBucketShift_(
191 detail::log2(
192 detail::nextPow2(std::max(startCapacity, SizeTraits::kDefaultCapacity / 2)))),
193 firstBucketLen_(size_type{1} << firstBucketShift_) {
194 T* firstTwo = cv::alloc<T>(2 * firstBucketLen_);
195 buffers_[0].store(firstTwo, std::memory_order_release);
196 buffers_[1].store(firstTwo + firstBucketLen_, std::memory_order_release);
197 }
198
202 explicit ConcurrentVector(size_t startSize) : ConcurrentVector(startSize, ReserveTag) {
203 size_.store(startSize, std::memory_order_relaxed);
204 T* buf = buffers_[0].load(std::memory_order_relaxed);
205 for (size_t i = 0; i < startSize; ++i) {
206 new (buf + i) T();
207 }
208 }
209
213 ConcurrentVector(size_t startSize, const T& defaultValue)
214 : ConcurrentVector(startSize, ReserveTag) {
215 size_.store(startSize, std::memory_order_relaxed);
216 T* buf = buffers_[0].load(std::memory_order_relaxed);
217 for (size_t i = 0; i < startSize; ++i) {
218 new (buf + i) T(defaultValue);
219 }
220 }
221
225 template <typename InIterator>
226 ConcurrentVector(InIterator start, InIterator end)
227 : ConcurrentVector(std::distance(start, end), start, end) {}
228
234 template <typename InIterator>
235 ConcurrentVector(size_type startSize, InIterator start, InIterator end)
236 : ConcurrentVector(startSize, ReserveTag) {
237 size_.store(startSize, std::memory_order_relaxed);
238 assert(std::distance(start, end) == static_cast<difference_type>(startSize));
239 internalInit(start, end, begin());
240 }
241
245 ConcurrentVector(std::initializer_list<T> l)
246 : ConcurrentVector(l.size(), std::begin(l), std::end(l)) {}
247
252 : ConcurrentVector(other.size(), other.cbegin(), other.cend()) {}
253
258 : buffers_(std::move(other.buffers_)),
259 firstBucketShift_(other.firstBucketShift_),
260 firstBucketLen_(other.firstBucketLen_),
261 size_(other.size_.load(std::memory_order_relaxed)) {
262 other.size_.store(0, std::memory_order_relaxed);
263 // This is possibly unnecessary overhead, but enables the "other" vector to be in a valid,
264 // usable state right away, no empty check or clear required, as it is for std::vector.
265 T* firstTwo = cv::alloc<T>(2 * firstBucketLen_);
266 other.buffers_[0].store(firstTwo, std::memory_order_relaxed);
267 other.buffers_[1].store(firstTwo + firstBucketLen_, std::memory_order_relaxed);
268 }
269
274 if (&other == this) {
275 return *this;
276 }
277
278 clear();
279 reserve(other.size());
280 size_.store(other.size(), std::memory_order_relaxed);
281 internalInit(other.cbegin(), other.cend(), begin());
282
283 return *this;
284 }
285
290 using std::swap;
291 if (&other == this) {
292 return *this;
293 }
294
295 clear();
296 swap(firstBucketShift_, other.firstBucketShift_);
297 swap(firstBucketLen_, other.firstBucketLen_);
298 buffers_ = std::move(other.buffers_);
299 size_t curLen = size_.load(std::memory_order_relaxed);
300 size_.store(other.size_.load(std::memory_order_relaxed), std::memory_order_relaxed);
301 other.size_.store(curLen, std::memory_order_relaxed);
302
303 return *this;
304 }
305
312 void assign(size_type count, const T& value) {
313 clear();
314 reserve(count);
315 size_.store(count, std::memory_order_relaxed);
316 internalFillN(begin(), count, value);
317 }
318
325 template <
326 typename It,
327 typename = typename std::iterator_traits<It>::difference_type,
328 typename = typename std::iterator_traits<It>::pointer,
329 typename = typename std::iterator_traits<It>::reference,
330 typename = typename std::iterator_traits<It>::value_type,
331 typename = typename std::iterator_traits<It>::iterator_category>
332 void assign(It start, It end) {
333 clear();
334 auto count = std::distance(start, end);
335 reserve(count);
336 size_.store(count, std::memory_order_relaxed);
337 internalInit(start, end, begin());
338 }
339
345 void reserve(difference_type capacity) {
346 buffers_.allocAsNecessary({0, 0, firstBucketLen_}, capacity, bucketAndSubIndex(capacity));
347 }
348
354 void resize(difference_type len) {
355 difference_type curLen = static_cast<difference_type>(size_.load(std::memory_order_relaxed));
356 if (curLen < len) {
357 grow_to_at_least(len);
358 } else if (curLen > len) {
359 auto it = end();
360 auto newEnd = begin() + len;
361 do {
362 --it;
363 it->~T();
364 } while (it != newEnd);
365 size_.store(len, std::memory_order_relaxed);
366 }
367 }
368
375 void resize(difference_type len, const T& value) {
376 difference_type curLen = static_cast<difference_type>(size_.load(std::memory_order_relaxed));
377 if (curLen < len) {
378 grow_to_at_least(len, value);
379 } else if (curLen > len) {
380 auto it = end();
381 auto newEnd = begin() + len;
382 do {
383 --it;
384 it->~T();
385 } while (it != newEnd);
386 size_.store(len, std::memory_order_relaxed);
387 }
388 }
389
394 size_type default_capacity() const {
395 return 2 * firstBucketLen_;
396 }
397
402 size_type capacity() const {
403 size_t cap = 2 * firstBucketLen_;
404 for (size_t b = 2; b < kMaxBuffers; ++b) {
405 if (!buffers_[b].load(std::memory_order_relaxed)) {
406 break;
407 }
408 cap *= 2;
409 }
410 return cap;
411 }
412
417 void clear() {
418 auto binfo = bucketAndSubIndex(size_.load(std::memory_order_relaxed));
419 size_t len = binfo.bucketIndex;
420 size_t cap = binfo.bucketCapacity;
421 size_t b = binfo.bucket;
422 do {
423 T* buf = buffers_[b].load(std::memory_order_relaxed);
424 T* t = buf + len;
425
426 while (t != buf) {
427 --t;
428 t->~T();
429 }
430 cap >>= int{b > 1};
431 len = cap;
432 } while (b--);
433 size_.store(0, std::memory_order_release);
434 }
435
441 constexpr size_t kMaxExtra = 2;
442 auto binfo = bucketAndSubIndex(size_.load(std::memory_order_relaxed));
443
444 // We need to at least skip the first two buckets, since we have those as a single allocation.
445 size_t startBucket = std::max<size_t>(2, binfo.bucket + kMaxExtra);
446
447 for (size_t b = startBucket; b < kMaxBuffers; ++b) {
448 T* ptr = buffers_[b].load(std::memory_order_relaxed);
449 if (!ptr) {
450 break;
451 }
452 if (buffers_.shouldDealloc(b)) {
453 cv::dealloc<T>(ptr);
454 }
455 buffers_[b].store(nullptr, std::memory_order_release);
456 }
457 }
458
463 clear();
465 cv::dealloc<T>(buffers_[0].load(std::memory_order_acquire));
466 }
467
474 iterator insert(const_iterator pos, const T& value) {
475 auto it = insertPartial(pos);
476 new (&*it) T(value);
477 return it;
478 }
479
486 iterator insert(const_iterator pos, T&& value) {
487 auto it = insertPartial(pos);
488 new (&*it) T(std::move(value));
489 return it;
490 }
491
499 iterator insert(const_iterator pos, size_type count, const T& value) {
500 auto it = insertPartial(pos, count);
501 std::fill_n(it, count, value);
502 return it;
503 }
504
512 template <
513 typename InputIt,
514 typename = typename std::iterator_traits<InputIt>::difference_type,
515 typename = typename std::iterator_traits<InputIt>::pointer,
516 typename = typename std::iterator_traits<InputIt>::reference,
517 typename = typename std::iterator_traits<InputIt>::value_type,
518 typename = typename std::iterator_traits<InputIt>::iterator_category>
519 iterator insert(const_iterator pos, InputIt first, InputIt last) {
520 size_t len = std::distance(first, last);
521 auto it = insertPartial(pos, len);
522 std::copy_n(first, len, it);
523 return it;
524 }
525
532 iterator insert(const_iterator pos, std::initializer_list<T> ilist) {
533 return insert(pos, ilist.begin(), ilist.end());
534 }
535
542 iterator erase(const_iterator pos) {
543 auto e = end();
544 if (e == pos) {
545 return e;
546 }
547 size_.fetch_sub(1, std::memory_order_relaxed);
548 --e;
549 if (e == pos) {
550 e->~T();
551 return e;
552 }
553 ++e;
554 auto it = begin();
555 it += (pos - it);
556 return std::move(pos + 1, const_iterator(e), it);
557 }
558
567 iterator erase(const_iterator first, const_iterator last) {
568 size_t len = std::distance(first, last);
569 auto it = begin();
570 size_t startIdx = first - it;
571 if (len == 0) {
572 return it + (startIdx + len);
573 }
574 it += startIdx;
575
576 auto e_it = std::move(last, cend(), it);
577
578 if (e_it < last) {
579 // remove any values that were not already moved into
580 do {
581 --last;
582 last->~T();
583 } while (e_it != last);
584 }
585 size_.fetch_sub(len, std::memory_order_relaxed);
586 return e_it;
587 }
588
594 iterator push_back(const T& val) {
595 return emplace_back(val);
596 }
597
603 iterator push_back(T&& val) {
604 return emplace_back(std::move(val));
605 }
606
612 template <typename... Args>
613 iterator emplace_back(Args&&... args) {
614 auto index = size_.fetch_add(1, std::memory_order_relaxed);
615 auto binfo = bucketAndSubIndex(index);
616
617 buffers_.allocAsNecessary(binfo);
618
619 iterator ret{this, index, binfo};
620
621 if (Traits::kIteratorPreferSpeed) {
622 new (&*ret) T(std::forward<Args>(args)...);
623 } else {
624 new (buffers_[binfo.bucket] + binfo.bucketIndex) T(std::forward<Args>(args)...);
625 }
626
627 return ret;
628 }
629
637 template <typename Gen>
638 iterator grow_by_generator(size_type delta, Gen gen) {
639 iterator ret = growByUninitialized(delta);
640 for (auto it = ret; delta--; ++it) {
641 new (&*it) T(gen());
642 }
643 return ret;
644 }
645
652 iterator grow_by(size_type delta, const T& t) {
653 iterator ret = growByUninitialized(delta);
654 internalFillN(ret, delta, t);
655 return ret;
656 }
657
663 iterator grow_by(size_type delta) {
664 iterator ret = growByUninitialized(delta);
665 internalFillDefaultN(ret, delta);
666 return ret;
667 }
668
675 template <
676 typename It,
677 typename = typename std::iterator_traits<It>::difference_type,
678 typename = typename std::iterator_traits<It>::pointer,
679 typename = typename std::iterator_traits<It>::reference,
680 typename = typename std::iterator_traits<It>::value_type,
681 typename = typename std::iterator_traits<It>::iterator_category>
682 iterator grow_by(It start, It end) {
683 iterator ret = growByUninitialized(std::distance(start, end));
684 internalInit(start, end, ret);
685 return ret;
686 }
687
693 iterator grow_by(std::initializer_list<T> initList) {
694 return grow_by(std::begin(initList), std::end(initList));
695 }
696
703 iterator grow_to_at_least(size_type n) {
704 size_t curSize = size_.load(std::memory_order_relaxed);
705 if (curSize < n) {
706 return grow_by(n - curSize);
707 }
708 return {this, n - 1, bucketAndSubIndex(n - 1)};
709 }
710
718 iterator grow_to_at_least(size_type n, const T& t) {
719 size_t curSize = size_.load(std::memory_order_relaxed);
720 if (curSize < n) {
721 return grow_by(n - curSize, t);
722 }
723 return {this, n - 1, bucketAndSubIndex(n - 1)};
724 }
725
729 void pop_back() {
730 auto binfo = bucketAndSubIndex(size_.fetch_sub(1, std::memory_order_relaxed) - 1);
731 T* elt = buffers_[binfo.bucket].load(std::memory_order_relaxed) + binfo.bucketIndex;
732 elt->~T();
733 }
734
743 const T& operator[](size_type index) const {
744 auto binfo = bucketAndSubIndexForIndex(index);
745 T* buf = buffers_[binfo.bucket].load(std::memory_order_relaxed);
746 return buf[binfo.bucketIndex];
747 }
748
757 T& operator[](size_type index) {
758 auto binfo = bucketAndSubIndexForIndex(index);
759 T* buf = buffers_[binfo.bucket].load(std::memory_order_relaxed);
760 return buf[binfo.bucketIndex];
761 }
762
772 const T& at(size_type index) const {
773#if defined(__cpp_exceptions)
774 if (index >= size_.load(std::memory_order_relaxed)) {
775 throw std::out_of_range("Index too large");
776 }
777#endif
778 return operator[](index);
779 }
780
790 T& at(size_type index) {
791#if defined(__cpp_exceptions)
792 if (index >= size_.load(std::memory_order_relaxed)) {
793 throw std::out_of_range("Index too large");
794 }
795#endif
796 return operator[](index);
797 }
798
806 iterator begin() {
807 return {this, 0, {0, 0, firstBucketLen_}};
808 }
809
818 iterator end() {
819 size_t curSize = size_.load(std::memory_order_relaxed);
820 auto binfo = bucketAndSubIndex(curSize);
821 return {this, curSize, binfo};
822 }
823
831 const_iterator begin() const {
832 return {this, 0, {0, 0, firstBucketLen_}};
833 }
834
843 const_iterator end() const {
844 size_t curSize = size_.load(std::memory_order_relaxed);
845 auto binfo = bucketAndSubIndex(curSize);
846 return {this, curSize, binfo};
847 }
848
856 const_iterator cbegin() const {
857 return {this, 0, {0, 0, firstBucketLen_}};
858 }
859
868 const_iterator cend() const {
869 size_t curSize = size_.load(std::memory_order_relaxed);
870 auto binfo = bucketAndSubIndex(curSize);
871 return {this, curSize, binfo};
872 }
873
882 reverse_iterator rbegin() {
883 return reverse_iterator(end());
884 }
885
893 reverse_iterator rend() {
894 return reverse_iterator(begin());
895 }
896
905 const_reverse_iterator rbegin() const {
906 return const_reverse_iterator(cend());
907 }
908
916 const_reverse_iterator rend() const {
917 return const_reverse_iterator(cbegin());
918 }
919
925 bool empty() const {
926 return size_.load(std::memory_order_relaxed) == 0;
927 }
928
934 constexpr size_type max_size() const noexcept {
935 return Traits::kMaxVectorSize;
936 }
937
943 size_type size() const {
944 return size_.load(std::memory_order_relaxed);
945 }
946
951 T& front() {
952 return *buffers_[0].load(std::memory_order_relaxed);
953 }
954
959 const T& front() const {
960 return *buffers_[0].load(std::memory_order_relaxed);
961 }
962
968 T& back() {
969 return *(end() - 1);
970 }
971
977 const T& back() const {
978 return *(end() - 1);
979 }
980
986 using std::swap;
987 swap(firstBucketShift_, oth.firstBucketShift_);
988 swap(firstBucketLen_, oth.firstBucketLen_);
989 size_t othlen = oth.size_.load(std::memory_order_relaxed);
990 oth.size_.store(size_.load(std::memory_order_relaxed), std::memory_order_relaxed);
991 size_.store(othlen, std::memory_order_relaxed);
992
993 // okay, this relies on the fact that we're essentially swapping in the move operator.
994 buffers_ = std::move(oth.buffers_);
995 }
996
997 private:
998 DISPENSO_INLINE cv::BucketInfo bucketAndSubIndexForIndex(size_t index) const {
999#if defined(__clang__)
1000 if (index < firstBucketLen_) {
1001 return {0, index, firstBucketLen_};
1002 }
1003
1004 size_t l2idx = detail::log2(index);
1005 size_t bucket = (l2idx + 1) - firstBucketShift_;
1006 size_t bucketCapacity = size_t{1} << l2idx;
1007 size_t bucketIndex = index - bucketCapacity;
1008
1009 return {bucket, bucketIndex, bucketCapacity};
1010#else
1011 return bucketAndSubIndex(index);
1012#endif // __clang__
1013 }
1014
1015 DISPENSO_INLINE cv::BucketInfo bucketAndSubIndex(size_t index) const {
1016 size_t l2idx = detail::log2(index | 1);
1017 size_t bucket = (l2idx + 1) - firstBucketShift_;
1018 size_t bucketCapacity = size_t{1} << l2idx;
1019 size_t bucketIndex = index - bucketCapacity;
1020
1021 bucket = index < firstBucketLen_ ? 0 : bucket;
1022 bucketIndex = index < firstBucketLen_ ? index : bucketIndex;
1023 bucketCapacity = index < firstBucketLen_ ? firstBucketLen_ : bucketCapacity;
1024
1025 return {bucket, bucketIndex, bucketCapacity};
1026 }
1027
1028 template <typename InIterator>
1029 void internalInit(InIterator start, InIterator end, iterator it) {
1030 while (start != end) {
1031 new (&*it) T(*start);
1032 ++it;
1033 ++start;
1034 }
1035 }
1036
1037 void internalFillN(iterator it, size_t len, const T& value) {
1038 for (; len--; ++it) {
1039 new (&*it) T(value);
1040 }
1041 }
1042
1043 void internalFillDefaultN(iterator it, size_t len) {
1044 for (; len--; ++it) {
1045 new (&*it) T();
1046 }
1047 }
1048
1049 iterator growByUninitialized(size_type delta) {
1050 auto index = size_.fetch_add(delta, std::memory_order_relaxed);
1051 auto binfo = bucketAndSubIndex(index);
1052 buffers_.allocAsNecessary(binfo, delta, bucketAndSubIndex(index + delta));
1053 return {this, index, binfo};
1054 }
1055
1056 iterator insertPartial(const_iterator pos) {
1057 auto e = end();
1058 auto index = size_.fetch_add(1, std::memory_order_relaxed);
1059 auto binfo = bucketAndSubIndex(index);
1060 buffers_.allocAsNecessary(binfo);
1061 new (&*e) T();
1062 return std::move_backward(pos, const_iterator(e), e + 1) - 1;
1063 }
1064
1065 iterator insertPartial(const_iterator pos, size_t len) {
1066 auto e = end();
1067 auto index = size_.fetch_add(len, std::memory_order_relaxed);
1068 buffers_.allocAsNecessary(bucketAndSubIndex(index), len, bucketAndSubIndex(index + len));
1069 for (auto it = e + len; it != e;) {
1070 --it;
1071 new (&*it) T();
1072 }
1073 return std::move_backward(pos, const_iterator(e), e + len) - len;
1074 }
1075
1076 alignas(kCacheLineSize) cv::ConVecBuffer<
1077 T,
1078 SizeTraits::kDefaultCapacity / 2,
1079 SizeTraits::kMaxVectorSize,
1080 Traits::kPreferBuffersInline,
1081 Traits::kReallocStrategy> buffers_;
1082 static constexpr size_t kMaxBuffers = decltype(buffers_)::kMaxBuffers;
1083
1084 size_t firstBucketShift_;
1085 size_t firstBucketLen_;
1086
1087 alignas(kCacheLineSize) std::atomic<size_t> size_{0};
1088
1089 friend class cv::ConVecIterBase<ConcurrentVector<T, Traits>, T>;
1090 friend class cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, false>;
1091 friend class cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, true>;
1092};
1093
1094template <typename T, class Traits1, class Traits2>
1095inline bool operator==(
1096 const ConcurrentVector<T, Traits1>& a,
1097 const ConcurrentVector<T, Traits2>& b) {
1098 return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
1099}
1100
1101template <typename T, class Traits1, class Traits2>
1102inline bool operator!=(
1103 const ConcurrentVector<T, Traits1>& a,
1104 const ConcurrentVector<T, Traits2>& b) {
1105 return !(a == b);
1106}
1107
1108template <typename T, class Traits1, class Traits2>
1109inline bool operator<(
1110 const ConcurrentVector<T, Traits1>& a,
1111 const ConcurrentVector<T, Traits2>& b) {
1112 return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
1113}
1114
1115template <typename T, class Traits1, class Traits2>
1116inline bool operator>(
1117 const ConcurrentVector<T, Traits1>& a,
1118 const ConcurrentVector<T, Traits2>& b) {
1119 return b < a;
1120}
1121
1122template <typename T, class Traits1, class Traits2>
1123inline bool operator<=(
1124 const ConcurrentVector<T, Traits1>& a,
1125 const ConcurrentVector<T, Traits2>& b) {
1126 return !(b < a);
1127}
1128
1129template <typename T, class Traits1, class Traits2>
1130inline bool operator>=(
1131 const ConcurrentVector<T, Traits1>& a,
1132 const ConcurrentVector<T, Traits2>& b) {
1133 return !(a < b);
1134}
1135
1136template <typename T, class Traits>
1137inline void swap(ConcurrentVector<T, Traits>& a, ConcurrentVector<T, Traits>& b) {
1138 a.swap(b);
1139}
1140
1141// Textual inclusion. Includes undocumented implementation details, e.g. iterators.
1142#include <dispenso/detail/concurrent_vector_impl2.h>
1143
1144} // namespace dispenso
ConcurrentVector & operator=(const ConcurrentVector &other)
iterator insert(const_iterator pos, const T &value)
iterator grow_by(It start, It end)
const_reverse_iterator rend() const
iterator emplace_back(Args &&... args)
iterator push_back(const T &val)
iterator erase(const_iterator first, const_iterator last)
const_iterator end() const
ConcurrentVector(size_t startSize, const T &defaultValue)
void assign(size_type count, const T &value)
void resize(difference_type len)
void reserve(difference_type capacity)
iterator insert(const_iterator pos, T &&value)
iterator insert(const_iterator pos, size_type count, const T &value)
const_reverse_iterator rbegin() const
const T & operator[](size_type index) const
iterator grow_to_at_least(size_type n)
ConcurrentVector(size_t startSize)
ConcurrentVector(const ConcurrentVector &other)
T & operator[](size_type index)
iterator grow_to_at_least(size_type n, const T &t)
const_iterator cend() const
constexpr size_type max_size() const noexcept
void resize(difference_type len, const T &value)
const_iterator begin() const
void assign(It start, It end)
const_iterator cbegin() const
size_type default_capacity() const
iterator insert(const_iterator pos, InputIt first, InputIt last)
iterator grow_by(std::initializer_list< T > initList)
ConcurrentVector(size_type startSize, InIterator start, InIterator end)
ConcurrentVector(std::initializer_list< T > l)
iterator insert(const_iterator pos, std::initializer_list< T > ilist)
iterator grow_by(size_type delta)
void swap(ConcurrentVector &oth)
iterator grow_by_generator(size_type delta, Gen gen)
ConcurrentVector(InIterator start, InIterator end)
ConcurrentVector(size_t startCapacity, ReserveTagS)
iterator erase(const_iterator pos)
const T & at(size_type index) const
ConcurrentVector(ConcurrentVector &&other)
iterator grow_by(size_type delta, const T &t)
ConcurrentVector & operator=(ConcurrentVector &&other)
ConcurrentVectorReallocStrategy
constexpr ReserveTagS ReserveTag
static constexpr size_t kDefaultCapacity
This is the starting user-expected capacity in number of elements (algorithm may provide more based o...
static constexpr size_t kMaxVectorSize
The maximum possible size for the vector.
static constexpr bool kPreferBuffersInline
Prefer to place the pointers to the buffers inline in the class.
static constexpr ConcurrentVectorReallocStrategy kReallocStrategy
How far/whether to allocate before memory is required.
static constexpr bool kIteratorPreferSpeed
Should we prefer faster, but larger iterators, or slower, but smaller iterators.