From bbe7570ddcc6fcce707a355b76d2d0024c44ea38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Mon, 14 Sep 2020 15:26:49 +0200 Subject: QByteArrayView: move array size deduction feature to fromArray 1. Make the ctor unable to construct a QByteArrayView from array literals other than 'char'. With the new behavior it would either be (very likely) unintended to pass e.g. a std::byte array to the ctor. And it would be confusing because you would get different sizes based on signed-ness. 2. Introduce fromArray Only supports array literals. Constructs a view of the full size. Explicit so it shouldn't be surprising. Change-Id: Ifdb55eb21057dfe7053b2561bd81e2c9825e9bc6 Reviewed-by: Andrei Golubev Reviewed-by: Sona Kurazyan Reviewed-by: Edward Welbourne --- .../code/src_corelib_text_qbytearrayview.cpp | 7 -- src/corelib/text/qbytearrayview.h | 20 +++-- src/corelib/text/qbytearrayview.qdoc | 39 ++++++--- .../text/qbytearrayview/tst_qbytearrayview.cpp | 92 ++++++++++++++++------ 4 files changed, 110 insertions(+), 48 deletions(-) diff --git a/src/corelib/doc/snippets/code/src_corelib_text_qbytearrayview.cpp b/src/corelib/doc/snippets/code/src_corelib_text_qbytearrayview.cpp index 31a2107c62..d70caa2650 100644 --- a/src/corelib/doc/snippets/code/src_corelib_text_qbytearrayview.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_text_qbytearrayview.cpp @@ -58,10 +58,3 @@ void fun(QByteArrayView bv); void fun(char ch) { fun(QByteArrayView(&ch, 1)); } //! [1] - -void wrapper() { - const char array[1] = { 0 }; -//! [2] - auto bv = QByteArrayView(array, std::size(array)); // using C++17 std::size() -//! [2] -} diff --git a/src/corelib/text/qbytearrayview.h b/src/corelib/text/qbytearrayview.h index 81db7029de..079f888f4b 100644 --- a/src/corelib/text/qbytearrayview.h +++ b/src/corelib/text/qbytearrayview.h @@ -94,6 +94,9 @@ struct IsContainerCompatibleWithQByteArrayView, QByteArray>>, + // We handle array literals specially for source compat reasons + std::negation>, + // Don't make an accidental copy constructor std::negation, QByteArrayView>>>>> : std::true_type {}; @@ -147,10 +150,11 @@ private: return qsizetype(std::size(c)); } - template - static constexpr qsizetype lengthHelperContainer(const Char (&)[N]) noexcept + static constexpr qsizetype lengthHelperCharArray(const char *data, size_t size) noexcept { - return qsizetype(N - 1); + const auto it = std::char_traits::find(data, size, '\0'); + const auto end = it ? it : std::next(data, size); + return qsizetype(std::distance(data, end)); } template @@ -175,9 +179,6 @@ public: : QByteArrayView(first, last - first) {} #ifdef Q_QDOC - template - constexpr QByteArrayView(const Byte (&array)[N]) noexcept; - template constexpr QByteArrayView(const Byte *data) noexcept; #else @@ -198,6 +199,13 @@ public: template = true> constexpr QByteArrayView(const Container &c) noexcept : QByteArrayView(std::data(c), lengthHelperContainer(c)) {} + template + constexpr QByteArrayView(const char (&data)[Size]) noexcept + : QByteArrayView(data, lengthHelperCharArray(data, Size)) {} + + template = true> + [[nodiscard]] constexpr static QByteArrayView fromArray(const Byte (&data)[Size]) noexcept + { return QByteArrayView(data, Size); } [[nodiscard]] inline QByteArray toByteArray() const; // defined in qbytearray.h diff --git a/src/corelib/text/qbytearrayview.qdoc b/src/corelib/text/qbytearrayview.qdoc index f386881fa2..839a956249 100644 --- a/src/corelib/text/qbytearrayview.qdoc +++ b/src/corelib/text/qbytearrayview.qdoc @@ -256,23 +256,25 @@ */ /*! - \fn template QByteArrayView::QByteArrayView(const Byte (&data)[N]) + \fn template QByteArrayView(const char (&data)[Size]) - Constructs a byte array view on the array of bytes \a data. - The length is set to \c{N-1}, excluding the trailing \{Byte(0)}. - If you need the full array, use the constructor from pointer and - size instead: - - \snippet code/src_corelib_text_qbytearrayview.cpp 2 + Constructs a byte array view on the char array \a data. + The view covers the array until the first \c{'\0'} is encountered, + or \c Size, whichever comes first. + If you need the full array, use fromArray() instead. \a data must remain valid for the lifetime of this byte array view object. - This constructor only participates in overload resolution if \a - data is an actual array and \c Byte is a compatible byte - type. + \note This constructor is only available for char array literals. + The reasoning behind that is for compatibility with C-libraries + which predefine "large-enough" arrays, but only use some of the + preallocated space. To support this in an intuitive way in an + implicit constructor overload, we need to stop at the first + \c{char(0)}. This is logical for a char array, but not + for a \c{std::byte} array. - \sa {Compatible Byte Types} + \sa fromArray */ /*! @@ -299,6 +301,21 @@ \sa {Compatible Byte Types} */ +/*! + \fn template static QByteArrayView QByteArrayView::fromArray(Byte (&data)[Size]) + + Constructs a byte array view on the array literal \a data. The view covers the full + array. That includes the trailing null-terminator of \c{char} array literals. + If you don't want the null-terminator included in the view, you can chop() it off + when you are certain it is at the end. Alternatively you can use the constructor + overload taking a char array literal which will create a view up to, but not including, + the first null-terminator in the data. + + This function will work with any array literal of a compatible byte type. + + \sa {Compatible Byte Types}, QByteArrayView +*/ + /*! \fn QByteArray QByteArrayView::toByteArray() const diff --git a/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp b/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp index c1d80b47dc..35f883068d 100644 --- a/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp +++ b/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp @@ -48,20 +48,20 @@ static_assert(CanConvert); static_assert(CanConvert); static_assert(!CanConvert); -static_assert(CanConvert); -static_assert(CanConvert); +static_assert(!CanConvert); +static_assert(!CanConvert); static_assert(CanConvert); static_assert(CanConvert); static_assert(!CanConvert); -static_assert(CanConvert); -static_assert(CanConvert); +static_assert(!CanConvert); +static_assert(!CanConvert); static_assert(CanConvert); static_assert(CanConvert); static_assert(!CanConvert); -static_assert(CanConvert); -static_assert(CanConvert); +static_assert(!CanConvert); +static_assert(!CanConvert); static_assert(CanConvert); static_assert(CanConvert); @@ -110,6 +110,7 @@ private slots: void constExpr() const; void basics() const; void literals() const; + void fromArray() const; void literalsWithInternalNulls() const; void at() const; @@ -324,9 +325,11 @@ void tst_QByteArrayView::basics() const QVERIFY(!(bv2 != bv1)); } +// Note: initially the size would be deduced from the array literal, +// but it caused source compatibility issues so this is currently not the case. void tst_QByteArrayView::literals() const { - const char hello[] = "Hello"; + const char hello[] = "Hello\0This shouldn't be found"; QCOMPARE(QByteArrayView(hello).size(), 5); QCOMPARE(QByteArrayView(hello + 0).size(), 5); // forces decay to pointer @@ -349,6 +352,44 @@ void tst_QByteArrayView::literals() const QVERIFY(!bv2.isNull()); QVERIFY(!bv2.empty()); QCOMPARE(bv2.size(), 5); + + const char abc[] = "abc"; + bv = abc; + QCOMPARE(bv.size(), 3); + + const char def[3] = {'d', 'e', 'f'}; + bv = def; + QCOMPARE(bv.size(), 3); +} + +void tst_QByteArrayView::fromArray() const +{ + static constexpr char hello[] = "Hello\0abc\0\0."; + + constexpr QByteArrayView bv = QByteArrayView::fromArray(hello); + QCOMPARE(bv.size(), 13); + QVERIFY(!bv.empty()); + QVERIFY(!bv.isEmpty()); + QVERIFY(!bv.isNull()); + QCOMPARE(*bv.data(), 'H'); + QCOMPARE(bv[0], 'H'); + QCOMPARE(bv.at(0), 'H'); + QCOMPARE(bv.front(), 'H'); + QCOMPARE(bv.first(), 'H'); + QCOMPARE(bv[4], 'o'); + QCOMPARE(bv.at(4), 'o'); + QCOMPARE(bv[5], '\0'); + QCOMPARE(bv.at(5), '\0'); + QCOMPARE(*(bv.data() + bv.size() - 2), '.'); + QCOMPARE(bv.back(), '\0'); + QCOMPARE(bv.last(), '\0'); + + const std::byte bytes[] = {std::byte(0x0), std::byte(0x1), std::byte(0x2)}; + QByteArrayView bbv = QByteArrayView::fromArray(bytes); + QCOMPARE(bbv.data(), reinterpret_cast(bytes + 0)); + QCOMPARE(bbv.size(), 3); + QCOMPARE(bbv.first(), 0x0); + QCOMPARE(bbv.last(), 0x2); } void tst_QByteArrayView::literalsWithInternalNulls() const @@ -356,36 +397,39 @@ void tst_QByteArrayView::literalsWithInternalNulls() const const char withnull[] = "a\0zzz"; // these are different results - QCOMPARE(size_t(QByteArrayView(withnull).size()), sizeof(withnull)/sizeof(withnull[0]) - 1); + QCOMPARE(size_t(QByteArrayView::fromArray(withnull).size()), std::size(withnull)); QCOMPARE(QByteArrayView(withnull + 0).size(), 1); - QByteArrayView nulled(withnull); + QByteArrayView nulled = QByteArrayView::fromArray(withnull); + QCOMPARE(nulled.last(), '\0'); + nulled.chop(1); // cut off trailing \0 QCOMPARE(nulled[1], '\0'); QCOMPARE(nulled.indexOf('\0'), 1); QCOMPARE(nulled.indexOf('z'), 2); QCOMPARE(nulled.lastIndexOf('z'), 4); QCOMPARE(nulled.lastIndexOf('a'), 0); QVERIFY(nulled.startsWith("a\0z")); - QVERIFY(!nulled.startsWith("a\0y")); + QVERIFY(nulled.startsWith("a\0y")); + QVERIFY(!nulled.startsWith(QByteArrayView("a\0y", 3))); QVERIFY(nulled.endsWith("zz")); QVERIFY(nulled.contains("z")); - QVERIFY(nulled.contains("\0z")); - QVERIFY(!nulled.contains("\0y")); - QCOMPARE(nulled.first(5), withnull); - QCOMPARE(nulled.last(5), withnull); - QCOMPARE(nulled.sliced(0), withnull); + QVERIFY(nulled.contains(QByteArrayView("\0z", 2))); + QVERIFY(!nulled.contains(QByteArrayView("\0y", 2))); + QCOMPARE(nulled.first(5), QByteArrayView(withnull, 5)); + QCOMPARE(nulled.last(5), QByteArrayView(withnull, 5)); + QCOMPARE(nulled.sliced(0), QByteArrayView(withnull, 5)); QCOMPARE(nulled.sliced(2, 2), "zz"); - QCOMPARE(nulled.chopped(2), "a\0z"); - QVERIFY(nulled.chopped(2) != "a\0y"); + QCOMPARE(nulled.chopped(2), QByteArrayView("a\0z", 3)); + QVERIFY(nulled.chopped(2) != QByteArrayView("a\0y", 3)); QCOMPARE(nulled.count('z'), 3); const char nullfirst[] = "\0buzz"; - QByteArrayView fromnull(nullfirst); + QByteArrayView fromnull = QByteArrayView::fromArray(nullfirst); QVERIFY(!fromnull.isEmpty()); const char nullNotEnd[] = { 'b', 'o', 'w', '\0', 'a', 'f', 't', 'z' }; - QByteArrayView midNull(nullNotEnd); - QCOMPARE(midNull.back(), 't'); + QByteArrayView midNull = QByteArrayView::fromArray(nullNotEnd); + QCOMPARE(midNull.back(), 'z'); } void tst_QByteArrayView::at() const @@ -522,14 +566,14 @@ void tst_QByteArrayView::fromEmptyLiteral() const QCOMPARE(QByteArrayView(null).size(), 0); QCOMPARE(QByteArrayView(null).data(), nullptr); - QCOMPARE(QByteArrayView(empty).size(), 0); - QCOMPARE(static_cast(QByteArrayView(empty).data()), + QCOMPARE(QByteArrayView::fromArray(empty).size(), 1); + QCOMPARE(static_cast(QByteArrayView::fromArray(empty).data()), static_cast(empty)); QVERIFY(QByteArrayView(null).isNull()); QVERIFY(QByteArrayView(null).isEmpty()); - QVERIFY(QByteArrayView(empty).isEmpty()); - QVERIFY(!QByteArrayView(empty).isNull()); + QVERIFY(!QByteArrayView::fromArray(empty).isEmpty()); + QVERIFY(!QByteArrayView::fromArray(empty).isNull()); } template -- cgit v1.2.3