summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2020-09-14 15:26:49 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2020-09-18 23:34:49 +0200
commitbbe7570ddcc6fcce707a355b76d2d0024c44ea38 (patch)
tree0a9d0413c4b0154e80ecb26233ab36a3b462ecd3
parent7a5e0c5712cbdeb9edb83dfd47d63277346537a9 (diff)
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 <andrei.golubev@qt.io> Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_text_qbytearrayview.cpp7
-rw-r--r--src/corelib/text/qbytearrayview.h20
-rw-r--r--src/corelib/text/qbytearrayview.qdoc39
-rw-r--r--tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp92
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<T, std::enable_if_t<
// This needs to be treated specially due to the empty vs null distinction
std::negation<std::is_same<std::decay_t<T>, QByteArray>>,
+ // We handle array literals specially for source compat reasons
+ std::negation<std::is_array<T>>,
+
// Don't make an accidental copy constructor
std::negation<std::is_same<std::decay_t<T>, QByteArrayView>>>>> : std::true_type {};
@@ -147,10 +150,11 @@ private:
return qsizetype(std::size(c));
}
- template <typename Char, size_t N>
- 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<char>::find(data, size, '\0');
+ const auto end = it ? it : std::next(data, size);
+ return qsizetype(std::distance(data, end));
}
template <typename Byte>
@@ -175,9 +179,6 @@ public:
: QByteArrayView(first, last - first) {}
#ifdef Q_QDOC
- template <typename Byte, size_t N>
- constexpr QByteArrayView(const Byte (&array)[N]) noexcept;
-
template <typename Byte>
constexpr QByteArrayView(const Byte *data) noexcept;
#else
@@ -198,6 +199,13 @@ public:
template <typename Container, if_compatible_container<Container> = true>
constexpr QByteArrayView(const Container &c) noexcept
: QByteArrayView(std::data(c), lengthHelperContainer(c)) {}
+ template <size_t Size>
+ constexpr QByteArrayView(const char (&data)[Size]) noexcept
+ : QByteArrayView(data, lengthHelperCharArray(data, Size)) {}
+
+ template <typename Byte, size_t Size, if_compatible_byte<Byte> = 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 <typename Byte, size_t N> QByteArrayView::QByteArrayView(const Byte (&data)[N])
+ \fn template <size_t Size> 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
*/
/*!
@@ -300,6 +302,21 @@
*/
/*!
+ \fn template <typename Byte, size_t Size> 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
Returns a deep copy of this byte array view's data as a QByteArray.
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<char*>);
static_assert(CanConvert<const char*>);
static_assert(!CanConvert<uchar>);
-static_assert(CanConvert<uchar[1]>);
-static_assert(CanConvert<const uchar[1]>);
+static_assert(!CanConvert<uchar[1]>);
+static_assert(!CanConvert<const uchar[1]>);
static_assert(CanConvert<uchar*>);
static_assert(CanConvert<const uchar*>);
static_assert(!CanConvert<signed char>);
-static_assert(CanConvert<signed char[1]>);
-static_assert(CanConvert<const signed char[1]>);
+static_assert(!CanConvert<signed char[1]>);
+static_assert(!CanConvert<const signed char[1]>);
static_assert(CanConvert<signed char*>);
static_assert(CanConvert<const signed char*>);
static_assert(!CanConvert<std::byte>);
-static_assert(CanConvert<std::byte[1]>);
-static_assert(CanConvert<const std::byte[1]>);
+static_assert(!CanConvert<std::byte[1]>);
+static_assert(!CanConvert<const std::byte[1]>);
static_assert(CanConvert<std::byte*>);
static_assert(CanConvert<const std::byte*>);
@@ -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<const char *>(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<const void*>(QByteArrayView(empty).data()),
+ QCOMPARE(QByteArrayView::fromArray(empty).size(), 1);
+ QCOMPARE(static_cast<const void*>(QByteArrayView::fromArray(empty).data()),
static_cast<const void*>(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 <typename Char>