Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qurlquery.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 Intel Corporation.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qurlquery.h"
5#include "qurl_p.h"
6
7#include <QtCore/qhashfunctions.h>
8#include <QtCore/qstringlist.h>
9
10#include <algorithm>
11
13
134
136{
137public:
139 : valueDelimiter(QUrlQuery::defaultQueryValueDelimiter()),
140 pairDelimiter(QUrlQuery::defaultQueryPairDelimiter())
141 { if (!query.isEmpty()) setQuery(query); }
142
143 QString recodeFromUser(const QString &input) const;
144 QString recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const;
145
146 void setQuery(const QString &query);
147
148 void addQueryItem(const QString &key, const QString &value)
150 int findRecodedKey(const QString &key, int from = 0) const
151 {
152 for (int i = from; i < itemList.size(); ++i)
153 if (itemList.at(i).first == key)
154 return i;
155 return itemList.size();
156 }
157 Map::const_iterator findKey(const QString &key) const
159 Map::iterator findKey(const QString &key)
161
165};
166
168{
169 if (d && d->ref.loadRelaxed() == 1)
170 return;
172 : new QUrlQueryPrivate);
173 x->ref.ref();
174 if (d && !d->ref.deref())
175 delete d;
176 d = x;
177}
178
179// Here's how we do the encoding in QUrlQuery
180// The RFC says these are the delimiters:
181// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
182// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
183// / "*" / "+" / "," / ";" / "="
184// And the definition of query is:
185// query = *( pchar / "/" / "?" )
186// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
187//
188// The strict definition of query says that it can have unencoded any
189// unreserved, sub-delim, ":", "@", "/" and "?". Or, by exclusion, excluded
190// delimiters are "#", "[" and "]" -- if those are present, they must be
191// percent-encoded. The fact that "[" and "]" should be encoded is probably a
192// mistake in the spec, so we ignore it and leave the decoded.
193//
194// The internal storage in the Map is equivalent to PrettyDecoded. That means
195// the getter methods, when called with the default encoding value, will not
196// have to recode anything (except for toString()).
197//
198// QUrlQuery handling of delimiters is quite simple: we never touch any of
199// them, except for the "#" character and the pair and value delimiters. Those
200// are always kept in their decoded forms.
201//
202// But when recreating the query string, in toString(), we must take care of
203// the special delimiters: the pair and value delimiters, as well as the "#"
204// character if unambiguous decoding is requested.
205
206#define decode(x) ushort(x)
207#define leave(x) ushort(0x100 | (x))
208#define encode(x) ushort(0x200 | (x))
209
211{
212 // note: duplicated in setQuery()
214 ushort prettyDecodedActions[] = {
217 decode('#'),
218 0
219 };
222 prettyDecodedActions))
223 return output;
224 return input;
225}
226
227inline bool idempotentRecodeToUser(QUrl::ComponentFormattingOptions encoding)
228{
229 return encoding == QUrl::PrettyDecoded;
230}
231
232inline QString QUrlQueryPrivate::recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const
233{
234 // our internal formats are stored in "PrettyDecoded" form
235 // and there are no ambiguous characters
236 if (idempotentRecodeToUser(encoding))
237 return input;
238
239 if (!(encoding & QUrl::EncodeDelimiters)) {
242 encoding, nullptr))
243 return output;
244 return input;
245 }
246
247 // re-encode the "#" character and the query delimiter pair
249 encode('#'), 0 };
251 if (qt_urlRecode(output, input, encoding, actions))
252 return output;
253 return input;
254}
255
257{
258 ushort prettyDecodedActions[] = {
261 decode('#'),
262 0
263 };
264
265 itemList.clear();
266 const QChar *pos = query.constData();
267 const QChar *const end = pos + query.size();
268 while (pos != end) {
269 const QChar *begin = pos;
270 const QChar *delimiter = nullptr;
271 while (pos != end) {
272 // scan for the component parts of this pair
273 if (!delimiter && *pos == valueDelimiter)
274 delimiter = pos;
275 if (*pos == pairDelimiter)
276 break;
277 ++pos;
278 }
279 if (!delimiter)
280 delimiter = pos;
281
282 // pos is the end of this pair (the end of the string or the pair delimiter)
283 // delimiter points to the value delimiter or to the end of this pair
284
285 QString key;
286 if (!qt_urlRecode(key, QStringView{begin, delimiter},
288 prettyDecodedActions))
289 key = QString(begin, delimiter - begin);
290
291 if (delimiter == pos) {
292 // the value delimiter wasn't found, store a null value
294 } else if (delimiter + 1 == pos) {
295 // if the delimiter was found but the value is empty, store empty-but-not-null
297 } else {
299 if (!qt_urlRecode(value, QStringView{delimiter + 1, pos},
301 prettyDecodedActions))
302 value = QString(delimiter + 1, pos - delimiter - 1);
304 }
305
306 if (pos != end)
307 ++pos;
308 }
309}
310
311// allow QUrlQueryPrivate to detach from null
312template <> inline QUrlQueryPrivate *
314{
315 return d ? new QUrlQueryPrivate(*d) : new QUrlQueryPrivate;
316}
317
325 : d(nullptr)
326{
327}
328
335QUrlQuery::QUrlQuery(const QString &queryString)
336 : d(queryString.isEmpty() ? nullptr : new QUrlQueryPrivate(queryString))
337{
338}
339
349 : d(nullptr)
350{
351 // use internals to avoid unnecessary recoding
352 // ### FIXME: actually do it
353 if (url.hasQuery())
354 d = new QUrlQueryPrivate(url.query());
355}
356
362 : d(other.d)
363{
364}
365
372 : d(std::move(other.d))
373{
374}
375
381{
382 d = other.d;
383 return *this;
384}
385
397{
398 // d auto-deletes
399}
400
406{
407 if (d == other.d)
408 return true;
409 if (d && other.d)
410 // keep in sync with qHash(QUrlQuery):
411 return d->valueDelimiter == other.d->valueDelimiter &&
412 d->pairDelimiter == other.d->pairDelimiter &&
413 d->itemList == other.d->itemList;
414
415 const QUrlQueryPrivate *x = d ? d.data() : other.d.data();
416 return x->valueDelimiter == defaultQueryValueDelimiter() &&
417 x->pairDelimiter == defaultQueryPairDelimiter() &&
418 x->itemList.isEmpty();
419}
420
428size_t qHash(const QUrlQuery &key, size_t seed) noexcept
429{
430 if (const QUrlQueryPrivate *d = key.d) {
432 // keep in sync with operator==:
433 seed = hash(seed, d->valueDelimiter);
434 seed = hash(seed, d->pairDelimiter);
435 seed = hash(seed, d->itemList);
436 }
437 return seed;
438}
439
447{
448 return d ? d->itemList.isEmpty() : true;
449}
450
455{
456 return d && d->ref.loadRelaxed() == 1;
457}
458
467{
468 if (d.constData())
469 d->itemList.clear();
470}
471
478void QUrlQuery::setQuery(const QString &queryString)
479{
480 d->setQuery(queryString);
481}
482
483static void recodeAndAppend(QString &to, const QString &input,
484 QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications)
485{
486 if (!qt_urlRecode(to, input, encoding, tableModifications))
487 to += input;
488}
489
505QString QUrlQuery::query(QUrl::ComponentFormattingOptions encoding) const
506{
507 if (!d)
508 return QString();
509
510 // unlike the component encoding, for the whole query we need to modify a little:
511 // - the "#" character is unambiguous, so we encode it in EncodeDelimiters mode
512 // - the query delimiter pair must always be encoded
513
514 // start with what's always encoded
515 ushort tableActions[] = {
516 encode(d->pairDelimiter.unicode()), // 0
517 encode(d->valueDelimiter.unicode()), // 1
518 0, // 2
519 0
520 };
521 if (encoding & QUrl::EncodeDelimiters) {
522 tableActions[2] = encode('#');
523 }
524
526 Map::const_iterator it = d->itemList.constBegin();
527 Map::const_iterator end = d->itemList.constEnd();
528
529 {
530 int size = 0;
531 for ( ; it != end; ++it)
532 size += it->first.size() + 1 + it->second.size() + 1;
533 result.reserve(size + size / 4);
534 }
535
536 for (it = d->itemList.constBegin(); it != end; ++it) {
537 if (!result.isEmpty())
539 recodeAndAppend(result, it->first, encoding, tableActions);
540 if (!it->second.isNull()) {
542 recodeAndAppend(result, it->second, encoding, tableActions);
543 }
544 }
545 return result;
546}
547
576void QUrlQuery::setQueryDelimiters(QChar valueDelimiter, QChar pairDelimiter)
577{
578 d->valueDelimiter = valueDelimiter;
579 d->pairDelimiter = pairDelimiter;
580}
581
589{
591}
592
600{
602}
603
617{
618 clear();
619 if (query.isEmpty())
620 return;
621
622 QUrlQueryPrivate *dd = d;
623 QList<QPair<QString, QString> >::const_iterator it = query.constBegin(),
624 end = query.constEnd();
625 for ( ; it != end; ++it)
626 dd->addQueryItem(it->first, it->second);
627}
628
637QList<QPair<QString, QString> > QUrlQuery::queryItems(QUrl::ComponentFormattingOptions encoding) const
638{
639 if (!d)
641 if (idempotentRecodeToUser(encoding))
642 return d->itemList;
643
645 Map::const_iterator it = d->itemList.constBegin();
646 Map::const_iterator end = d->itemList.constEnd();
647 result.reserve(d->itemList.size());
648 for ( ; it != end; ++it)
649 result << qMakePair(d->recodeToUser(it->first, encoding),
650 d->recodeToUser(it->second, encoding));
651 return result;
652}
653
663{
664 if (!d)
665 return false;
666 return d->findKey(key) != d->itemList.constEnd();
667}
668
683{
685}
686
702QString QUrlQuery::queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const
703{
705 if (d) {
706 Map::const_iterator it = d->findKey(key);
707 if (it != d->itemList.constEnd())
708 result = d->recodeToUser(it->second, encoding);
709 }
710 return result;
711}
712
722QStringList QUrlQuery::allQueryItemValues(const QString &key, QUrl::ComponentFormattingOptions encoding) const
723{
725 if (d) {
726 QString encodedKey = d->recodeFromUser(key);
727 int idx = d->findRecodedKey(encodedKey);
728 while (idx < d->itemList.size()) {
729 result << d->recodeToUser(d->itemList.at(idx).second, encoding);
730 idx = d->findRecodedKey(encodedKey, idx + 1);
731 }
732 }
733 return result;
734}
735
747{
748 if (d.constData()) {
749 auto *p = d.data();
750 Map::iterator it = p->findKey(key);
751 if (it != p->itemList.end())
752 p->itemList.erase(it);
753 }
754}
755
765{
766 if (d.constData()) {
767 auto *p = d.data();
768 const QString encodedKey = p->recodeFromUser(key);
769 auto firstEqualsEncodedKey = [&encodedKey](const QPair<QString, QString> &item) {
770 return item.first == encodedKey;
771 };
772 p->itemList.removeIf(firstEqualsEncodedKey);
773 }
774}
775
820
821#undef decode
822#undef leave
823#undef encode
bool ref() noexcept
T loadRelaxed() const noexcept
\inmodule QtCore
Definition qchar.h:48
constexpr char16_t unicode() const noexcept
Returns the numeric Unicode value of the QChar.
Definition qchar.h:458
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
const_iterator constBegin() const noexcept
Definition qlist.h:615
iterator begin()
Definition qlist.h:608
void append(parameter_type t)
Definition qlist.h:441
const_iterator constEnd() const noexcept
Definition qlist.h:616
void clear()
Definition qlist.h:417
qsizetype size() const
Definition qset.h:50
iterator end()
Definition qset.h:140
const_iterator constBegin() const noexcept
Definition qset.h:139
iterator erase(const_iterator i)
Definition qset.h:145
void detach()
If the shared data object's reference count is greater than 1, this function creates a deep copy of t...
Definition qshareddata.h:40
const T * constData() const noexcept
Returns a const pointer to the shared data object.
Definition qshareddata.h:51
T * data()
Returns a pointer to the shared data object.
Definition qshareddata.h:47
\inmodule QtCore
Definition qshareddata.h:19
QAtomicInt ref
Definition qshareddata.h:21
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString & removeIf(Predicate pred)
Definition qstring.h:490
QUrlQueryPrivate(const QString &query=QString())
int findRecodedKey(const QString &key, int from=0) const
void addQueryItem(const QString &key, const QString &value)
void setQuery(const QString &query)
QString recodeFromUser(const QString &input) const
QString recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const
Map::const_iterator findKey(const QString &key) const
Map::iterator findKey(const QString &key)
\inmodule QtCore
Definition qurlquery.h:20
void setQuery(const QString &queryString)
Parses the query string in queryString and sets the internal items to the values found there.
void removeAllQueryItems(const QString &key)
Removes all the query string pairs whose key is equal to key from the URL.
void setQueryItems(const QList< QPair< QString, QString > > &query)
Sets the items in this QUrlQuery object to query.
void clear()
Clears this QUrlQuery object by removing all of the key-value pairs currently stored.
bool isEmpty() const
Returns true if this QUrlQuery object contains no key-value pairs, such as after being default-constr...
static constexpr char16_t defaultQueryPairDelimiter() noexcept
Returns the default character for separating keys-value pairs from each other, an ampersand ("&").
Definition qurlquery.h:68
void addQueryItem(const QString &key, const QString &value)
Appends the pair key = value to the end of the query string of the URL.
bool operator==(const QUrlQuery &other) const
Returns true if this object and the other object contain the same contents, in the same order,...
bool hasQueryItem(const QString &key) const
Returns true if there is a query string pair whose key is equal to key from the URL.
void removeQueryItem(const QString &key)
Removes the query string pair whose key is equal to key from the URL.
QList< QPair< QString, QString > > queryItems(QUrl::ComponentFormattingOptions encoding=QUrl::PrettyDecoded) const
Returns the query string of the URL, as a map of keys and values, using the options specified in enco...
static constexpr char16_t defaultQueryValueDelimiter() noexcept
Returns the default character for separating keys from values in the query, an equal sign ("=").
Definition qurlquery.h:67
QString query(QUrl::ComponentFormattingOptions encoding=QUrl::PrettyDecoded) const
Returns the reconstructed query string, formed from the key-value pairs currently stored in this QUrl...
QUrlQuery()
Constructs an empty QUrlQuery object.
void setQueryDelimiters(QChar valueDelimiter, QChar pairDelimiter)
Sets the characters used for delimiting between keys and values, and between key-value pairs in the U...
QUrlQuery & operator=(const QUrlQuery &other)
Move-assigns other to this QUrlQuery instance.
QStringList allQueryItemValues(const QString &key, QUrl::ComponentFormattingOptions encoding=QUrl::PrettyDecoded) const
Returns the a list of query string values whose key is equal to key from the URL, using the options s...
~QUrlQuery()
Destroys this QUrlQuery object.
QString queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding=QUrl::PrettyDecoded) const
Returns the query value associated with key key from the URL, using the options specified in encoding...
QChar queryValueDelimiter() const
Returns the character used to delimit between keys and values when reconstructing the query string in...
QChar queryPairDelimiter() const
Returns the character used to delimit between keys-value pairs when reconstructing the query string i...
bool isDetached() const
\inmodule QtCore
Definition qurl.h:94
bool hasQuery() const
Definition qurl.cpp:2510
QString query(ComponentFormattingOptions=PrettyDecoded) const
Returns the query string of the URL if there's a query string, or an empty result if not.
Definition qurl.cpp:2606
@ PrettyDecoded
Definition qurl.h:121
@ EncodeDelimiters
Definition qurl.h:124
@ DecodeReserved
Definition qurl.h:126
QHash< int, QWidget * > hash
[35multi]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
std::pair< T1, T2 > QPair
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
size_t qHash(const QFileSystemWatcherPathKey &key, size_t seed=0)
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum query
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLenum GLenum input
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
unsigned short ushort
Definition qtypes.h:28
QT_BEGIN_NAMESPACE Q_AUTOTEST_EXPORT qsizetype qt_urlRecode(QString &appendTo, QStringView url, QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications=nullptr)
#define decode(x)
static void recodeAndAppend(QString &to, const QString &input, QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications)
QList< QPair< QString, QString > > Map
#define encode(x)
bool idempotentRecodeToUser(QUrl::ComponentFormattingOptions encoding)
QT_BEGIN_NAMESPACE typedef uchar * output
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
QGraphicsItem * item