Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-08 10:27:04

0001 // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
0002 // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
0003 // Copyright (C) 2020 The Qt Company Ltd.
0004 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0005 // Qt-Security score:significant reason:default
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   \internal
0032 
0033   Returns whether \a p is within a range [b, e). In simplest form equivalent to:
0034   b <= p < e.
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   \internal
0045 
0046   Returns whether \a p is within container \a c. In its simplest form equivalent to:
0047   c.data() <= p < c.data() + c.size()
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     // std::distance because QArrayDataPointer has a "qsizetype size"
0055     // member but no size() function
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         // Even if n == 0, out == nullptr or first == nullptr are UB for memcpy().
0079         // Negative n shouldn't happen, but the compiler can't see that and might warn about
0080         // memcpy being called with an out-of-range argument. Since we need to check for n == 0
0081         // anyway, check for n > 0 instead and assert below.
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     \internal
0100 
0101     A wrapper around std::rotate(), with an optimization for
0102     Q_RELOCATABLE_TYPEs. We omit the return value, as it would be more work to
0103     compute in the Q_RELOCATABLE_TYPE case and, unlike std::rotate on
0104     ForwardIterators, callers can compute the result in constant time
0105     themselves.
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     \internal
0120     Copies all elements, except the ones for which \a pred returns \c true, from
0121     range [first, last), to the uninitialized memory buffer starting at \a out.
0122 
0123     It's undefined behavior if \a out points into [first, last).
0124 
0125     Returns a pointer one past the last copied element.
0126 
0127     If an exception is thrown, all the already copied elements in the destination
0128     buffer are destroyed.
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     // requires: [first, n) is a valid range
0157     // requires: d_first + n is reachable from d_first
0158     // requires: iterator is at least a random access iterator
0159     // requires: value_type(iterator) has a non-throwing destructor
0160 
0161     Q_ASSERT(n);
0162     Q_ASSERT(d_first < first); // only allow moves to the "left"
0163     using T = typename std::iterator_traits<iterator>::value_type;
0164 
0165     // Watches passed iterator. Unless commit() is called, all the elements that
0166     // the watched iterator passes through are deleted at the end of object
0167     // lifetime. freeze() could be used to stop watching the passed iterator and
0168     // remain at current place.
0169     //
0170     // requires: the iterator is expected to always point to an invalid object
0171     //           (to uninitialized memory)
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     // Note: use pair and explicitly copy iterators from it to prevent
0196     // accidental reference semantics instead of copy. equivalent to:
0197     //
0198     // auto [overlapBegin, overlapEnd] = std::minmax(d_last, first);
0199     auto pair = std::minmax(d_last, first);
0200 
0201     // overlap area between [d_first, d_first + n) and [first, first + n) or an
0202     // uninitialized memory area between the two ranges
0203     iterator overlapBegin = pair.first;
0204     iterator overlapEnd = pair.second;
0205 
0206     // move construct elements in uninitialized region
0207     while (d_first != overlapBegin) {
0208         // account for std::reverse_iterator, cannot use new(d_first) directly
0209         new (std::addressof(*d_first)) T(std::move_if_noexcept(*first));
0210         ++d_first;
0211         ++first;
0212     }
0213 
0214     // cannot commit but have to stop - there might be an overlap region
0215     // which we don't want to delete (because it's part of existing data)
0216     destroyer.freeze();
0217 
0218     // move assign elements in overlap region
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(); // can commit here as ~T() below does not throw
0227 
0228     while (first != overlapEnd)
0229         (--first)->~T();
0230 }
0231 
0232 /*!
0233   \internal
0234 
0235   Relocates a range [first, n) to [d_first, n) taking care of potential memory
0236   overlaps. This is a generic equivalent of memmove.
0237 
0238   If an exception is thrown during the relocation, all the relocated elements
0239   are destroyed and [first, n) may contain valid but unspecified values,
0240   including moved-from values (basic exception safety).
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 { // generic version has to be used
0254         if (d_first < first) {
0255             q_relocate_overlap_n_left_move(first, n, d_first);
0256         } else { // first < d_first
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     // This is remove_if() modified to perform the find_if step on
0345     // const_iterators to avoid shared container detaches if nothing needs to
0346     // be removed. We cannot run remove_if after find_if: doing so would apply
0347     // the predicate to the first matching element twice!
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; // `0` of the right type
0355 
0356     // now detach:
0357     const auto e = c.end();
0358 
0359     auto it = std::next(c.begin(), result);
0360     auto dest = it;
0361 
0362     // Loop Invariants:
0363     // - it != e
0364     // - [next(it), e[ still to be checked
0365     // - [c.begin(), dest[ are result
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     // use the equivalence relation from http://eel.is/c++draft/list.erasure#1
0382     auto cmp = [&](const auto &e) -> bool { return e == t; };
0383     return sequential_erase_if(c, cmp); // can't pass rvalues!
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(); // stable across detach (QHash::end() is a stateless sentinel)...
0410     while (it != e) {
0411         if (pred(*it)) {
0412             ++result;
0413             it = set.erase(it);
0414             e = set.cend(); // ...but re-set nonetheless, in case at some point it won't be
0415         } else {
0416             ++it;
0417         }
0418     }
0419     return result;
0420 }
0421 
0422 
0423 // Prerequisite: F is invocable on ArgTypes
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 // is_invocable_r checks for implicit conversions, but we need to check
0429 // for explicit conversions in remove_if. So, roll our own trait.
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     // we support predicates callable with either Container::iterator
0440     // or with std::pair<const Key &, Value &>
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 } // namespace QtPrivate
0475 
0476 QT_END_NAMESPACE
0477 
0478 #endif // QCONTAINERTOOLS_IMPL_H