Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-10 09:29:29

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         if (n != N(0)) { // even if N == 0, out == nullptr or first == nullptr are UB for memcpy()
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     \internal
0094 
0095     A wrapper around std::rotate(), with an optimization for
0096     Q_RELOCATABLE_TYPEs. We omit the return value, as it would be more work to
0097     compute in the Q_RELOCATABLE_TYPE case and, unlike std::rotate on
0098     ForwardIterators, callers can compute the result in constant time
0099     themselves.
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     \internal
0114     Copies all elements, except the ones for which \a pred returns \c true, from
0115     range [first, last), to the uninitialized memory buffer starting at \a out.
0116 
0117     It's undefined behavior if \a out points into [first, last).
0118 
0119     Returns a pointer one past the last copied element.
0120 
0121     If an exception is thrown, all the already copied elements in the destination
0122     buffer are destroyed.
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     // requires: [first, n) is a valid range
0151     // requires: d_first + n is reachable from d_first
0152     // requires: iterator is at least a random access iterator
0153     // requires: value_type(iterator) has a non-throwing destructor
0154 
0155     Q_ASSERT(n);
0156     Q_ASSERT(d_first < first); // only allow moves to the "left"
0157     using T = typename std::iterator_traits<iterator>::value_type;
0158 
0159     // Watches passed iterator. Unless commit() is called, all the elements that
0160     // the watched iterator passes through are deleted at the end of object
0161     // lifetime. freeze() could be used to stop watching the passed iterator and
0162     // remain at current place.
0163     //
0164     // requires: the iterator is expected to always point to an invalid object
0165     //           (to uninitialized memory)
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     // Note: use pair and explicitly copy iterators from it to prevent
0190     // accidental reference semantics instead of copy. equivalent to:
0191     //
0192     // auto [overlapBegin, overlapEnd] = std::minmax(d_last, first);
0193     auto pair = std::minmax(d_last, first);
0194 
0195     // overlap area between [d_first, d_first + n) and [first, first + n) or an
0196     // uninitialized memory area between the two ranges
0197     iterator overlapBegin = pair.first;
0198     iterator overlapEnd = pair.second;
0199 
0200     // move construct elements in uninitialized region
0201     while (d_first != overlapBegin) {
0202         // account for std::reverse_iterator, cannot use new(d_first) directly
0203         new (std::addressof(*d_first)) T(std::move_if_noexcept(*first));
0204         ++d_first;
0205         ++first;
0206     }
0207 
0208     // cannot commit but have to stop - there might be an overlap region
0209     // which we don't want to delete (because it's part of existing data)
0210     destroyer.freeze();
0211 
0212     // move assign elements in overlap region
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(); // can commit here as ~T() below does not throw
0221 
0222     while (first != overlapEnd)
0223         (--first)->~T();
0224 }
0225 
0226 /*!
0227   \internal
0228 
0229   Relocates a range [first, n) to [d_first, n) taking care of potential memory
0230   overlaps. This is a generic equivalent of memmove.
0231 
0232   If an exception is thrown during the relocation, all the relocated elements
0233   are destroyed and [first, n) may contain valid but unspecified values,
0234   including moved-from values (basic exception safety).
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 { // generic version has to be used
0248         if (d_first < first) {
0249             q_relocate_overlap_n_left_move(first, n, d_first);
0250         } else { // first < d_first
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     // This is remove_if() modified to perform the find_if step on
0339     // const_iterators to avoid shared container detaches if nothing needs to
0340     // be removed. We cannot run remove_if after find_if: doing so would apply
0341     // the predicate to the first matching element twice!
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; // `0` of the right type
0349 
0350     // now detach:
0351     const auto e = c.end();
0352 
0353     auto it = std::next(c.begin(), result);
0354     auto dest = it;
0355 
0356     // Loop Invariants:
0357     // - it != e
0358     // - [next(it), e[ still to be checked
0359     // - [c.begin(), dest[ are result
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     // use the equivalence relation from http://eel.is/c++draft/list.erasure#1
0376     auto cmp = [&](const auto &e) -> bool { return e == t; };
0377     return sequential_erase_if(c, cmp); // can't pass rvalues!
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(); // stable across detach (QHash::end() is a stateless sentinel)...
0404     while (it != e) {
0405         if (pred(*it)) {
0406             ++result;
0407             it = set.erase(it);
0408             e = set.cend(); // ...but re-set nonetheless, in case at some point it won't be
0409         } else {
0410             ++it;
0411         }
0412     }
0413     return result;
0414 }
0415 
0416 
0417 // Prerequisite: F is invocable on ArgTypes
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 // is_invocable_r checks for implicit conversions, but we need to check
0423 // for explicit conversions in remove_if. So, roll our own trait.
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     // we support predicates callable with either Container::iterator
0434     // or with std::pair<const Key &, Value &>
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 } // namespace QtPrivate
0469 
0470 QT_END_NAMESPACE
0471 
0472 #endif // QCONTAINERTOOLS_IMPL_H