Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:07:20

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 
0006 #if 0
0007 #pragma qt_sync_skip_header_check
0008 #pragma qt_sync_stop_processing
0009 #endif
0010 
0011 #ifndef QCONTAINERTOOLS_IMPL_H
0012 #define QCONTAINERTOOLS_IMPL_H
0013 
0014 #include <QtCore/qglobal.h>
0015 #include <QtCore/qtypeinfo.h>
0016 
0017 #include <QtCore/qxptype_traits.h>
0018 
0019 #include <cstring>
0020 #include <iterator>
0021 #include <memory>
0022 #include <algorithm>
0023 
0024 QT_BEGIN_NAMESPACE
0025 
0026 namespace QtPrivate
0027 {
0028 
0029 /*!
0030   \internal
0031 
0032   Returns whether \a p is within a range [b, e). In simplest form equivalent to:
0033   b <= p < e.
0034 */
0035 template<typename T, typename Cmp = std::less<>>
0036 static constexpr bool q_points_into_range(const T *p, const T *b, const T *e,
0037                                           Cmp less = {}) noexcept
0038 {
0039     return !less(p, b) && less(p, e);
0040 }
0041 
0042 /*!
0043   \internal
0044 
0045   Returns whether \a p is within container \a c. In its simplest form equivalent to:
0046   c.data() <= p < c.data() + c.size()
0047 */
0048 template <typename C, typename T>
0049 static constexpr bool q_points_into_range(const T &p, const C &c) noexcept
0050 {
0051     static_assert(std::is_same_v<decltype(std::data(c)), T>);
0052 
0053     // std::distance because QArrayDataPointer has a "qsizetype size"
0054     // member but no size() function
0055     return q_points_into_range(p, std::data(c),
0056                                std::data(c) + std::distance(std::begin(c), std::end(c)));
0057 }
0058 
0059 QT_WARNING_PUSH
0060 QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
0061 
0062 template <typename T, typename N>
0063 void q_uninitialized_move_if_noexcept_n(T* first, N n, T* out)
0064 {
0065     if constexpr (std::is_nothrow_move_constructible_v<T> || !std::is_copy_constructible_v<T>)
0066         std::uninitialized_move_n(first, n, out);
0067     else
0068         std::uninitialized_copy_n(first, n, out);
0069 }
0070 
0071 template <typename T, typename N>
0072 void q_uninitialized_relocate_n(T* first, N n, T* out)
0073 {
0074     if constexpr (QTypeInfo<T>::isRelocatable) {
0075         static_assert(std::is_copy_constructible_v<T> || std::is_move_constructible_v<T>,
0076                       "Refusing to relocate this non-copy/non-move-constructible type.");
0077         if (n != N(0)) { // even if N == 0, out == nullptr or first == nullptr are UB for memcpy()
0078             std::memcpy(static_cast<void *>(out),
0079                         static_cast<const void *>(first),
0080                         n * sizeof(T));
0081         }
0082     } else {
0083         q_uninitialized_move_if_noexcept_n(first, n, out);
0084         if constexpr (QTypeInfo<T>::isComplex)
0085             std::destroy_n(first, n);
0086     }
0087 }
0088 
0089 QT_WARNING_POP
0090 
0091 /*!
0092     \internal
0093 
0094     A wrapper around std::rotate(), with an optimization for
0095     Q_RELOCATABLE_TYPEs. We omit the return value, as it would be more work to
0096     compute in the Q_RELOCATABLE_TYPE case and, unlike std::rotate on
0097     ForwardIterators, callers can compute the result in constant time
0098     themselves.
0099 */
0100 template <typename T>
0101 void q_rotate(T *first, T *mid, T *last)
0102 {
0103     if constexpr (QTypeInfo<T>::isRelocatable) {
0104         const auto cast = [](T *p) { return reinterpret_cast<uchar*>(p); };
0105         std::rotate(cast(first), cast(mid), cast(last));
0106     } else {
0107         std::rotate(first, mid, last);
0108     }
0109 }
0110 
0111 /*!
0112     \internal
0113     Copies all elements, except the ones for which \a pred returns \c true, from
0114     range [first, last), to the uninitialized memory buffer starting at \a out.
0115 
0116     It's undefined behavior if \a out points into [first, last).
0117 
0118     Returns a pointer one past the last copied element.
0119 
0120     If an exception is thrown, all the already copied elements in the destination
0121     buffer are destroyed.
0122 */
0123 template <typename T, typename Predicate>
0124 T *q_uninitialized_remove_copy_if(T *first, T *last, T *out, Predicate &pred)
0125 {
0126     static_assert(std::is_nothrow_destructible_v<T>,
0127                   "This algorithm requires that T has a non-throwing destructor");
0128     Q_ASSERT(!q_points_into_range(out, first, last));
0129 
0130     T *dest_begin = out;
0131     QT_TRY {
0132         while (first != last) {
0133             if (!pred(*first)) {
0134                 new (std::addressof(*out)) T(*first);
0135                 ++out;
0136             }
0137             ++first;
0138         }
0139     } QT_CATCH (...) {
0140         std::destroy(std::reverse_iterator(out), std::reverse_iterator(dest_begin));
0141         QT_RETHROW;
0142     }
0143     return out;
0144 }
0145 
0146 template<typename iterator, typename N>
0147 void q_relocate_overlap_n_left_move(iterator first, N n, iterator d_first)
0148 {
0149     // requires: [first, n) is a valid range
0150     // requires: d_first + n is reachable from d_first
0151     // requires: iterator is at least a random access iterator
0152     // requires: value_type(iterator) has a non-throwing destructor
0153 
0154     Q_ASSERT(n);
0155     Q_ASSERT(d_first < first); // only allow moves to the "left"
0156     using T = typename std::iterator_traits<iterator>::value_type;
0157 
0158     // Watches passed iterator. Unless commit() is called, all the elements that
0159     // the watched iterator passes through are deleted at the end of object
0160     // lifetime. freeze() could be used to stop watching the passed iterator and
0161     // remain at current place.
0162     //
0163     // requires: the iterator is expected to always point to an invalid object
0164     //           (to uninitialized memory)
0165     struct Destructor
0166     {
0167         iterator *iter;
0168         iterator end;
0169         iterator intermediate;
0170 
0171         Destructor(iterator &it) noexcept : iter(std::addressof(it)), end(it) { }
0172         void commit() noexcept { iter = std::addressof(end); }
0173         void freeze() noexcept
0174         {
0175             intermediate = *iter;
0176             iter = std::addressof(intermediate);
0177         }
0178         ~Destructor() noexcept
0179         {
0180             for (const int step = *iter < end ? 1 : -1; *iter != end;) {
0181                 std::advance(*iter, step);
0182                 (*iter)->~T();
0183             }
0184         }
0185     } destroyer(d_first);
0186 
0187     const iterator d_last = d_first + n;
0188     // Note: use pair and explicitly copy iterators from it to prevent
0189     // accidental reference semantics instead of copy. equivalent to:
0190     //
0191     // auto [overlapBegin, overlapEnd] = std::minmax(d_last, first);
0192     auto pair = std::minmax(d_last, first);
0193 
0194     // overlap area between [d_first, d_first + n) and [first, first + n) or an
0195     // uninitialized memory area between the two ranges
0196     iterator overlapBegin = pair.first;
0197     iterator overlapEnd = pair.second;
0198 
0199     // move construct elements in uninitialized region
0200     while (d_first != overlapBegin) {
0201         // account for std::reverse_iterator, cannot use new(d_first) directly
0202         new (std::addressof(*d_first)) T(std::move_if_noexcept(*first));
0203         ++d_first;
0204         ++first;
0205     }
0206 
0207     // cannot commit but have to stop - there might be an overlap region
0208     // which we don't want to delete (because it's part of existing data)
0209     destroyer.freeze();
0210 
0211     // move assign elements in overlap region
0212     while (d_first != d_last) {
0213         *d_first = std::move_if_noexcept(*first);
0214         ++d_first;
0215         ++first;
0216     }
0217 
0218     Q_ASSERT(d_first == destroyer.end + n);
0219     destroyer.commit(); // can commit here as ~T() below does not throw
0220 
0221     while (first != overlapEnd)
0222         (--first)->~T();
0223 }
0224 
0225 /*!
0226   \internal
0227 
0228   Relocates a range [first, n) to [d_first, n) taking care of potential memory
0229   overlaps. This is a generic equivalent of memmove.
0230 
0231   If an exception is thrown during the relocation, all the relocated elements
0232   are destroyed and [first, n) may contain valid but unspecified values,
0233   including moved-from values (basic exception safety).
0234 */
0235 template<typename T, typename N>
0236 void q_relocate_overlap_n(T *first, N n, T *d_first)
0237 {
0238     static_assert(std::is_nothrow_destructible_v<T>,
0239                   "This algorithm requires that T has a non-throwing destructor");
0240 
0241     if (n == N(0) || first == d_first || first == nullptr || d_first == nullptr)
0242         return;
0243 
0244     if constexpr (QTypeInfo<T>::isRelocatable) {
0245         std::memmove(static_cast<void *>(d_first), static_cast<const void *>(first), n * sizeof(T));
0246     } else { // generic version has to be used
0247         if (d_first < first) {
0248             q_relocate_overlap_n_left_move(first, n, d_first);
0249         } else { // first < d_first
0250             auto rfirst = std::make_reverse_iterator(first + n);
0251             auto rd_first = std::make_reverse_iterator(d_first + n);
0252             q_relocate_overlap_n_left_move(rfirst, n, rd_first);
0253         }
0254     }
0255 }
0256 
0257 template <typename Iterator>
0258 using IfIsInputIterator = typename std::enable_if<
0259     std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::input_iterator_tag>::value,
0260     bool>::type;
0261 
0262 template <typename Iterator>
0263 using IfIsForwardIterator = typename std::enable_if<
0264     std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
0265     bool>::type;
0266 
0267 template <typename Iterator>
0268 using IfIsNotForwardIterator = typename std::enable_if<
0269     !std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
0270     bool>::type;
0271 
0272 template <typename Container,
0273           typename InputIterator,
0274           IfIsNotForwardIterator<InputIterator> = true>
0275 void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
0276 {
0277 }
0278 
0279 template <typename Container,
0280           typename ForwardIterator,
0281           IfIsForwardIterator<ForwardIterator> = true>
0282 void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l)
0283 {
0284     c->reserve(static_cast<typename Container::size_type>(std::distance(f, l)));
0285 }
0286 
0287 template <typename Iterator>
0288 using KeyAndValueTest = decltype(
0289     std::declval<Iterator &>().key(),
0290     std::declval<Iterator &>().value()
0291 );
0292 
0293 template <typename Iterator>
0294 using FirstAndSecondTest = decltype(
0295     std::declval<Iterator &>()->first,
0296     std::declval<Iterator &>()->second
0297 );
0298 
0299 template <typename Iterator>
0300 using IfAssociativeIteratorHasKeyAndValue =
0301     std::enable_if_t<qxp::is_detected_v<KeyAndValueTest, Iterator>, bool>;
0302 
0303 template <typename Iterator>
0304 using IfAssociativeIteratorHasFirstAndSecond =
0305     std::enable_if_t<
0306         std::conjunction_v<
0307             std::negation<qxp::is_detected<KeyAndValueTest, Iterator>>,
0308             qxp::is_detected<FirstAndSecondTest, Iterator>
0309         >, bool>;
0310 
0311 template <typename Iterator>
0312 using MoveBackwardsTest = decltype(
0313     std::declval<Iterator &>().operator--()
0314 );
0315 
0316 template <typename Iterator>
0317 using IfIteratorCanMoveBackwards =
0318     std::enable_if_t<qxp::is_detected_v<MoveBackwardsTest, Iterator>, bool>;
0319 
0320 template <typename T, typename U>
0321 using IfIsNotSame =
0322     typename std::enable_if<!std::is_same<T, U>::value, bool>::type;
0323 
0324 template<typename T, typename U>
0325 using IfIsNotConvertible = typename std::enable_if<!std::is_convertible<T, U>::value, bool>::type;
0326 
0327 template <typename Container, typename Predicate>
0328 auto sequential_erase_if(Container &c, Predicate &pred)
0329 {
0330     // This is remove_if() modified to perform the find_if step on
0331     // const_iterators to avoid shared container detaches if nothing needs to
0332     // be removed. We cannot run remove_if after find_if: doing so would apply
0333     // the predicate to the first matching element twice!
0334 
0335     const auto cbegin = c.cbegin();
0336     const auto cend = c.cend();
0337     const auto t_it = std::find_if(cbegin, cend, pred);
0338     auto result = std::distance(cbegin, t_it);
0339     if (result == c.size())
0340         return result - result; // `0` of the right type
0341 
0342     // now detach:
0343     const auto e = c.end();
0344 
0345     auto it = std::next(c.begin(), result);
0346     auto dest = it;
0347 
0348     // Loop Invariants:
0349     // - it != e
0350     // - [next(it), e[ still to be checked
0351     // - [c.begin(), dest[ are result
0352     while (++it != e) {
0353         if (!pred(*it)) {
0354             *dest = std::move(*it);
0355             ++dest;
0356         }
0357     }
0358 
0359     result = std::distance(dest, e);
0360     c.erase(dest, e);
0361     return result;
0362 }
0363 
0364 template <typename Container, typename T>
0365 auto sequential_erase(Container &c, const T &t)
0366 {
0367     // use the equivalence relation from http://eel.is/c++draft/list.erasure#1
0368     auto cmp = [&](auto &e) { return e == t; };
0369     return sequential_erase_if(c, cmp); // can't pass rvalues!
0370 }
0371 
0372 template <typename Container, typename T>
0373 auto sequential_erase_with_copy(Container &c, const T &t)
0374 {
0375     using CopyProxy = std::conditional_t<std::is_copy_constructible_v<T>, T, const T &>;
0376     return sequential_erase(c, CopyProxy(t));
0377 }
0378 
0379 template <typename Container, typename T>
0380 auto sequential_erase_one(Container &c, const T &t)
0381 {
0382     const auto cend = c.cend();
0383     const auto it = std::find(c.cbegin(), cend, t);
0384     if (it == cend)
0385         return false;
0386     c.erase(it);
0387     return true;
0388 }
0389 
0390 template <typename T, typename Predicate>
0391 qsizetype qset_erase_if(QSet<T> &set, Predicate &pred)
0392 {
0393     qsizetype result = 0;
0394     auto it = set.begin();
0395     const auto e = set.end();
0396     while (it != e) {
0397         if (pred(*it)) {
0398             ++result;
0399             it = set.erase(it);
0400         } else {
0401             ++it;
0402         }
0403     }
0404     return result;
0405 }
0406 
0407 
0408 // Prerequisite: F is invocable on ArgTypes
0409 template <typename R, typename F, typename ... ArgTypes>
0410 struct is_invoke_result_explicitly_convertible : std::is_constructible<R, std::invoke_result_t<F, ArgTypes...>>
0411 {};
0412 
0413 // is_invocable_r checks for implicit conversions, but we need to check
0414 // for explicit conversions in remove_if. So, roll our own trait.
0415 template <typename R, typename F, typename ... ArgTypes>
0416 constexpr bool is_invocable_explicit_r_v = std::conjunction_v<
0417     std::is_invocable<F, ArgTypes...>,
0418     is_invoke_result_explicitly_convertible<R, F, ArgTypes...>
0419 >;
0420 
0421 template <typename Container, typename Predicate>
0422 auto associative_erase_if(Container &c, Predicate &pred)
0423 {
0424     // we support predicates callable with either Container::iterator
0425     // or with std::pair<const Key &, Value &>
0426     using Iterator = typename Container::iterator;
0427     using Key = typename Container::key_type;
0428     using Value = typename Container::mapped_type;
0429     using KeyValuePair = std::pair<const Key &, Value &>;
0430 
0431     typename Container::size_type result = 0;
0432 
0433     auto it = c.begin();
0434     const auto e = c.end();
0435     while (it != e) {
0436         if constexpr (is_invocable_explicit_r_v<bool, Predicate &, Iterator &>) {
0437             if (pred(it)) {
0438                 it = c.erase(it);
0439                 ++result;
0440             } else {
0441                 ++it;
0442             }
0443         } else if constexpr (is_invocable_explicit_r_v<bool, Predicate &, KeyValuePair &&>) {
0444             KeyValuePair p(it.key(), it.value());
0445             if (pred(std::move(p))) {
0446                 it = c.erase(it);
0447                 ++result;
0448             } else {
0449                 ++it;
0450             }
0451         } else {
0452             static_assert(sizeof(Container) == 0, "Predicate has an incompatible signature");
0453         }
0454     }
0455 
0456     return result;
0457 }
0458 
0459 } // namespace QtPrivate
0460 
0461 QT_END_NAMESPACE
0462 
0463 #endif // QCONTAINERTOOLS_IMPL_H