Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
135typedef QList<std::pair<QString, QString> > Map;
136
138{
139public:
141 : valueDelimiter(QUrlQuery::defaultQueryValueDelimiter()),
142 pairDelimiter(QUrlQuery::defaultQueryPairDelimiter())
143 { if (!query.isEmpty()) setQuery(query); }
144
145 QString recodeFromUser(const QString &input) const;
146 QString recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const;
147
148 void setQuery(const QString &query);
149
150 void addQueryItem(const QString &key, const QString &value)
151 { itemList.append(std::make_pair(recodeFromUser(key), recodeFromUser(value))); }
152 int findRecodedKey(const QString &key, int from = 0) const
153 {
154 for (int i = from; i < itemList.size(); ++i)
155 if (itemList.at(i).first == key)
156 return i;
157 return itemList.size();
158 }
159 Map::const_iterator findKey(const QString &key) const
161 Map::iterator findKey(const QString &key)
163
167};
168
170{
171 if (d && d->ref.loadRelaxed() == 1)
172 return;
174 : new QUrlQueryPrivate);
175 x->ref.ref();
176 if (d && !d->ref.deref())
177 delete d;
178 d = x;
179}
180
181// Here's how we do the encoding in QUrlQuery
182// The RFC says these are the delimiters:
183// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
184// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
185// / "*" / "+" / "," / ";" / "="
186// And the definition of query is:
187// query = *( pchar / "/" / "?" )
188// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
189//
190// The strict definition of query says that it can have unencoded any
191// unreserved, sub-delim, ":", "@", "/" and "?". Or, by exclusion, excluded
192// delimiters are "#", "[" and "]" -- if those are present, they must be
193// percent-encoded. The fact that "[" and "]" should be encoded is probably a
194// mistake in the spec, so we ignore it and leave the decoded.
195//
196// The internal storage in the Map is equivalent to PrettyDecoded. That means
197// the getter methods, when called with the default encoding value, will not
198// have to recode anything (except for toString()).
199//
200// QUrlQuery handling of delimiters is quite simple: we never touch any of
201// them, except for the "#" character and the pair and value delimiters. Those
202// are always kept in their decoded forms.
203//
204// But when recreating the query string, in toString(), we must take care of
205// the special delimiters: the pair and value delimiters, as well as the "#"
206// character if unambiguous decoding is requested.
207
208#define decode(x) ushort(x)
209#define leave(x) ushort(0x100 | (x))
210#define encode(x) ushort(0x200 | (x))
211
213{
214 // note: duplicated in setQuery()
216 ushort prettyDecodedActions[] = {
217 decode(pairDelimiter.unicode()),
218 decode(valueDelimiter.unicode()),
219 decode('#'),
220 0
221 };
224 prettyDecodedActions))
225 return output;
226 return input;
227}
228
229inline bool idempotentRecodeToUser(QUrl::ComponentFormattingOptions encoding)
230{
231 return encoding == QUrl::PrettyDecoded;
232}
233
234inline QString QUrlQueryPrivate::recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const
235{
236 // our internal formats are stored in "PrettyDecoded" form
237 // and there are no ambiguous characters
238 if (idempotentRecodeToUser(encoding))
239 return input;
240
241 if (!(encoding & QUrl::EncodeDelimiters)) {
244 encoding, nullptr))
245 return output;
246 return input;
247 }
248
249 // re-encode the "#" character and the query delimiter pair
250 ushort actions[] = { encode(pairDelimiter.unicode()), encode(valueDelimiter.unicode()),
251 encode('#'), 0 };
253 if (qt_urlRecode(output, input, encoding, actions))
254 return output;
255 return input;
256}
257
259{
260 ushort prettyDecodedActions[] = {
261 decode(pairDelimiter.unicode()),
262 decode(valueDelimiter.unicode()),
263 decode('#'),
264 0
265 };
266
267 itemList.clear();
268 const QChar *pos = query.constData();
269 const QChar *const end = pos + query.size();
270 while (pos != end) {
271 const QChar *begin = pos;
272 const QChar *delimiter = nullptr;
273 while (pos != end) {
274 // scan for the component parts of this pair
275 if (!delimiter && *pos == valueDelimiter)
276 delimiter = pos;
277 if (*pos == pairDelimiter)
278 break;
279 ++pos;
280 }
281 if (!delimiter)
282 delimiter = pos;
283
284 // pos is the end of this pair (the end of the string or the pair delimiter)
285 // delimiter points to the value delimiter or to the end of this pair
286
287 QString key;
288 if (!qt_urlRecode(key, QStringView{begin, delimiter},
290 prettyDecodedActions))
291 key = QString(begin, delimiter - begin);
292
293 if (delimiter == pos) {
294 // the value delimiter wasn't found, store a null value
295 itemList.append(std::make_pair(key, QString()));
296 } else if (delimiter + 1 == pos) {
297 // if the delimiter was found but the value is empty, store empty-but-not-null
298 itemList.append(std::make_pair(key, QString(0, Qt::Uninitialized)));
299 } else {
301 if (!qt_urlRecode(value, QStringView{delimiter + 1, pos},
303 prettyDecodedActions))
304 value = QString(delimiter + 1, pos - delimiter - 1);
305 itemList.append(std::make_pair(key, value));
306 }
307
308 if (pos != end)
309 ++pos;
310 }
311}
312
313// allow QUrlQueryPrivate to detach from null
314template <> inline QUrlQueryPrivate *
316{
317 return d ? new QUrlQueryPrivate(*d) : new QUrlQueryPrivate;
318}
319
327 : d(nullptr)
328{
329}
330
337QUrlQuery::QUrlQuery(const QString &queryString)
338 : d(queryString.isEmpty() ? nullptr : new QUrlQueryPrivate(queryString))
339{
340}
341
351 : d(nullptr)
352{
353 // use internals to avoid unnecessary recoding
354 // ### FIXME: actually do it
355 if (url.hasQuery())
356 d = new QUrlQueryPrivate(url.query());
357}
358
364 : d(other.d)
365{
366}
367
374 : d(std::move(other.d))
375{
376}
377
383{
384 d = other.d;
385 return *this;
386}
387
399{
400 // d auto-deletes
401}
402
410bool comparesEqual(const QUrlQuery &lhs, const QUrlQuery &rhs)
411{
412 if (lhs.d == rhs.d)
413 return true;
414 if (lhs.d && rhs.d)
415 // keep in sync with qHash(QUrlQuery):
416 return lhs.d->valueDelimiter == rhs.d->valueDelimiter &&
417 lhs.d->pairDelimiter == rhs.d->pairDelimiter &&
418 lhs.d->itemList == rhs.d->itemList;
419
420 const QUrlQueryPrivate *x = lhs.d ? lhs.d.data() : rhs.d.data();
422 x->pairDelimiter == QUrlQuery::defaultQueryPairDelimiter() &&
423 x->itemList.isEmpty();
424}
425
433size_t qHash(const QUrlQuery &key, size_t seed) noexcept
434{
435 if (const QUrlQueryPrivate *d = key.d) {
437 // keep in sync with operator==:
438 seed = hash(seed, d->valueDelimiter);
439 seed = hash(seed, d->pairDelimiter);
440 seed = hash(seed, d->itemList);
441 }
442 return seed;
443}
444
452{
453 return d ? d->itemList.isEmpty() : true;
454}
455
460{
461 return d && d->ref.loadRelaxed() == 1;
462}
463
472{
473 if (d.constData())
474 d->itemList.clear();
475}
476
483void QUrlQuery::setQuery(const QString &queryString)
484{
485 d->setQuery(queryString);
486}
487
488static void recodeAndAppend(QString &to, const QString &input,
489 QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications)
490{
491 if (!qt_urlRecode(to, input, encoding, tableModifications))
492 to += input;
493}
494
510QString QUrlQuery::query(QUrl::ComponentFormattingOptions encoding) const
511{
512 if (!d)
513 return QString();
514
515 // unlike the component encoding, for the whole query we need to modify a little:
516 // - the "#" character is unambiguous, so we encode it in EncodeDelimiters mode
517 // - the query delimiter pair must always be encoded
518
519 // start with what's always encoded
520 ushort tableActions[] = {
521 encode(d->pairDelimiter.unicode()), // 0
522 encode(d->valueDelimiter.unicode()), // 1
523 0, // 2
524 0
525 };
526 if (encoding & QUrl::EncodeDelimiters) {
527 tableActions[2] = encode('#');
528 }
529
531 Map::const_iterator it = d->itemList.constBegin();
532 Map::const_iterator end = d->itemList.constEnd();
533
534 {
535 int size = 0;
536 for ( ; it != end; ++it)
537 size += it->first.size() + 1 + it->second.size() + 1;
538 result.reserve(size + size / 4);
539 }
540
541 for (it = d->itemList.constBegin(); it != end; ++it) {
542 if (!result.isEmpty())
544 recodeAndAppend(result, it->first, encoding, tableActions);
545 if (!it->second.isNull()) {
547 recodeAndAppend(result, it->second, encoding, tableActions);
548 }
549 }
550 return result;
551}
552
581void QUrlQuery::setQueryDelimiters(QChar valueDelimiter, QChar pairDelimiter)
582{
583 d->valueDelimiter = valueDelimiter;
584 d->pairDelimiter = pairDelimiter;
585}
586
597
608
621void QUrlQuery::setQueryItems(const QList<std::pair<QString, QString> > &query)
622{
623 clear();
624 if (query.isEmpty())
625 return;
626
627 QUrlQueryPrivate *dd = d;
628 QList<std::pair<QString, QString> >::const_iterator it = query.constBegin(),
629 end = query.constEnd();
630 for ( ; it != end; ++it)
631 dd->addQueryItem(it->first, it->second);
632}
633
642QList<std::pair<QString, QString> > QUrlQuery::queryItems(QUrl::ComponentFormattingOptions encoding) const
643{
644 if (!d)
645 return QList<std::pair<QString, QString> >();
646 if (idempotentRecodeToUser(encoding))
647 return d->itemList;
648
649 QList<std::pair<QString, QString> > result;
650 Map::const_iterator it = d->itemList.constBegin();
651 Map::const_iterator end = d->itemList.constEnd();
652 result.reserve(d->itemList.size());
653 for ( ; it != end; ++it)
654 result << std::make_pair(d->recodeToUser(it->first, encoding),
655 d->recodeToUser(it->second, encoding));
656 return result;
657}
658
668{
669 if (!d)
670 return false;
671 return d->findKey(key) != d->itemList.constEnd();
672}
673
688{
690}
691
707QString QUrlQuery::queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const
708{
710 if (d) {
711 Map::const_iterator it = d->findKey(key);
712 if (it != d->itemList.constEnd())
713 result = d->recodeToUser(it->second, encoding);
714 }
715 return result;
716}
717
727QStringList QUrlQuery::allQueryItemValues(const QString &key, QUrl::ComponentFormattingOptions encoding) const
728{
730 if (d) {
731 QString encodedKey = d->recodeFromUser(key);
732 int idx = d->findRecodedKey(encodedKey);
733 while (idx < d->itemList.size()) {
734 result << d->recodeToUser(d->itemList.at(idx).second, encoding);
735 idx = d->findRecodedKey(encodedKey, idx + 1);
736 }
737 }
738 return result;
739}
740
752{
753 if (d.constData()) {
754 auto *p = d.data();
755 Map::iterator it = p->findKey(key);
756 if (it != p->itemList.end())
757 p->itemList.erase(it);
758 }
759}
760
770{
771 if (d.constData()) {
772 auto *p = d.data();
773 const QString encodedKey = p->recodeFromUser(key);
774 auto firstEqualsEncodedKey = [&encodedKey](const std::pair<QString, QString> &item) {
775 return item.first == encodedKey;
776 };
777 p->itemList.removeIf(firstEqualsEncodedKey);
778 }
779}
780
826
827#undef decode
828#undef leave
829#undef encode
bool ref() noexcept
T loadRelaxed() const noexcept
\inmodule QtCore
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
const_iterator constBegin() const noexcept
Definition qlist.h:632
iterator begin()
Definition qlist.h:625
void append(parameter_type t)
Definition qlist.h:458
const_iterator constEnd() const noexcept
Definition qlist.h:633
void clear()
Definition qlist.h:434
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:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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 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:70
void addQueryItem(const QString &key, const QString &value)
Appends the pair key = value to the end of the query string of the URL.
QList< std::pair< 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...
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.
static constexpr char16_t defaultQueryValueDelimiter() noexcept
Returns the default character for separating keys from values in the query, an equal sign ("=").
Definition qurlquery.h:69
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...
void setQueryItems(const QList< std::pair< QString, QString > > &query)
Sets the items in this QUrlQuery object to query.
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:2513
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:2609
@ 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
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
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
unsigned short ushort
Definition qtypes.h:33
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)
bool comparesEqual(const QUrlQuery &lhs, const QUrlQuery &rhs)
#define encode(x)
QList< std::pair< QString, QString > > Map
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