summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMate Barany <[email protected]>2024-06-19 17:57:15 +0200
committerMate Barany <[email protected]>2024-07-04 22:08:12 +0200
commitaca8235c753d673abe7442cdf8b628fe4e05c471 (patch)
tree8becf2c2deec270d91aaa672a2f01ca5ef4a8c24
parent31ec108dd08d6381a15e49b6fbec9337705c3b2a (diff)
Apply the PIMPL idiom to QFormDataPartBuilder
Change QFormDataBuilderPrivate to store a vector of QFormDataPartBuilderPrivates and pimplify QFormDataPartBuilder such that it stores a pointer to the QFormDataBuilderPrivate and an index into the vector. This makes it robust against QFDP::m_parts reallocations and we can make QFDBP copyable which has the benefit that if the user wants to "go back" to an earlier part, she can do so by storing the result of the original part() call by value. Found in API review. Task-number: QTBUG-125985 Pick-to: 6.8 Change-Id: I56e9018e539457e9494751bdb62509f84a680889 Reviewed-by: Marc Mutz <[email protected]>
-rw-r--r--src/network/access/qformdatabuilder.cpp146
-rw-r--r--src/network/access/qformdatabuilder.h70
-rw-r--r--tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp41
3 files changed, 173 insertions, 84 deletions
diff --git a/src/network/access/qformdatabuilder.cpp b/src/network/access/qformdatabuilder.cpp
index 72ef96c253a..9f5cc683d0f 100644
--- a/src/network/access/qformdatabuilder.cpp
+++ b/src/network/access/qformdatabuilder.cpp
@@ -30,6 +30,26 @@ QT_BEGIN_NAMESPACE
\sa QHttpPart, QHttpMultiPart, QFormDataBuilder
*/
+class QFormDataPartBuilderPrivate
+{
+public:
+ explicit QFormDataPartBuilderPrivate(QAnyStringView name);
+ QHttpPart build();
+
+ QString m_name;
+ QByteArray m_mimeType;
+ QString m_originalBodyName;
+ QHttpHeaders m_httpHeaders;
+ std::variant<QIODevice*, QByteArray> m_body;
+};
+
+QFormDataPartBuilderPrivate::QFormDataPartBuilderPrivate(QAnyStringView name)
+ : m_name{name.toString()}
+{
+
+}
+
+
static void escapeNameAndAppend(QByteArray &dst, QByteArrayView src)
{
for (auto c : src) {
@@ -57,17 +77,6 @@ static void escapeNameAndAppend(QByteArray &dst, QStringView src)
}
/*!
- Constructs a QFormDataPartBuilder object and sets \a name as the name
- parameter of the form-data.
-*/
-QFormDataPartBuilder::QFormDataPartBuilder(QAnyStringView name, PrivateConstructor /*unused*/)
- : m_name{name.toString()}
-{
- static_assert(std::is_nothrow_move_constructible_v<decltype(m_body)>);
- static_assert(std::is_nothrow_move_assignable_v<decltype(m_body)>);
-}
-
-/*!
\fn QFormDataPartBuilder::QFormDataPartBuilder(QFormDataPartBuilder &&other) noexcept
Move-constructs a QFormDataPartBuilder instance, making it point at the same
@@ -81,11 +90,56 @@ QFormDataPartBuilder::QFormDataPartBuilder(QAnyStringView name, PrivateConstruct
*/
/*!
- Destroys the QFormDataPartBuilder object.
+ \fn QFormDataPartBuilder::QFormDataPartBuilder(const QFormDataPartBuilder &other)
+
+ Constructs a copy of \a other. The object is valid for as long as the associated
+ QFormDataBuilder has not been destroyed.
+
+ The data of the copy is shared (shallow copy): modifying one part will also change
+ the other.
+
+ \code
+ QFormDataPartBuilder foo()
+ {
+ QFormDataBuilder builder;
+ auto qfdpb1 = builder.part("First"_L1);
+ auto qfdpb2 = qfdpb1; // this creates a shallow copy
+
+ qfdpb2.setBodyDevice(&image, "cutecat.jpg"); // qfdpb1 is also modified
+
+ return qfdbp2; // invalid, builder is destroyed at the end of the scope
+ }
+ \endcode
*/
-QFormDataPartBuilder::~QFormDataPartBuilder()
- = default;
+/*!
+ \fn QFormDataPartBuilder& QFormDataPartBuilder::operator=(const QFormDataPartBuilder &other)
+
+ Assigns \a other to QFormDataPartBuilder and returns a reference to this
+ QFormDataPartBuilder. The object is valid for as long as the associated QFormDataBuilder
+ has not been destroyed.
+
+ The data of the copy is shared (shallow copy): modifying one part will also change the other.
+
+ \code
+ QFormDataPartBuilder foo()
+ {
+ QFormDataBuilder builder;
+ auto qfdpb1 = builder.part("First"_L1);
+ auto qfdpb2 = qfdpb1; // this creates a shallow copy
+
+ qfdpb2.setBodyDevice(&image, "cutecat.jpg"); // qfdpb1 is also modified
+
+ return qfdbp2; // invalid, builder is destroyed at the end of the scope
+ }
+ \endcode
+*/
+
+/*!
+ \fn QFormDataPartBuilder::~QFormDataPartBuilder()
+
+ Destroys the QFormDataPartBuilder object.
+*/
static void convertInto_impl(QByteArray &dst, QUtf8StringView in)
{
@@ -110,13 +164,15 @@ static void convertInto(QByteArray &dst, QAnyStringView in)
in.visit([&dst](auto in) { convertInto_impl(dst, in); });
}
-QFormDataPartBuilder &QFormDataPartBuilder::setBodyHelper(const QByteArray &data,
- QAnyStringView name,
- QAnyStringView mimeType)
+QFormDataPartBuilder QFormDataPartBuilder::setBodyHelper(const QByteArray &data,
+ QAnyStringView name,
+ QAnyStringView mimeType)
{
- m_originalBodyName = name.toString();
- convertInto(m_mimeType, mimeType);
- m_body = data;
+ Q_D(QFormDataPartBuilder);
+
+ d->m_originalBodyName = name.toString();
+ convertInto(d->m_mimeType, mimeType);
+ d->m_body = data;
return *this;
}
@@ -136,9 +192,9 @@ QFormDataPartBuilder &QFormDataPartBuilder::setBodyHelper(const QByteArray &data
\sa setBodyDevice()
*/
-QFormDataPartBuilder &QFormDataPartBuilder::setBody(QByteArrayView data,
- QAnyStringView fileName,
- QAnyStringView mimeType)
+QFormDataPartBuilder QFormDataPartBuilder::setBody(QByteArrayView data,
+ QAnyStringView fileName,
+ QAnyStringView mimeType)
{
return setBody(data.toByteArray(), fileName, mimeType);
}
@@ -168,12 +224,14 @@ QFormDataPartBuilder &QFormDataPartBuilder::setBody(QByteArrayView data,
\sa setBody(), QHttpPart::setBodyDevice()
*/
-QFormDataPartBuilder &QFormDataPartBuilder::setBodyDevice(QIODevice *body, QAnyStringView fileName,
- QAnyStringView mimeType)
+QFormDataPartBuilder QFormDataPartBuilder::setBodyDevice(QIODevice *body, QAnyStringView fileName,
+ QAnyStringView mimeType)
{
- m_originalBodyName = fileName.toString();
- convertInto(m_mimeType, mimeType);
- m_body = body;
+ Q_D(QFormDataPartBuilder);
+
+ d->m_originalBodyName = fileName.toString();
+ convertInto(d->m_mimeType, mimeType);
+ d->m_body = body;
return *this;
}
@@ -184,9 +242,11 @@ QFormDataPartBuilder &QFormDataPartBuilder::setBodyDevice(QIODevice *body, QAnyS
specified in \a headers, will be overwritten by the class.
*/
-QFormDataPartBuilder &QFormDataPartBuilder::setHeaders(const QHttpHeaders &headers)
+QFormDataPartBuilder QFormDataPartBuilder::setHeaders(const QHttpHeaders &headers)
{
- m_httpHeaders = headers;
+ Q_D(QFormDataPartBuilder);
+
+ d->m_httpHeaders = headers;
return *this;
}
@@ -200,7 +260,7 @@ QFormDataPartBuilder &QFormDataPartBuilder::setHeaders(const QHttpHeaders &heade
header.
*/
-QHttpPart QFormDataPartBuilder::build()
+QHttpPart QFormDataPartBuilderPrivate::build()
{
QHttpPart httpPart;
@@ -292,9 +352,20 @@ QHttpPart QFormDataPartBuilder::build()
class QFormDataBuilderPrivate
{
public:
- std::vector<QFormDataPartBuilder> parts;
+ std::vector<QFormDataPartBuilderPrivate> parts;
};
+QFormDataPartBuilderPrivate* QFormDataPartBuilder::d_func()
+{
+ return const_cast<QFormDataPartBuilderPrivate*>(std::as_const(*this).d_func());
+}
+
+const QFormDataPartBuilderPrivate* QFormDataPartBuilder::d_func() const
+{
+ Q_ASSERT(m_index < d->parts.size());
+ return &d->parts[m_index];
+}
+
/*!
Constructs an empty QFormDataBuilder object.
*/
@@ -328,9 +399,9 @@ QFormDataBuilder::~QFormDataBuilder()
*/
/*!
- Constructs and returns a reference to a QFormDataPartBuilder object and sets
- \a name as the name parameter of the form-data. The returned reference is
- valid until the next call to this function.
+ Returns a newly-constructed QFormDataPartBuilder object using \a name as the
+ form-data's \c name parameter. The object is valid for as long as the
+ associated QFormDataBuilder has not been destroyed.
Limiting \a name characters to US-ASCII is
\l {https://datatracker.ietf.org/doc/html/rfc7578#section-5.1.1}{strongly recommended}
@@ -339,11 +410,12 @@ QFormDataBuilder::~QFormDataBuilder()
\sa QFormDataPartBuilder, QHttpPart
*/
-QFormDataPartBuilder &QFormDataBuilder::part(QAnyStringView name)
+QFormDataPartBuilder QFormDataBuilder::part(QAnyStringView name)
{
Q_D(QFormDataBuilder);
- return d->parts.emplace_back(name, QFormDataPartBuilder::PrivateConstructor());
+ d->parts.emplace_back(name);
+ return QFormDataPartBuilder(d, d->parts.size() - 1);
}
/*!
diff --git a/src/network/access/qformdatabuilder.h b/src/network/access/qformdatabuilder.h
index ddbb8ee0fa2..f1f148147f6 100644
--- a/src/network/access/qformdatabuilder.h
+++ b/src/network/access/qformdatabuilder.h
@@ -26,67 +26,49 @@ class QHttpMultiPart;
class QDebug;
class QFormDataBuilderPrivate;
+class QFormDataPartBuilderPrivate;
class QFormDataPartBuilder
{
- struct PrivateConstructor { explicit PrivateConstructor() = default; };
+ QFormDataPartBuilder(QFormDataBuilderPrivate *qfdb, qsizetype idx) : d(qfdb), m_index(idx) {}
public:
- Q_NETWORK_EXPORT explicit QFormDataPartBuilder(QAnyStringView name, PrivateConstructor);
-
- QFormDataPartBuilder(QFormDataPartBuilder &&other) noexcept
- : m_name(std::move(other.m_name)),
- m_originalBodyName(std::move(other.m_originalBodyName)),
- m_httpHeaders(std::move(other.m_httpHeaders)),
- m_body(std::move(other.m_body)),
- m_reserved(std::exchange(other.m_reserved, nullptr))
- {
-
- }
-
- QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QFormDataPartBuilder)
void swap(QFormDataPartBuilder &other) noexcept
{
- m_name.swap(other.m_name);
- m_originalBodyName.swap(other.m_originalBodyName);
- m_httpHeaders.swap(other.m_httpHeaders);
- m_body.swap(other.m_body);
- qt_ptr_swap(m_reserved, other.m_reserved);
+ qt_ptr_swap(d, other.d);
+ std::swap(m_index, other.m_index);
}
- Q_NETWORK_EXPORT ~QFormDataPartBuilder();
+ QFormDataPartBuilder() = default;
+ // Rule of zero applies
- Q_WEAK_OVERLOAD QFormDataPartBuilder &setBody(const QByteArray &data,
- QAnyStringView fileName = {},
- QAnyStringView mimeType = {})
+ Q_WEAK_OVERLOAD QFormDataPartBuilder setBody(const QByteArray &data,
+ QAnyStringView fileName = {},
+ QAnyStringView mimeType = {})
{ return setBodyHelper(data, fileName, mimeType); }
- Q_NETWORK_EXPORT QFormDataPartBuilder &setBody(QByteArrayView data,
- QAnyStringView fileName = {},
- QAnyStringView mimeType = {});
- Q_NETWORK_EXPORT QFormDataPartBuilder &setBodyDevice(QIODevice *body,
- QAnyStringView fileName = {},
- QAnyStringView mimeType = {});
- Q_NETWORK_EXPORT QFormDataPartBuilder &setHeaders(const QHttpHeaders &headers);
+ Q_NETWORK_EXPORT QFormDataPartBuilder setBody(QByteArrayView data,
+ QAnyStringView fileName = {},
+ QAnyStringView mimeType = {});
+ Q_NETWORK_EXPORT QFormDataPartBuilder setBodyDevice(QIODevice *body,
+ QAnyStringView fileName = {},
+ QAnyStringView mimeType = {});
+ Q_NETWORK_EXPORT QFormDataPartBuilder setHeaders(const QHttpHeaders &headers);
private:
- Q_DISABLE_COPY(QFormDataPartBuilder)
+ Q_NETWORK_EXPORT QFormDataPartBuilder setBodyHelper(const QByteArray &data,
+ QAnyStringView fileName,
+ QAnyStringView mimeType);
- Q_NETWORK_EXPORT QFormDataPartBuilder &setBodyHelper(const QByteArray &data,
- QAnyStringView fileName,
- QAnyStringView mimeType);
- QHttpPart build();
+ QFormDataPartBuilderPrivate* d_func();
+ const QFormDataPartBuilderPrivate* d_func() const;
- QString m_name;
- QByteArray m_mimeType;
- QString m_originalBodyName;
- QHttpHeaders m_httpHeaders;
- std::variant<QIODevice*, QByteArray> m_body;
- void *m_reserved = nullptr;
+ QFormDataBuilderPrivate *d;
+ size_t m_index;
friend class QFormDataBuilder;
- friend void swap(QFormDataPartBuilder &lhs, QFormDataPartBuilder &rhs) noexcept
- { lhs.swap(rhs); }
};
+Q_DECLARE_SHARED(QFormDataPartBuilder)
+
class QFormDataBuilder
{
public:
@@ -101,7 +83,7 @@ public:
}
Q_NETWORK_EXPORT ~QFormDataBuilder();
- Q_NETWORK_EXPORT QFormDataPartBuilder &part(QAnyStringView name);
+ Q_NETWORK_EXPORT QFormDataPartBuilder part(QAnyStringView name);
Q_NETWORK_EXPORT std::unique_ptr<QHttpMultiPart> buildMultiPart();
private:
QFormDataBuilderPrivate *d_ptr;
diff --git a/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp b/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp
index 1502cb7e3c7..8cd2665d5ff 100644
--- a/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp
+++ b/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp
@@ -79,6 +79,7 @@ private Q_SLOTS:
void picksUtf8NameEncodingIfAsciiDoesNotSuffice();
void moveSemantics();
+ void keepResultOfCallingPartAliveAmongSubsequentCallsToPart();
};
void tst_QFormDataBuilder::checkBodyPartsAreEquivalent(QByteArrayView expected, QByteArrayView actual)
@@ -335,7 +336,7 @@ void tst_QFormDataBuilder::setHeadersDoesNotAffectHeaderFieldsManagedByBuilder()
QVERIFY(buff.open(QIODevice::ReadOnly));
const auto msg = serialized([&](auto &builder) {
- auto &qfdpb = builder.part(name_data).setBodyDevice(&buff, body_name_data);
+ auto qfdpb = builder.part(name_data).setBodyDevice(&buff, body_name_data);
if (overwrite || extra_headers) {
QHttpHeaders headers;
@@ -383,7 +384,7 @@ void tst_QFormDataBuilder::specifyMimeType()
QVERIFY(buff.open(QIODevice::ReadOnly));
const auto msg = serialized([&](auto &builder) {
- auto &qfdpb = builder.part(name_data).setBodyDevice(&buff, body_name_data);
+ auto qfdpb = builder.part(name_data).setBodyDevice(&buff, body_name_data);
if (!mime_type.empty())
qfdpb.setBodyDevice(&buff, body_name_data, mime_type);
@@ -452,7 +453,7 @@ void tst_QFormDataBuilder::moveSemantics()
QVERIFY2(data_file.open(QIODeviceBase::ReadOnly), qPrintable(data_file.errorString()));
QFormDataBuilder qfdb;
- auto &p = qfdb.part("text"_L1);
+ auto p = qfdb.part("text"_L1);
const QByteArray actual = serialized([&, moved = std::move(qfdb)](auto &) mutable {
p.setBodyDevice(&data_file, "rfc3252.txt");
return std::ref(moved);
@@ -477,5 +478,39 @@ void tst_QFormDataBuilder::moveSemantics()
}
}
+void tst_QFormDataBuilder::keepResultOfCallingPartAliveAmongSubsequentCallsToPart()
+{
+ QFormDataBuilder qfdb;
+ auto p1 = qfdb.part("1"_L1);
+ auto p2 = qfdb.part("2"_L1);
+ auto p3 = qfdb.part("3"_L1);
+ auto p4 = qfdb.part("4"_L1);
+ auto p5 = qfdb.part("5"_L1);
+ auto p6 = qfdb.part("6"_L1);
+ auto p7 = qfdb.part("7"_L1);
+ auto p8 = qfdb.part("8"_L1);
+ auto p9 = qfdb.part("9"_L1);
+ auto p10 = qfdb.part("10"_L1);
+ auto p11 = qfdb.part("11"_L1);
+ auto p12 = qfdb.part("12"_L1);
+
+ QByteArray dummyData = "totally_a_text_file"_ba;
+
+ p1.setBody(dummyData, "body1"_L1);
+ p2.setBody(dummyData, "body2"_L1);
+ p3.setBody(dummyData, "body3"_L1);
+ p4.setBody(dummyData, "body4"_L1);
+ p5.setBody(dummyData, "body5"_L1);
+ p6.setBody(dummyData, "body6"_L1);
+ p7.setBody(dummyData, "body7"_L1);
+ p8.setBody(dummyData, "body8"_L1);
+ p9.setBody(dummyData, "body9"_L1);
+ p10.setBody(dummyData, "body10"_L1);
+ p11.setBody(dummyData, "body11"_L1);
+ p12.setBody(dummyData, "body12"_L1);
+
+ qfdb.buildMultiPart();
+}
+
QTEST_MAIN(tst_QFormDataBuilder)
#include "tst_qformdatabuilder.moc"