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