File indexing completed on 2026-09-05 09:19:20
0001
0002
0003
0004
0005 #ifndef QSPAN_H
0006 #define QSPAN_H
0007
0008 #include <QtCore/qcompilerdetection.h>
0009 #include <QtCore/qtypes.h>
0010 #include <QtCore/qcontainerfwd.h>
0011
0012 #include <array>
0013 #include <cstddef>
0014 #include <cassert>
0015 #include <initializer_list>
0016 #include <QtCore/q20iterator.h>
0017 #include <QtCore/q20memory.h>
0018 #ifdef __cpp_lib_span
0019 #include <span>
0020 #endif
0021 #include <QtCore/q20type_traits.h>
0022
0023 QT_BEGIN_NAMESPACE
0024
0025
0026 namespace q20 {
0027 inline constexpr auto dynamic_extent = std::size_t(-1);
0028 }
0029
0030 QT_BEGIN_INCLUDE_NAMESPACE
0031 #ifdef __cpp_lib_span
0032 #ifdef __cpp_lib_concepts
0033 namespace std::ranges {
0034
0035
0036
0037 template <typename T, std::size_t E>
0038 constexpr inline bool enable_borrowed_range<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
0039 template <typename T, std::size_t E>
0040 constexpr inline bool enable_view<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
0041 }
0042 #endif
0043 #endif
0044 QT_END_INCLUDE_NAMESPACE
0045
0046 namespace QSpanPrivate {
0047
0048 template <typename From, typename To>
0049 std::conditional_t<std::is_const_v<From>, const To &, To &>
0050 const_propagated(To &in) { return in; }
0051
0052 template <typename T, std::size_t E> class QSpanBase;
0053
0054 template <typename T>
0055 struct is_qspan_helper : std::false_type {};
0056 template <typename T, std::size_t E>
0057 struct is_qspan_helper<QSpan<T, E>> : std::true_type {};
0058 template <typename T, std::size_t E>
0059 struct is_qspan_helper<QSpanBase<T, E>> : std::true_type {};
0060 template <typename T>
0061 using is_qspan = is_qspan_helper<q20::remove_cvref_t<T>>;
0062
0063 template <typename T>
0064 struct is_std_span_helper : std::false_type {};
0065 #ifdef __cpp_lib_span
0066 template <typename T, std::size_t E>
0067 struct is_std_span_helper<std::span<T, E>> : std::true_type {};
0068 #endif
0069 template <typename T>
0070 using is_std_span = is_std_span_helper<q20::remove_cvref_t<T>>;
0071
0072 template <typename T>
0073 struct is_std_array_helper : std::false_type {};
0074 template <typename T, std::size_t N>
0075 struct is_std_array_helper<std::array<T, N>> : std::true_type {};
0076 template <typename T>
0077 using is_std_array = is_std_array_helper<q20::remove_cvref_t<T>>;
0078
0079 template <typename From, typename To>
0080 using is_qualification_conversion =
0081 std::is_convertible<From(*)[], To(*)[]>;
0082 template <typename From, typename To>
0083 constexpr inline bool is_qualification_conversion_v = is_qualification_conversion<From, To>::value;
0084
0085 namespace AdlTester {
0086 #define MAKE_ADL_TEST(what) \
0087 using std:: what; \
0088 template <typename T> using what ## _result = decltype( what (std::declval<T&&>())); \
0089
0090 MAKE_ADL_TEST(begin)
0091 MAKE_ADL_TEST(data)
0092 MAKE_ADL_TEST(size)
0093 #undef MAKE_ADL_TEST
0094 }
0095
0096
0097
0098 template <typename Range>
0099 AdlTester::begin_result<Range> adl_begin(Range &&r) { using std::begin; return begin(r); }
0100 template <typename Range>
0101 AdlTester::data_result<Range> adl_data(Range &&r) { using std::data; return data(r); }
0102 template <typename Range>
0103 AdlTester::size_result<Range> adl_size(Range &&r) { using std::size; return size(r); }
0104
0105
0106
0107 template <typename Range>
0108 using iterator_t = decltype(QSpanPrivate::adl_begin(std::declval<Range&>()));
0109 template <typename Range>
0110 using range_reference_t = q20::iter_reference_t<QSpanPrivate::iterator_t<Range>>;
0111
0112 template <typename T>
0113 class QSpanCommon {
0114 protected:
0115 template <typename Iterator>
0116 using is_compatible_iterator = std::conjunction<
0117
0118 std::is_base_of<
0119 std::random_access_iterator_tag,
0120 typename std::iterator_traits<Iterator>::iterator_category
0121 >,
0122 is_qualification_conversion<
0123 std::remove_reference_t<q20::iter_reference_t<Iterator>>,
0124 T
0125 >
0126 >;
0127 template <typename Iterator, typename End>
0128 using is_compatible_iterator_and_sentinel = std::conjunction<
0129
0130 is_compatible_iterator<Iterator>,
0131 std::negation<std::is_convertible<End, std::size_t>>
0132 >;
0133 template <typename Range, typename = void>
0134 struct is_compatible_range_helper : std::false_type {};
0135 template <typename Range>
0136 struct is_compatible_range_helper<Range, std::void_t<QSpanPrivate::iterator_t<Range>>>
0137 : is_compatible_iterator<QSpanPrivate::iterator_t<Range>> {};
0138 template <typename Range>
0139 using is_compatible_range = std::conjunction<
0140
0141 std::negation<is_qspan<Range>>,
0142 std::negation<is_std_span<Range>>,
0143 std::negation<is_std_array<Range>>,
0144 std::negation<std::is_array<q20::remove_cvref_t<Range>>>,
0145 is_compatible_range_helper<Range>
0146 >;
0147
0148
0149 template <typename Iterator>
0150 using if_compatible_iterator = std::enable_if_t<
0151 is_compatible_iterator<Iterator>::value
0152 , bool>;
0153 template <typename Iterator, typename End>
0154 using if_compatible_iterator_and_sentinel = std::enable_if_t<
0155 is_compatible_iterator_and_sentinel<Iterator, End>::value
0156 , bool>;
0157 template <typename Range>
0158 using if_compatible_range = std::enable_if_t<is_compatible_range<Range>::value, bool>;
0159 };
0160
0161 template <typename T, std::size_t E>
0162 class QSpanBase : protected QSpanCommon<T>
0163 {
0164 static_assert(E < size_t{(std::numeric_limits<qsizetype>::max)()},
0165 "QSpan only supports extents that fit into the signed size type (qsizetype).");
0166
0167 template <typename S, std::size_t N>
0168 using if_compatible_array = std::enable_if_t<
0169 N == E && is_qualification_conversion_v<S, T>
0170 , bool>;
0171
0172 template <typename S>
0173 using if_qualification_conversion = std::enable_if_t<
0174 is_qualification_conversion_v<S, T>
0175 , bool>;
0176 protected:
0177 using Base = QSpanCommon<T>;
0178
0179
0180 T *m_data;
0181 static constexpr qsizetype m_size = qsizetype(E);
0182
0183
0184
0185
0186
0187 public:
0188 template <std::size_t E2 = E, std::enable_if_t<E2 == 0, bool> = true>
0189 Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr} {}
0190
0191 template <typename It, typename Base::template if_compatible_iterator<It> = true>
0192 explicit constexpr QSpanBase(It first, qsizetype count)
0193 : m_data{q20::to_address(first)}
0194 {
0195 Q_ASSERT(count == m_size);
0196 }
0197
0198 template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
0199 explicit constexpr QSpanBase(It first, End last)
0200 : QSpanBase(first, last - first) {}
0201
0202 template <size_t N, std::enable_if_t<N == E, bool> = true>
0203 Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
0204 : QSpanBase(arr, N) {}
0205
0206 template <typename S, size_t N, if_compatible_array<S, N> = true>
0207 Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
0208 : QSpanBase(arr.data(), N) {}
0209
0210 template <typename S, size_t N, if_compatible_array<S, N> = true>
0211 Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
0212 : QSpanBase(arr.data(), N) {}
0213
0214 template <typename Range, typename Base::template if_compatible_range<Range> = true>
0215 Q_IMPLICIT constexpr QSpanBase(Range &&r)
0216 : QSpanBase(QSpanPrivate::adl_data(QSpanPrivate::const_propagated<T>(r)),
0217 qsizetype(QSpanPrivate::adl_size(r)))
0218 {}
0219
0220 template <typename S, if_qualification_conversion<S> = true>
0221 Q_IMPLICIT constexpr QSpanBase(QSpan<S, E> other) noexcept
0222 : QSpanBase(other.data(), other.size())
0223 {}
0224
0225 template <typename S, if_qualification_conversion<S> = true>
0226 Q_IMPLICIT constexpr QSpanBase(QSpan<S> other)
0227 : QSpanBase(other.data(), other.size())
0228 {}
0229
0230 template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true>
0231 Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il)
0232 : QSpanBase(il.begin(), il.size())
0233 {}
0234
0235 #ifdef __cpp_lib_span
0236 template <typename S, if_qualification_conversion<S> = true>
0237 Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
0238 : QSpanBase(other.data(), other.size())
0239 {}
0240
0241 template <typename S, if_qualification_conversion<S> = true>
0242 Q_IMPLICIT constexpr QSpanBase(std::span<S> other)
0243 : QSpanBase(other.data(), other.size())
0244 {}
0245 #endif
0246 };
0247
0248 template <typename T>
0249 class QSpanBase<T, q20::dynamic_extent> : protected QSpanCommon<T>
0250 {
0251 template <typename S>
0252 using if_qualification_conversion = std::enable_if_t<
0253 is_qualification_conversion_v<S, T>
0254 , bool>;
0255 protected:
0256 using Base = QSpanCommon<T>;
0257
0258
0259 T *m_data;
0260 qsizetype m_size;
0261
0262
0263 public:
0264 Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr}, m_size{0} {}
0265
0266 template <typename It, typename Base::template if_compatible_iterator<It> = true>
0267 Q_IMPLICIT constexpr QSpanBase(It first, qsizetype count)
0268 : m_data{q20::to_address(first)}, m_size{count} {}
0269
0270 template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
0271 Q_IMPLICIT constexpr QSpanBase(It first, End last)
0272 : QSpanBase(first, last - first) {}
0273
0274 template <size_t N>
0275 Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
0276 : QSpanBase(arr, N) {}
0277
0278 template <typename S, size_t N, if_qualification_conversion<S> = true>
0279 Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
0280 : QSpanBase(arr.data(), N) {}
0281
0282 template <typename S, size_t N, if_qualification_conversion<S> = true>
0283 Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
0284 : QSpanBase(arr.data(), N) {}
0285
0286 template <typename Range, typename Base::template if_compatible_range<Range> = true>
0287 Q_IMPLICIT constexpr QSpanBase(Range &&r)
0288 : QSpanBase(QSpanPrivate::adl_data(QSpanPrivate::const_propagated<T>(r)),
0289 qsizetype(QSpanPrivate::adl_size(r)))
0290 {}
0291
0292 template <typename S, size_t N, if_qualification_conversion<S> = true>
0293 Q_IMPLICIT constexpr QSpanBase(QSpan<S, N> other) noexcept
0294 : QSpanBase(other.data(), other.size())
0295 {}
0296
0297 template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true>
0298 Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il) noexcept
0299 : QSpanBase(il.begin(), il.size())
0300 {}
0301
0302 #ifdef __cpp_lib_span
0303 template <typename S, size_t N, if_qualification_conversion<S> = true>
0304 Q_IMPLICIT constexpr QSpanBase(std::span<S, N> other) noexcept
0305 : QSpanBase(other.data(), other.size())
0306 {}
0307 #endif
0308 };
0309
0310 }
0311
0312 template <typename T, std::size_t E>
0313 class QSpan
0314 #ifndef Q_QDOC
0315 : private QSpanPrivate::QSpanBase<T, E>
0316 #endif
0317 {
0318 using Base = QSpanPrivate::QSpanBase<T, E>;
0319 Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0,
0320 [[maybe_unused]] qsizetype n = 1) const
0321 {
0322 Q_ASSERT(pos >= 0);
0323 Q_ASSERT(pos <= size());
0324 Q_ASSERT(n >= 0);
0325 Q_ASSERT(n <= size() - pos);
0326 }
0327
0328 template <std::size_t N>
0329 static constexpr bool subspan_always_succeeds_v = N <= E && E != q20::dynamic_extent;
0330 public:
0331
0332 using value_type = std::remove_cv_t<T>;
0333 #ifdef QT_COMPILER_HAS_LWG3346
0334 using iterator_concept = std::contiguous_iterator_tag;
0335 using element_type = T;
0336 #endif
0337 using size_type = qsizetype;
0338 using difference_type = qptrdiff;
0339 using pointer = T*;
0340 using const_pointer = const T*;
0341 using reference = T&;
0342 using const_reference = const T&;
0343 using iterator = pointer;
0344 using const_iterator = const_pointer;
0345 using reverse_iterator = std::reverse_iterator<iterator>;
0346 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
0347 static constexpr std::size_t extent = E;
0348
0349
0350 using Base::Base;
0351 #ifdef Q_QDOC
0352 template <typename It> using if_compatible_iterator = bool;
0353 template <typename S> using if_qualification_conversion = bool;
0354 template <typename Range> using if_compatible_range = bool;
0355 template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, qsizetype count);
0356 template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, It last);
0357 template <size_t N> constexpr QSpan(q20::type_identity_t<T> (&arr)[N]) noexcept;
0358 template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::array<S, N> &arr) noexcept;
0359 template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(const std::array<S, N> &arr) noexcept;
0360 template <typename Range, if_compatible_range<Range> = true> constexpr QSpan(Range &&r);
0361 template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(QSpan<S, N> other) noexcept;
0362 template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::span<S, N> other) noexcept;
0363 constexpr QSpan(std::initializer_list<value_type> il);
0364 #endif
0365
0366
0367 [[nodiscard]] constexpr size_type size() const noexcept { return this->m_size; }
0368 [[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(T); }
0369 [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
0370
0371
0372 [[nodiscard]] constexpr reference operator[](size_type idx) const
0373 { verify(idx); return data()[idx]; }
0374 [[nodiscard]] constexpr reference front() const { verify(); return *data(); }
0375 [[nodiscard]] constexpr reference back() const { verify(); return data()[size() - 1]; }
0376 [[nodiscard]] constexpr pointer data() const noexcept { return this->m_data; }
0377
0378
0379 [[nodiscard]] constexpr iterator begin() const noexcept { return data(); }
0380 [[nodiscard]] constexpr iterator end() const noexcept { return data() + size(); }
0381 [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
0382 [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
0383 [[nodiscard]] constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
0384 [[nodiscard]] constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
0385 [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
0386 [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
0387
0388
0389 template <std::size_t Count>
0390 [[nodiscard]] constexpr QSpan<T, Count> first() const
0391 noexcept(subspan_always_succeeds_v<Count>)
0392 {
0393 static_assert(Count <= E,
0394 "Count cannot be larger than the span's extent.");
0395 verify(0, Count);
0396 return QSpan<T, Count>{data(), Count};
0397 }
0398
0399 template <std::size_t Count>
0400 [[nodiscard]] constexpr QSpan<T, Count> last() const
0401 noexcept(subspan_always_succeeds_v<Count>)
0402 {
0403 static_assert(Count <= E,
0404 "Count cannot be larger than the span's extent.");
0405 verify(0, Count);
0406 return QSpan<T, Count>{data() + (size() - Count), Count};
0407 }
0408
0409 template <std::size_t Offset>
0410 [[nodiscard]] constexpr auto subspan() const
0411 noexcept(subspan_always_succeeds_v<Offset>)
0412 {
0413 static_assert(Offset <= E,
0414 "Offset cannot be larger than the span's extent.");
0415 verify(Offset, 0);
0416 if constexpr (E == q20::dynamic_extent)
0417 return QSpan<T>{data() + Offset, qsizetype(size() - Offset)};
0418 else
0419 return QSpan<T, E - Offset>{data() + Offset, qsizetype(E - Offset)};
0420 }
0421
0422 template <std::size_t Offset, std::size_t Count>
0423 [[nodiscard]] constexpr auto subspan() const
0424 noexcept(subspan_always_succeeds_v<Offset + Count>)
0425 { return subspan<Offset>().template first<Count>(); }
0426
0427 [[nodiscard]] constexpr QSpan<T> first(size_type n) const { verify(0, n); return {data(), n}; }
0428 [[nodiscard]] constexpr QSpan<T> last(size_type n) const { verify(0, n); return {data() + (size() - n), n}; }
0429 [[nodiscard]] constexpr QSpan<T> subspan(size_type pos) const { verify(pos, 0); return {data() + pos, size() - pos}; }
0430 [[nodiscard]] constexpr QSpan<T> subspan(size_type pos, size_type n) const { return subspan(pos).first(n); }
0431
0432
0433 [[nodiscard]] constexpr bool isEmpty() const noexcept { return empty(); }
0434
0435 [[nodiscard]] constexpr QSpan<T> sliced(size_type pos) const { return subspan(pos); }
0436 [[nodiscard]] constexpr QSpan<T> sliced(size_type pos, size_type n) const { return subspan(pos, n); }
0437 [[nodiscard]] constexpr QSpan<T> chopped(size_type n) const { verify(0, n); return first(size() - n); }
0438
0439 #ifdef __cpp_concepts
0440 # define QT_ONLY_IF_DYNAMIC_SPAN(DECL) \
0441 DECL requires(E == q20::dynamic_extent)
0442 #else
0443 # define QT_ONLY_IF_DYNAMIC_SPAN(DECL) \
0444 template <size_t M = E, typename = std::enable_if_t<M == q20::dynamic_extent>> DECL
0445 #endif
0446 QT_ONLY_IF_DYNAMIC_SPAN(
0447 constexpr void slice(size_type pos)
0448 )
0449 { *this = sliced(pos); }
0450 QT_ONLY_IF_DYNAMIC_SPAN(
0451 constexpr void slice(size_type pos, size_type n)
0452 )
0453 { *this = sliced(pos, n); }
0454 QT_ONLY_IF_DYNAMIC_SPAN(
0455 constexpr void chop(size_type n)
0456 )
0457 { *this = chopped(n); }
0458 #undef QT_ONLY_IF_DYNAMIC_SPAN
0459
0460 private:
0461
0462 [[nodiscard]] friend
0463 QSpan<const std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>
0464 as_bytes(QSpan s) noexcept
0465 {
0466 using R = QSpan<const std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>;
0467 return R{reinterpret_cast<const std::byte *>(s.data()), s.size_bytes()};
0468 }
0469
0470 template <typename U>
0471 using if_mutable = std::enable_if_t<!std::is_const_v<U>, bool>;
0472
0473 #ifndef Q_QDOC
0474 template <typename T2 = T, if_mutable<T2> = true>
0475 #endif
0476 [[nodiscard]] friend
0477 QSpan<std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>
0478 as_writable_bytes(QSpan s) noexcept
0479 {
0480 using R = QSpan<std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>;
0481 return R{reinterpret_cast<std::byte *>(s.data()), s.size_bytes()};
0482 }
0483 };
0484
0485
0486 template <class It, class EndOrSize>
0487 QSpan(It, EndOrSize) -> QSpan<std::remove_reference_t<q20::iter_reference_t<It>>>;
0488 template <class T, std::size_t N>
0489 QSpan(T (&)[N]) -> QSpan<T, N>;
0490 template <class T, std::size_t N>
0491 QSpan(std::array<T, N> &) -> QSpan<T, N>;
0492 template <class T, std::size_t N>
0493 QSpan(const std::array<T, N> &) -> QSpan<const T, N>;
0494 template <class R>
0495 QSpan(R&&) -> QSpan<std::remove_reference_t<QSpanPrivate::range_reference_t<R>>>;
0496
0497 QT_END_NAMESPACE
0498
0499 #endif