File indexing completed on 2026-08-10 09:29:29
0001
0002
0003
0004
0005
0006
0007 #if 0
0008 #pragma qt_sync_skip_header_check
0009 #pragma qt_sync_stop_processing
0010 #endif
0011
0012 #ifndef QCONTAINERTOOLS_IMPL_H
0013 #define QCONTAINERTOOLS_IMPL_H
0014
0015 #include <QtCore/qglobal.h>
0016 #include <QtCore/qtypeinfo.h>
0017
0018 #include <QtCore/qxptype_traits.h>
0019
0020 #include <cstring>
0021 #include <iterator>
0022 #include <memory>
0023 #include <algorithm>
0024
0025 QT_BEGIN_NAMESPACE
0026
0027 namespace QtPrivate
0028 {
0029
0030
0031
0032
0033
0034
0035
0036 template<typename T, typename Cmp = std::less<>>
0037 static constexpr bool q_points_into_range(const T *p, const T *b, const T *e,
0038 Cmp less = {}) noexcept
0039 {
0040 return !less(p, b) && less(p, e);
0041 }
0042
0043
0044
0045
0046
0047
0048
0049 template <typename C, typename T>
0050 static constexpr bool q_points_into_range(const T &p, const C &c) noexcept
0051 {
0052 static_assert(std::is_same_v<decltype(std::data(c)), T>);
0053
0054
0055
0056 return q_points_into_range(p, std::data(c),
0057 std::data(c) + std::distance(std::begin(c), std::end(c)));
0058 }
0059
0060 QT_WARNING_PUSH
0061 QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
0062
0063 template <typename T, typename N>
0064 void q_uninitialized_move_if_noexcept_n(T* first, N n, T* out)
0065 {
0066 if constexpr (std::is_nothrow_move_constructible_v<T> || !std::is_copy_constructible_v<T>)
0067 std::uninitialized_move_n(first, n, out);
0068 else
0069 std::uninitialized_copy_n(first, n, out);
0070 }
0071
0072 template <typename T, typename N>
0073 void q_uninitialized_relocate_n(T* first, N n, T* out)
0074 {
0075 if constexpr (QTypeInfo<T>::isRelocatable) {
0076 static_assert(std::is_copy_constructible_v<T> || std::is_move_constructible_v<T>,
0077 "Refusing to relocate this non-copy/non-move-constructible type.");
0078 if (n != N(0)) {
0079 std::memcpy(static_cast<void *>(out),
0080 static_cast<const void *>(first),
0081 n * sizeof(T));
0082 }
0083 } else {
0084 q_uninitialized_move_if_noexcept_n(first, n, out);
0085 if constexpr (QTypeInfo<T>::isComplex)
0086 std::destroy_n(first, n);
0087 }
0088 }
0089
0090 QT_WARNING_POP
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 template <typename T>
0102 void q_rotate(T *first, T *mid, T *last)
0103 {
0104 if constexpr (QTypeInfo<T>::isRelocatable) {
0105 const auto cast = [](T *p) { return reinterpret_cast<uchar*>(p); };
0106 std::rotate(cast(first), cast(mid), cast(last));
0107 } else {
0108 std::rotate(first, mid, last);
0109 }
0110 }
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124 template <typename T, typename Predicate>
0125 T *q_uninitialized_remove_copy_if(T *first, T *last, T *out, Predicate &pred)
0126 {
0127 static_assert(std::is_nothrow_destructible_v<T>,
0128 "This algorithm requires that T has a non-throwing destructor");
0129 Q_ASSERT(!q_points_into_range(out, first, last));
0130
0131 T *dest_begin = out;
0132 QT_TRY {
0133 while (first != last) {
0134 if (!pred(*first)) {
0135 new (std::addressof(*out)) T(*first);
0136 ++out;
0137 }
0138 ++first;
0139 }
0140 } QT_CATCH (...) {
0141 std::destroy(std::reverse_iterator(out), std::reverse_iterator(dest_begin));
0142 QT_RETHROW;
0143 }
0144 return out;
0145 }
0146
0147 template<typename iterator, typename N>
0148 void q_relocate_overlap_n_left_move(iterator first, N n, iterator d_first)
0149 {
0150
0151
0152
0153
0154
0155 Q_ASSERT(n);
0156 Q_ASSERT(d_first < first);
0157 using T = typename std::iterator_traits<iterator>::value_type;
0158
0159
0160
0161
0162
0163
0164
0165
0166 struct Destructor
0167 {
0168 iterator *iter;
0169 iterator end;
0170 iterator intermediate;
0171
0172 Destructor(iterator &it) noexcept : iter(std::addressof(it)), end(it) { }
0173 void commit() noexcept { iter = std::addressof(end); }
0174 void freeze() noexcept
0175 {
0176 intermediate = *iter;
0177 iter = std::addressof(intermediate);
0178 }
0179 ~Destructor() noexcept
0180 {
0181 for (const int step = *iter < end ? 1 : -1; *iter != end;) {
0182 std::advance(*iter, step);
0183 (*iter)->~T();
0184 }
0185 }
0186 } destroyer(d_first);
0187
0188 const iterator d_last = d_first + n;
0189
0190
0191
0192
0193 auto pair = std::minmax(d_last, first);
0194
0195
0196
0197 iterator overlapBegin = pair.first;
0198 iterator overlapEnd = pair.second;
0199
0200
0201 while (d_first != overlapBegin) {
0202
0203 new (std::addressof(*d_first)) T(std::move_if_noexcept(*first));
0204 ++d_first;
0205 ++first;
0206 }
0207
0208
0209
0210 destroyer.freeze();
0211
0212
0213 while (d_first != d_last) {
0214 *d_first = std::move_if_noexcept(*first);
0215 ++d_first;
0216 ++first;
0217 }
0218
0219 Q_ASSERT(d_first == destroyer.end + n);
0220 destroyer.commit();
0221
0222 while (first != overlapEnd)
0223 (--first)->~T();
0224 }
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236 template<typename T, typename N>
0237 void q_relocate_overlap_n(T *first, N n, T *d_first)
0238 {
0239 static_assert(std::is_nothrow_destructible_v<T>,
0240 "This algorithm requires that T has a non-throwing destructor");
0241
0242 if (n == N(0) || first == d_first || first == nullptr || d_first == nullptr)
0243 return;
0244
0245 if constexpr (QTypeInfo<T>::isRelocatable) {
0246 std::memmove(static_cast<void *>(d_first), static_cast<const void *>(first), n * sizeof(T));
0247 } else {
0248 if (d_first < first) {
0249 q_relocate_overlap_n_left_move(first, n, d_first);
0250 } else {
0251 auto rfirst = std::make_reverse_iterator(first + n);
0252 auto rd_first = std::make_reverse_iterator(d_first + n);
0253 q_relocate_overlap_n_left_move(rfirst, n, rd_first);
0254 }
0255 }
0256 }
0257
0258 template <typename T>
0259 struct ArrowProxy
0260 {
0261 T t;
0262 T *operator->() noexcept { return &t; }
0263 };
0264
0265 template <typename Iterator>
0266 using IfIsInputIterator = typename std::enable_if<
0267 std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::input_iterator_tag>::value,
0268 bool>::type;
0269
0270 template <typename Iterator>
0271 using IfIsForwardIterator = typename std::enable_if<
0272 std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
0273 bool>::type;
0274
0275 template <typename Iterator>
0276 using IfIsNotForwardIterator = typename std::enable_if<
0277 !std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
0278 bool>::type;
0279
0280 template <typename Container,
0281 typename InputIterator,
0282 IfIsNotForwardIterator<InputIterator> = true>
0283 void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
0284 {
0285 }
0286
0287 template <typename Container,
0288 typename ForwardIterator,
0289 IfIsForwardIterator<ForwardIterator> = true>
0290 void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l)
0291 {
0292 c->reserve(static_cast<typename Container::size_type>(std::distance(f, l)));
0293 }
0294
0295 template <typename Iterator>
0296 using KeyAndValueTest = decltype(
0297 std::declval<Iterator &>().key(),
0298 std::declval<Iterator &>().value()
0299 );
0300
0301 template <typename Iterator>
0302 using FirstAndSecondTest = decltype(
0303 (*std::declval<Iterator &>()).first,
0304 (*std::declval<Iterator &>()).second
0305 );
0306
0307 template <typename Iterator>
0308 using IfAssociativeIteratorHasKeyAndValue =
0309 std::enable_if_t<qxp::is_detected_v<KeyAndValueTest, Iterator>, bool>;
0310
0311 template <typename Iterator>
0312 using IfAssociativeIteratorHasFirstAndSecond =
0313 std::enable_if_t<
0314 std::conjunction_v<
0315 std::negation<qxp::is_detected<KeyAndValueTest, Iterator>>,
0316 qxp::is_detected<FirstAndSecondTest, Iterator>
0317 >, bool>;
0318
0319 template <typename Iterator>
0320 using MoveBackwardsTest = decltype(
0321 std::declval<Iterator &>().operator--()
0322 );
0323
0324 template <typename Iterator>
0325 using IfIteratorCanMoveBackwards =
0326 std::enable_if_t<qxp::is_detected_v<MoveBackwardsTest, Iterator>, bool>;
0327
0328 template <typename T, typename U>
0329 using IfIsNotSame =
0330 typename std::enable_if<!std::is_same<T, U>::value, bool>::type;
0331
0332 template<typename T, typename U>
0333 using IfIsNotConvertible = typename std::enable_if<!std::is_convertible<T, U>::value, bool>::type;
0334
0335 template <typename Container, typename Predicate>
0336 auto sequential_erase_if(Container &c, Predicate &pred)
0337 {
0338
0339
0340
0341
0342
0343 const auto cbegin = c.cbegin();
0344 const auto cend = c.cend();
0345 const auto t_it = std::find_if(cbegin, cend, pred);
0346 auto result = std::distance(cbegin, t_it);
0347 if (result == c.size())
0348 return result - result;
0349
0350
0351 const auto e = c.end();
0352
0353 auto it = std::next(c.begin(), result);
0354 auto dest = it;
0355
0356
0357
0358
0359
0360 while (++it != e) {
0361 if (!pred(*it)) {
0362 *dest = std::move(*it);
0363 ++dest;
0364 }
0365 }
0366
0367 result = std::distance(dest, e);
0368 c.erase(dest, e);
0369 return result;
0370 }
0371
0372 template <typename Container, typename T>
0373 auto sequential_erase(Container &c, const T &t)
0374 {
0375
0376 auto cmp = [&](const auto &e) -> bool { return e == t; };
0377 return sequential_erase_if(c, cmp);
0378 }
0379
0380 template <typename Container, typename T>
0381 auto sequential_erase_with_copy(Container &c, const T &t)
0382 {
0383 using CopyProxy = std::conditional_t<std::is_copy_constructible_v<T>, T, const T &>;
0384 return sequential_erase(c, CopyProxy(t));
0385 }
0386
0387 template <typename Container, typename T>
0388 auto sequential_erase_one(Container &c, const T &t)
0389 {
0390 const auto cend = c.cend();
0391 const auto it = std::find(c.cbegin(), cend, t);
0392 if (it == cend)
0393 return false;
0394 c.erase(it);
0395 return true;
0396 }
0397
0398 template <typename T, typename Predicate>
0399 qsizetype qset_erase_if(QSet<T> &set, Predicate &pred)
0400 {
0401 qsizetype result = 0;
0402 auto it = set.cbegin();
0403 auto e = set.cend();
0404 while (it != e) {
0405 if (pred(*it)) {
0406 ++result;
0407 it = set.erase(it);
0408 e = set.cend();
0409 } else {
0410 ++it;
0411 }
0412 }
0413 return result;
0414 }
0415
0416
0417
0418 template <typename R, typename F, typename ... ArgTypes>
0419 struct is_invoke_result_explicitly_convertible : std::is_constructible<R, std::invoke_result_t<F, ArgTypes...>>
0420 {};
0421
0422
0423
0424 template <typename R, typename F, typename ... ArgTypes>
0425 constexpr bool is_invocable_explicit_r_v = std::conjunction_v<
0426 std::is_invocable<F, ArgTypes...>,
0427 is_invoke_result_explicitly_convertible<R, F, ArgTypes...>
0428 >;
0429
0430 template <typename Container, typename Predicate>
0431 auto associative_erase_if(Container &c, Predicate &pred)
0432 {
0433
0434
0435 using Iterator = typename Container::iterator;
0436 using Key = typename Container::key_type;
0437 using Value = typename Container::mapped_type;
0438 using KeyValuePair = std::pair<const Key &, Value &>;
0439
0440 typename Container::size_type result = 0;
0441
0442 auto it = c.begin();
0443 const auto e = c.end();
0444 while (it != e) {
0445 if constexpr (is_invocable_explicit_r_v<bool, Predicate &, Iterator &>) {
0446 if (pred(it)) {
0447 it = c.erase(it);
0448 ++result;
0449 } else {
0450 ++it;
0451 }
0452 } else if constexpr (is_invocable_explicit_r_v<bool, Predicate &, KeyValuePair &&>) {
0453 KeyValuePair p(it.key(), it.value());
0454 if (pred(std::move(p))) {
0455 it = c.erase(it);
0456 ++result;
0457 } else {
0458 ++it;
0459 }
0460 } else {
0461 static_assert(type_dependent_false<Container>(), "Predicate has an incompatible signature");
0462 }
0463 }
0464
0465 return result;
0466 }
0467
0468 }
0469
0470 QT_END_NAMESPACE
0471
0472 #endif