52#include <initializer_list>
56#include <dispenso/detail/math.h>
57#include <dispenso/platform.h>
83#include <dispenso/detail/concurrent_vector_impl.h>
134 ConcurrentVectorReallocStrategy::kAsNeeded;
159 using value_type =
T;
160 using reference =
T&;
161 using const_reference =
const T&;
163 using difference_type = ssize_t;
164 using reference_type =
T&;
165 using const_reference_type =
const 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>;
190 : firstBucketShift_(detail::
log2(
192 firstBucketLen_(size_type{1} << firstBucketShift_) {
193 T*
firstTwo = cv::alloc<T>(2 * firstBucketLen_);
194 buffers_[0].store(
firstTwo, std::memory_order_release);
195 buffers_[1].store(
firstTwo + firstBucketLen_, std::memory_order_release);
202 size_.store(
startSize, std::memory_order_relaxed);
203 T*
buf = buffers_[0].load(std::memory_order_relaxed);
214 size_.store(
startSize, std::memory_order_relaxed);
215 T*
buf = buffers_[0].load(std::memory_order_relaxed);
224 template <
typename InIterator>
233 template <
typename InIterator>
236 size_.store(
startSize, std::memory_order_relaxed);
258 firstBucketShift_(
other.firstBucketShift_),
259 firstBucketLen_(
other.firstBucketLen_),
261 other.size_.store(0, std::memory_order_relaxed);
264 T*
firstTwo = cv::alloc<T>(2 * firstBucketLen_);
265 other.buffers_[0].store(
firstTwo, std::memory_order_relaxed);
266 other.buffers_[1].store(
firstTwo + firstBucketLen_, std::memory_order_relaxed);
273 if (&
other ==
this) {
279 size_.store(
other.size(), std::memory_order_relaxed);
290 if (&
other ==
this) {
295 swap(firstBucketShift_,
other.firstBucketShift_);
296 swap(firstBucketLen_,
other.firstBucketLen_);
297 buffers_ = std::move(
other.buffers_);
298 size_t curLen = size_.load(std::memory_order_relaxed);
299 size_.store(
other.size_.load(std::memory_order_relaxed), std::memory_order_relaxed);
300 other.size_.store(
curLen, std::memory_order_relaxed);
314 size_.store(
count, std::memory_order_relaxed);
326 typename =
typename std::iterator_traits<It>::difference_type,
327 typename =
typename std::iterator_traits<It>::pointer,
328 typename =
typename std::iterator_traits<It>::reference,
329 typename =
typename std::iterator_traits<It>::value_type,
330 typename =
typename std::iterator_traits<It>::iterator_category>
333 auto count = std::distance(start,
end);
335 size_.store(
count, std::memory_order_relaxed);
345 buffers_.allocAsNecessary({0, 0, firstBucketLen_},
capacity, bucketAndSubIndex(
capacity));
354 difference_type
curLen =
static_cast<difference_type
>(size_.load(std::memory_order_relaxed));
364 size_.store(
len, std::memory_order_relaxed);
375 difference_type
curLen =
static_cast<difference_type
>(size_.load(std::memory_order_relaxed));
385 size_.store(
len, std::memory_order_relaxed);
394 return 2 * firstBucketLen_;
402 size_t cap = 2 * firstBucketLen_;
403 for (
size_t b = 2; b < kMaxBuffers; ++b) {
404 if (!buffers_[b].
load(std::memory_order_relaxed)) {
417 auto binfo = bucketAndSubIndex(size_.load(std::memory_order_relaxed));
420 size_t b =
binfo.bucket;
422 T*
buf = buffers_[b].load(std::memory_order_relaxed);
432 size_.store(0, std::memory_order_release);
441 auto binfo = bucketAndSubIndex(size_.load(std::memory_order_relaxed));
446 for (
size_t b =
startBucket; b < kMaxBuffers; ++b) {
447 T*
ptr = buffers_[b].load(std::memory_order_relaxed);
451 if (buffers_.shouldDealloc(b)) {
454 buffers_[b].store(
nullptr, std::memory_order_release);
464 cv::dealloc<T>(buffers_[0].
load(std::memory_order_acquire));
474 auto it = insertPartial(
pos);
486 auto it = insertPartial(
pos);
513 typename =
typename std::iterator_traits<InputIt>::difference_type,
514 typename =
typename std::iterator_traits<InputIt>::pointer,
515 typename =
typename std::iterator_traits<InputIt>::reference,
516 typename =
typename std::iterator_traits<InputIt>::value_type,
517 typename =
typename std::iterator_traits<InputIt>::iterator_category>
546 size_.fetch_sub(1, std::memory_order_relaxed);
555 return std::move(
pos + 1, const_iterator(
e),
it);
584 size_.fetch_sub(
len, std::memory_order_relaxed);
611 template <
typename...
Args>
613 auto index = size_.fetch_add(1, std::memory_order_relaxed);
616 buffers_.allocAsNecessary(
binfo);
620 if (Traits::kIteratorPreferSpeed) {
621 new (&*
ret)
T(std::forward<Args>(
args)...);
623 new (buffers_[
binfo.bucket] +
binfo.bucketIndex)
T(std::forward<Args>(
args)...);
636 template <
typename Gen>
638 iterator
ret = growByUninitialized(
delta);
652 iterator
ret = growByUninitialized(
delta);
663 iterator
ret = growByUninitialized(
delta);
676 typename =
typename std::iterator_traits<It>::difference_type,
677 typename =
typename std::iterator_traits<It>::pointer,
678 typename =
typename std::iterator_traits<It>::reference,
679 typename =
typename std::iterator_traits<It>::value_type,
680 typename =
typename std::iterator_traits<It>::iterator_category>
682 iterator
ret = growByUninitialized(std::distance(start,
end));
683 internalInit(start,
end,
ret);
703 size_t curSize = size_.load(std::memory_order_relaxed);
707 return {
this,
n - 1, bucketAndSubIndex(
n - 1)};
718 size_t curSize = size_.load(std::memory_order_relaxed);
722 return {
this,
n - 1, bucketAndSubIndex(
n - 1)};
729 auto binfo = bucketAndSubIndex(size_.fetch_sub(1, std::memory_order_relaxed) - 1);
730 T*
elt = buffers_[
binfo.bucket].load(std::memory_order_relaxed) +
binfo.bucketIndex;
743 auto binfo = bucketAndSubIndexForIndex(
index);
744 T*
buf = buffers_[
binfo.bucket].load(std::memory_order_relaxed);
757 auto binfo = bucketAndSubIndexForIndex(
index);
758 T*
buf = buffers_[
binfo.bucket].load(std::memory_order_relaxed);
772#if defined(__cpp_exceptions)
773 if (
index >= size_.load(std::memory_order_relaxed)) {
774 throw std::out_of_range(
"Index too large");
790#if defined(__cpp_exceptions)
791 if (
index >= size_.load(std::memory_order_relaxed)) {
792 throw std::out_of_range(
"Index too large");
806 return {
this, 0, {0, 0, firstBucketLen_}};
818 size_t curSize = size_.load(std::memory_order_relaxed);
831 return {
this, 0, {0, 0, firstBucketLen_}};
842 const_iterator
end()
const {
843 size_t curSize = size_.load(std::memory_order_relaxed);
856 return {
this, 0, {0, 0, firstBucketLen_}};
868 size_t curSize = size_.load(std::memory_order_relaxed);
882 return reverse_iterator(
end());
893 return reverse_iterator(
begin());
905 return const_reverse_iterator(
cend());
915 const_reverse_iterator
rend()
const {
916 return const_reverse_iterator(
cbegin());
925 return size_.load(std::memory_order_relaxed) == 0;
934 return Traits::kMaxVectorSize;
943 return size_.load(std::memory_order_relaxed);
951 return *buffers_[0].load(std::memory_order_relaxed);
959 return *buffers_[0].load(std::memory_order_relaxed);
986 swap(firstBucketShift_,
oth.firstBucketShift_);
987 swap(firstBucketLen_,
oth.firstBucketLen_);
988 size_t othlen =
oth.size_.load(std::memory_order_relaxed);
989 oth.size_.store(size_.load(std::memory_order_relaxed), std::memory_order_relaxed);
990 size_.store(
othlen, std::memory_order_relaxed);
993 buffers_ = std::move(
oth.buffers_);
997 DISPENSO_INLINE cv::BucketInfo bucketAndSubIndexForIndex(
size_t index)
const {
998#if defined(__clang__)
999 if (
index < firstBucketLen_) {
1000 return {0,
index, firstBucketLen_};
1003 size_t l2idx = detail::log2(index);
1004 size_t bucket = (l2idx + 1) - firstBucketShift_;
1005 size_t bucketCapacity =
size_t{1} << l2idx;
1006 size_t bucketIndex = index - bucketCapacity;
1008 return {bucket, bucketIndex, bucketCapacity};
1010 return bucketAndSubIndex(index);
1014 DISPENSO_INLINE cv::BucketInfo bucketAndSubIndex(
size_t index)
const {
1015 size_t l2idx = detail::log2(index | 1);
1016 size_t bucket = (l2idx + 1) - firstBucketShift_;
1017 size_t bucketCapacity =
size_t{1} << l2idx;
1018 size_t bucketIndex = index - bucketCapacity;
1020 bucket = index < firstBucketLen_ ? 0 : bucket;
1021 bucketIndex = index < firstBucketLen_ ? index : bucketIndex;
1022 bucketCapacity = index < firstBucketLen_ ? firstBucketLen_ : bucketCapacity;
1024 return {bucket, bucketIndex, bucketCapacity};
1027 template <
typename InIterator>
1028 void internalInit(InIterator start, InIterator
end, iterator it) {
1029 while (start !=
end) {
1030 new (&*it) T(*start);
1036 void internalFillN(iterator it,
size_t len,
const T& value) {
1037 for (; len--; ++it) {
1038 new (&*it) T(value);
1042 void internalFillDefaultN(iterator it,
size_t len) {
1043 for (; len--; ++it) {
1048 iterator growByUninitialized(size_type delta) {
1049 auto index = size_.fetch_add(delta, std::memory_order_relaxed);
1050 auto binfo = bucketAndSubIndex(index);
1051 buffers_.allocAsNecessary(binfo, delta, bucketAndSubIndex(index + delta));
1052 return {
this, index, binfo};
1055 iterator insertPartial(const_iterator pos) {
1057 auto index = size_.fetch_add(1, std::memory_order_relaxed);
1058 auto binfo = bucketAndSubIndex(index);
1059 buffers_.allocAsNecessary(binfo);
1061 return std::move_backward(pos, const_iterator(e), e + 1) - 1;
1064 iterator insertPartial(const_iterator pos,
size_t len) {
1066 auto index = size_.fetch_add(len, std::memory_order_relaxed);
1067 buffers_.allocAsNecessary(bucketAndSubIndex(index), len, bucketAndSubIndex(index + len));
1068 for (
auto it = e + len; it != e;) {
1072 return std::move_backward(pos, const_iterator(e), e + len) - len;
1075 alignas(kCacheLineSize) cv::ConVecBuffer<
1077 SizeTraits::kDefaultCapacity / 2,
1078 SizeTraits::kMaxVectorSize,
1079 Traits::kPreferBuffersInline,
1080 Traits::kReallocStrategy> buffers_;
1081 static constexpr size_t kMaxBuffers =
decltype(buffers_)::kMaxBuffers;
1083 size_t firstBucketShift_;
1084 size_t firstBucketLen_;
1086 alignas(kCacheLineSize) std::atomic<size_t> size_{0};
1089 friend class cv::ConcurrentVectorIterator<
ConcurrentVector<T, Traits>, T, false>;
1090 friend class cv::ConcurrentVectorIterator<
ConcurrentVector<T, Traits>, T, true>;
1093template <
typename T,
class Traits1,
class Traits2>
1094inline bool operator==(
1095 const ConcurrentVector<T, Traits1>& a,
1096 const ConcurrentVector<T, Traits2>& b) {
1097 return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
1100template <
typename T,
class Traits1,
class Traits2>
1101inline bool operator!=(
1102 const ConcurrentVector<T, Traits1>& a,
1103 const ConcurrentVector<T, Traits2>& b) {
1107template <
typename T,
class Traits1,
class Traits2>
1108inline bool operator<(
1109 const ConcurrentVector<T, Traits1>& a,
1110 const ConcurrentVector<T, Traits2>& b) {
1111 return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
1114template <
typename T,
class Traits1,
class Traits2>
1115inline bool operator>(
1116 const ConcurrentVector<T, Traits1>& a,
1117 const ConcurrentVector<T, Traits2>& b) {
1121template <
typename T,
class Traits1,
class Traits2>
1122inline bool operator<=(
1123 const ConcurrentVector<T, Traits1>& a,
1124 const ConcurrentVector<T, Traits2>& b) {
1128template <
typename T,
class Traits1,
class Traits2>
1129inline bool operator>=(
1130 const ConcurrentVector<T, Traits1>& a,
1131 const ConcurrentVector<T, Traits2>& b) {
1135template <
typename T,
class Traits>
1136inline void swap(ConcurrentVector<T, Traits>& a, ConcurrentVector<T, Traits>& b) {
1141#include <dispenso/detail/concurrent_vector_impl2.h>
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 push_back(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)
size_type capacity() const
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)
reverse_iterator rbegin()
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
detail::OpResult< T > OpResult
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.