summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qstring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qstring.cpp')
-rw-r--r--src/corelib/tools/qstring.cpp817
1 files changed, 414 insertions, 403 deletions
diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp
index 9921d5cfbb..beda0f4919 100644
--- a/src/corelib/tools/qstring.cpp
+++ b/src/corelib/tools/qstring.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 The Qt Company Ltd.
** Copyright (C) 2013 Intel Corporation
-** Contact: http://www.qt-project.org/legal
+** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
@@ -11,9 +11,9 @@
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
@@ -24,8 +24,8 @@
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
@@ -70,6 +70,7 @@
#include "qchar.cpp"
#include "qstringmatcher.cpp"
#include "qstringiterator_p.h"
+#include "qstringalgorithms_p.h"
#include "qthreadstorage.h"
#ifdef Q_OS_WIN
@@ -239,7 +240,7 @@ void qt_from_latin1(ushort *dst, const char *str, size_t size)
dst += offset;
str += offset;
# ifdef Q_COMPILER_LAMBDA
- return UnrollTailLoop<15>::exec(size, [=](int i) { dst[i] = (uchar)str[i]; });
+ return UnrollTailLoop<15>::exec(int(size), [=](int i) { dst[i] = (uchar)str[i]; });
# endif
#endif
#if defined(__mips_dsp)
@@ -731,12 +732,30 @@ inline char qToLower(char ch)
const QString::Null QString::null = { };
/*!
+ \macro QT_RESTRICTED_CAST_FROM_ASCII
+ \relates QString
+
+ Defining this macro disables most automatic conversions from source
+ literals and 8-bit data to unicode QStrings, but allows the use of
+ the \c{QChar(char)} and \c{QString(const char (&ch)[N]} constructors,
+ and the \c{QString::operator=(const char (&ch)[N])} assignment operator
+ giving most of the type-safety benefits of QT_NO_CAST_FROM_ASCII
+ but does not require user code to wrap character and string literals
+ with QLatin1Char, QLatin1String or similar.
+
+ Using this macro together with source strings outside the 7-bit range,
+ non-literals, or literals with embedded NUL characters is undefined.
+
+ \sa QT_NO_CAST_FROM_ASCII, QT_NO_CAST_TO_ASCII
+*/
+
+/*!
\macro QT_NO_CAST_FROM_ASCII
\relates QString
Disables automatic conversions from 8-bit strings (char *) to unicode QStrings
- \sa QT_NO_CAST_TO_ASCII, QT_NO_CAST_FROM_BYTEARRAY
+ \sa QT_NO_CAST_TO_ASCII, QT_RESTRICTED_CAST_FROM_ASCII, QT_NO_CAST_FROM_BYTEARRAY
*/
/*!
@@ -745,7 +764,7 @@ const QString::Null QString::null = { };
disables automatic conversion from QString to 8-bit strings (char *)
- \sa QT_NO_CAST_FROM_ASCII, QT_NO_CAST_FROM_BYTEARRAY
+ \sa QT_NO_CAST_FROM_ASCII, QT_RESTRICTED_CAST_FROM_ASCII, QT_NO_CAST_FROM_BYTEARRAY
*/
/*!
@@ -759,7 +778,7 @@ const QString::Null QString::null = { };
Note: This only works for compilers that support warnings for
deprecated API.
- \sa QT_NO_CAST_TO_ASCII, QT_NO_CAST_FROM_ASCII
+ \sa QT_NO_CAST_TO_ASCII, QT_NO_CAST_FROM_ASCII, QT_RESTRICTED_CAST_FROM_ASCII
*/
/*!
@@ -993,6 +1012,9 @@ const QString::Null QString::null = { };
\list
\li \c QT_NO_CAST_FROM_ASCII disables automatic conversions from
C string literals and pointers to Unicode.
+ \li \c QT_RESTRICTED_CAST_FROM_ASCII allows automatic conversions
+ from C characters and character arrays, but disables automatic
+ conversions from character pointers to Unicode.
\li \c QT_NO_CAST_TO_ASCII disables automatic conversion from QString
to C strings.
\endlist
@@ -1310,6 +1332,12 @@ const QString::Null QString::null = { };
can be useful if you want to ensure that all user-visible strings
go through QObject::tr(), for example.
+ \note Defining QT_RESTRICTED_CAST_FROM_ASCII also disables
+ this constructor, but enables a \c{QString(const char (&ch)[N])}
+ constructor instead. Using non-literal input, or input with
+ embedded NUL characters, or non-7-bit characters is undefined
+ in this case.
+
\sa fromLatin1(), fromLocal8Bit(), fromUtf8()
*/
@@ -1328,7 +1356,7 @@ const QString::Null QString::null = { };
windows) and ucs4 if the size of wchar_t is 4 bytes (most Unix
systems).
- \sa fromUtf16(), fromLatin1(), fromLocal8Bit(), fromUtf8(), fromUcs4()
+ \sa fromUtf16(), fromLatin1(), fromLocal8Bit(), fromUtf8(), fromUcs4(), fromStdU16String(), fromStdU32String()
*/
/*! \fn QString QString::fromWCharArray(const wchar_t *string, int size)
@@ -1353,7 +1381,7 @@ const QString::Null QString::null = { };
This method is mostly useful to pass a QString to a function
that accepts a std::wstring object.
- \sa utf16(), toLatin1(), toUtf8(), toLocal8Bit()
+ \sa utf16(), toLatin1(), toUtf8(), toLocal8Bit(), toStdU16String(), toStdU32String()
*/
int QString::toUcs4_helper(const ushort *uc, int length, uint *out)
@@ -1656,8 +1684,11 @@ void QString::resize(int size)
void QString::reallocData(uint alloc, bool grow)
{
- if (grow)
+ if (grow) {
+ if (alloc > (uint(MaxAllocSize) - sizeof(Data)) / sizeof(QChar))
+ qBadAlloc();
alloc = qAllocMore(alloc * sizeof(QChar), sizeof(Data)) / sizeof(QChar);
+ }
if (d->ref.isShared() || IS_RAW_DATA(d)) {
Data::AllocationOptions allocOptions(d->capacityReserved ? Data::CapacityReserved : 0);
@@ -1703,7 +1734,7 @@ void QString::expand(int i)
string.
*/
-QString &QString::operator=(const QString &other)
+QString &QString::operator=(const QString &other) Q_DECL_NOTHROW
{
other.d->ref.ref();
if (!d->ref.deref())
@@ -1748,10 +1779,11 @@ QString &QString::operator=(const QString &other)
Assigns \a str to this string. The const char pointer is converted
to Unicode using the fromUtf8() function.
- You can disable this operator by defining \c
- QT_NO_CAST_FROM_ASCII when you compile your applications. This
- can be useful if you want to ensure that all user-visible strings
+ You can disable this operator by defining \c QT_NO_CAST_FROM_ASCII
+ or \c QT_RESTRICTED_CAST_FROM_ASCII when you compile your applications.
+ This can be useful if you want to ensure that all user-visible strings
go through QObject::tr(), for example.
+
*/
/*! \fn QString &QString::operator=(char ch)
@@ -3664,14 +3696,39 @@ int QString::count(const QRegExp& rx) const
*/
int QString::indexOf(const QRegularExpression& re, int from) const
{
+ return indexOf(re, from, Q_NULLPTR);
+}
+
+/*!
+ \overload
+ \since 5.5
+
+ Returns the index position of the first match of the regular
+ expression \a re in the string, searching forward from index
+ position \a from. Returns -1 if \a re didn't match anywhere.
+
+ If the match is successful and \a rmatch is not a null pointer, it also
+ writes the results of the match into the QRegularExpressionMatch object
+ pointed to by \a rmatch.
+
+ Example:
+
+ \snippet qstring/main.cpp 97
+*/
+int QString::indexOf(const QRegularExpression &re, int from, QRegularExpressionMatch *rmatch) const
+{
if (!re.isValid()) {
qWarning("QString::indexOf: invalid QRegularExpression object");
return -1;
}
QRegularExpressionMatch match = re.match(*this, from);
- if (match.hasMatch())
- return match.capturedStart();
+ if (match.hasMatch()) {
+ const int ret = match.capturedStart();
+ if (rmatch)
+ *rmatch = qMove(match);
+ return ret;
+ }
return -1;
}
@@ -3690,22 +3747,45 @@ int QString::indexOf(const QRegularExpression& re, int from) const
*/
int QString::lastIndexOf(const QRegularExpression &re, int from) const
{
+ return lastIndexOf(re, from, Q_NULLPTR);
+}
+
+/*!
+ \overload
+ \since 5.5
+
+ Returns the index position of the last match of the regular
+ expression \a re in the string, which starts before the index
+ position \a from. Returns -1 if \a re didn't match anywhere.
+
+ If the match is successful and \a rmatch is not a null pointer, it also
+ writes the results of the match into the QRegularExpressionMatch object
+ pointed to by \a rmatch.
+
+ Example:
+
+ \snippet qstring/main.cpp 98
+*/
+int QString::lastIndexOf(const QRegularExpression &re, int from, QRegularExpressionMatch *rmatch) const
+{
if (!re.isValid()) {
qWarning("QString::lastIndexOf: invalid QRegularExpression object");
return -1;
}
int endpos = (from < 0) ? (size() + from + 1) : (from + 1);
-
QRegularExpressionMatchIterator iterator = re.globalMatch(*this);
int lastIndex = -1;
while (iterator.hasNext()) {
QRegularExpressionMatch match = iterator.next();
int start = match.capturedStart();
- if (start < endpos)
+ if (start < endpos) {
lastIndex = start;
- else
+ if (rmatch)
+ *rmatch = qMove(match);
+ } else {
break;
+ }
}
return lastIndex;
@@ -3719,12 +3799,7 @@ int QString::lastIndexOf(const QRegularExpression &re, int from) const
*/
bool QString::contains(const QRegularExpression &re) const
{
- if (!re.isValid()) {
- qWarning("QString::contains: invalid QRegularExpression object");
- return false;
- }
- QRegularExpressionMatch match = re.match(*this);
- return match.hasMatch();
+ return contains(re, Q_NULLPTR);
}
/*!
@@ -3744,13 +3819,13 @@ bool QString::contains(const QRegularExpression &re) const
bool QString::contains(const QRegularExpression &re, QRegularExpressionMatch *match) const
{
if (!re.isValid()) {
- qWarning("QString::contains: invalid QRegularExpresssion object");
+ qWarning("QString::contains: invalid QRegularExpression object");
return false;
}
QRegularExpressionMatch m = re.match(*this);
bool hasMatch = m.hasMatch();
if (hasMatch && match)
- *match = m;
+ *match = qMove(m);
return hasMatch;
}
@@ -3912,12 +3987,14 @@ QString QString::section(const QString &sep, int start, int end, SectionFlags fl
#if !(defined(QT_NO_REGEXP) && defined(QT_NO_REGULAREXPRESSION))
class qt_section_chunk {
public:
- qt_section_chunk(int l, QString s) { length = l; string = s; }
+ qt_section_chunk() {}
+ qt_section_chunk(int l, QString s) : length(l), string(qMove(s)) {}
int length;
QString string;
};
+Q_DECLARE_TYPEINFO(qt_section_chunk, Q_MOVABLE_TYPE);
-static QString extractSections(const QList<qt_section_chunk> &sections,
+static QString extractSections(const QVector<qt_section_chunk> &sections,
int start,
int end,
QString::SectionFlags flags)
@@ -4003,7 +4080,7 @@ QString QString::section(const QRegExp &reg, int start, int end, SectionFlags fl
sep.setCaseSensitivity((flags & SectionCaseInsensitiveSeps) ? Qt::CaseInsensitive
: Qt::CaseSensitive);
- QList<qt_section_chunk> sections;
+ QVector<qt_section_chunk> sections;
int n = length(), m = 0, last_m = 0, last_len = 0;
while ((m = sep.indexIn(*this, m)) != -1) {
sections.append(qt_section_chunk(last_len, QString(uc + last_m, m - last_m)));
@@ -4048,7 +4125,7 @@ QString QString::section(const QRegularExpression &re, int start, int end, Secti
if (flags & SectionCaseInsensitiveSeps)
sep.setPatternOptions(sep.patternOptions() | QRegularExpression::CaseInsensitiveOption);
- QList<qt_section_chunk> sections;
+ QVector<qt_section_chunk> sections;
int n = length(), m = 0, last_m = 0, last_len = 0;
QRegularExpressionMatchIterator iterator = sep.globalMatch(*this);
while (iterator.hasNext()) {
@@ -4555,7 +4632,7 @@ QString QString::fromUtf8_helper(const char *str, int size)
QString makes a deep copy of the Unicode data.
- \sa utf16(), setUtf16()
+ \sa utf16(), setUtf16(), fromStdU16String()
*/
QString QString::fromUtf16(const ushort *unicode, int size)
{
@@ -4579,7 +4656,7 @@ QString QString::fromUtf16(const ushort *unicode, int size)
If \a size is -1 (default), \a unicode must be terminated
with a 0.
- \sa toUcs4(), fromUtf16(), utf16(), setUtf16(), fromWCharArray()
+ \sa toUcs4(), fromUtf16(), utf16(), setUtf16(), fromWCharArray(), fromStdU32String()
*/
QString QString::fromUcs4(const uint *unicode, int size)
{
@@ -4626,6 +4703,8 @@ QString& QString::setUnicode(const QChar *unicode, int size)
*/
/*!
+ \fn QString QString::simplified() const
+
Returns a string that has whitespace removed from the start
and the end, and that has each sequence of internal whitespace
replaced with a single space.
@@ -4640,83 +4719,19 @@ QString& QString::setUnicode(const QChar *unicode, int size)
\sa trimmed()
*/
-QString QString::simplified() const
+QString QString::simplified_helper(const QString &str)
{
- if (d->size == 0)
- return *this;
-
- const QChar * const start = reinterpret_cast<QChar *>(d->data());
- const QChar *from = start;
- const QChar *fromEnd = start + d->size;
- forever {
- QChar ch = *from;
- if (!ch.isSpace())
- break;
- if (++from == fromEnd) {
- // All-whitespace string
- QStringDataPtr empty = { Data::allocate(0) };
- return QString(empty);
- }
- }
- // This loop needs no underflow check, as we already determined that
- // the string contains non-whitespace. If the string has exactly one
- // non-whitespace, it will be checked twice - we can live with that.
- while (fromEnd[-1].isSpace())
- fromEnd--;
- // The rest of the function depends on the fact that we already know
- // that the last character in the source is no whitespace.
- const QChar *copyFrom = from;
- int copyCount;
- forever {
- if (++from == fromEnd) {
- // Only leading and/or trailing whitespace, if any at all
- return mid(copyFrom - start, from - copyFrom);
- }
- QChar ch = *from;
- if (!ch.isSpace())
- continue;
- if (ch != QLatin1Char(' ')) {
- copyCount = from - copyFrom;
- break;
- }
- ch = *++from;
- if (ch.isSpace()) {
- copyCount = from - copyFrom - 1;
- break;
- }
- }
- // 'from' now points at the non-trailing whitespace which made the
- // string not simplified in the first place. 'copyCount' is the number
- // of already simplified characters - at least one, obviously -
- // without a trailing space.
- QString result((fromEnd - from) + copyCount, Qt::Uninitialized);
- QChar *to = reinterpret_cast<QChar *>(result.d->data());
- ::memcpy(to, copyFrom, copyCount * 2);
- to += copyCount;
- fromEnd--;
- QChar ch;
- forever {
- *to++ = QLatin1Char(' ');
- do {
- ch = *++from;
- } while (ch.isSpace());
- if (from == fromEnd)
- break;
- do {
- *to++ = ch;
- ch = *++from;
- if (from == fromEnd)
- goto done;
- } while (!ch.isSpace());
+ return QStringAlgorithms<const QString>::simplified_helper(str);
+}
- }
- done:
- *to++ = ch;
- result.truncate(to - reinterpret_cast<QChar *>(result.d->data()));
- return result;
+QString QString::simplified_helper(QString &str)
+{
+ return QStringAlgorithms<QString>::simplified_helper(str);
}
/*!
+ \fn QString QString::trimmed() const
+
Returns a string that has whitespace removed from the start and
the end.
@@ -4732,27 +4747,14 @@ QString QString::simplified() const
\sa simplified()
*/
-QString QString::trimmed() const
+QString QString::trimmed_helper(const QString &str)
{
- if (d->size == 0)
- return *this;
- const QChar *s = (const QChar*)d->data();
- if (!s->isSpace() && !s[d->size-1].isSpace())
- return *this;
- int start = 0;
- int end = d->size - 1;
- while (start<=end && s[start].isSpace()) // skip white space from start
- start++;
- if (start <= end) { // only white space
- while (end && s[end].isSpace()) // skip white space from end
- end--;
- }
- int l = end - start + 1;
- if (l <= 0) {
- QStringDataPtr empty = { Data::allocate(0) };
- return QString(empty);
- }
- return QString(s + start, l);
+ return QStringAlgorithms<const QString>::trimmed_helper(str);
+}
+
+QString QString::trimmed_helper(QString &str)
+{
+ return QStringAlgorithms<QString>::trimmed_helper(str);
}
/*! \fn const QChar QString::at(int position) const
@@ -5519,6 +5521,8 @@ QString QString::rightJustified(int width, QChar fill, bool truncate) const
}
/*!
+ \fn QString QString::toLower() const
+
Returns a lowercase copy of the string.
\snippet qstring/main.cpp 75
@@ -5529,132 +5533,114 @@ QString QString::rightJustified(int width, QChar fill, bool truncate) const
\sa toUpper(), QLocale::toLower()
*/
-QString QString::toLower() const
+namespace QUnicodeTables {
+struct LowercaseTraits
{
- const ushort *p = d->data();
- if (!p)
- return *this;
+ static signed short caseDiff(const Properties *prop)
+ { return prop->lowerCaseDiff; }
+ static bool caseSpecial(const Properties *prop)
+ { return prop->lowerCaseSpecial; }
+};
- const ushort *e = p + d->size;
- // this avoids out of bounds check in the loop
- while (e != p && QChar::isHighSurrogate(*(e - 1)))
- --e;
+struct UppercaseTraits
+{
+ static signed short caseDiff(const Properties *prop)
+ { return prop->upperCaseDiff; }
+ static bool caseSpecial(const Properties *prop)
+ { return prop->upperCaseSpecial; }
+};
- const QUnicodeTables::Properties *prop;
- while (p != e) {
- if (QChar::isHighSurrogate(*p) && QChar::isLowSurrogate(p[1])) {
- ushort high = *p++;
- prop = qGetProp(QChar::surrogateToUcs4(high, *p));
+struct CasefoldTraits
+{
+ static signed short caseDiff(const Properties *prop)
+ { return prop->caseFoldDiff; }
+ static bool caseSpecial(const Properties *prop)
+ { return prop->caseFoldSpecial; }
+};
+
+template <typename Traits, typename T>
+Q_NEVER_INLINE
+static QString detachAndConvertCase(T &str, QStringIterator it)
+{
+ QString s = qMove(str); // will copy if T is const QString
+ QChar *pp = s.begin() + it.index(); // will detach if necessary
+ uint uc = it.nextUnchecked();
+ forever {
+ const QUnicodeTables::Properties *prop = qGetProp(uc);
+ signed short caseDiff = Traits::caseDiff(prop);
+
+ if (Q_UNLIKELY(Traits::caseSpecial(prop))) {
+ // slow path
+ const ushort *specialCase = specialCaseMap + caseDiff;
+ ushort length = *specialCase++;
+ int pos = pp - s.constBegin();
+ s.replace(pos, 1, reinterpret_cast<const QChar *>(specialCase), length);
+ pp = const_cast<QChar *>(s.constBegin()) + pos + length;
+ } else if (QChar::requiresSurrogates(uc)) {
+ *pp++ = QChar::highSurrogate(uc + caseDiff);
+ *pp++ = QChar::lowSurrogate(uc + caseDiff);
} else {
- prop = qGetProp(*p);
+ *pp++ = QChar(uc + caseDiff);
}
- if (prop->lowerCaseDiff) {
- if (QChar::isLowSurrogate(*p))
- --p; // safe; diff is 0 for surrogates
- QString s(d->size, Qt::Uninitialized);
- memcpy(s.d->data(), d->data(), (p - d->data())*sizeof(ushort));
- ushort *pp = s.d->data() + (p - d->data());
- while (p != e) {
- if (QChar::isHighSurrogate(*p) && QChar::isLowSurrogate(p[1])) {
- *pp = *p++;
- prop = qGetProp(QChar::surrogateToUcs4(*pp++, *p));
- } else {
- prop = qGetProp(*p);
- }
- if (prop->lowerCaseSpecial) {
- const ushort *specialCase = specialCaseMap + prop->lowerCaseDiff;
- ushort length = *specialCase++;
- int pos = pp - s.d->data();
- s.resize(s.d->size + length - 1);
- pp = s.d->data() + pos;
- while (length--)
- *pp++ = *specialCase++;
- } else {
- *pp++ = *p + prop->lowerCaseDiff;
- }
- ++p;
- }
-
- // this restores high surrogate parts eaten above, if any
- while (e != d->data() + d->size)
- *pp++ = *e++;
+ if (!it.hasNext())
return s;
- }
- ++p;
+
+ uc = it.nextUnchecked();
}
- return *this;
}
-/*!
- Returns the case folded equivalent of the string. For most Unicode
- characters this is the same as toLower().
-*/
-QString QString::toCaseFolded() const
+template <typename Traits, typename T>
+static QString convertCase(T &str)
{
- const ushort *p = d->data();
- if (!p)
- return *this;
+ const QChar *p = str.constBegin();
+ const QChar *e = p + str.size();
- const ushort *e = p + d->size;
// this avoids out of bounds check in the loop
- while (e != p && QChar::isHighSurrogate(*(e - 1)))
+ while (e != p && e[-1].isHighSurrogate())
--e;
const QUnicodeTables::Properties *prop;
- while (p != e) {
- if (QChar::isHighSurrogate(*p) && QChar::isLowSurrogate(p[1])) {
- ushort high = *p++;
- prop = qGetProp(QChar::surrogateToUcs4(high, *p));
- } else {
- prop = qGetProp(*p);
- }
- if (prop->caseFoldDiff) {
- if (QChar::isLowSurrogate(*p))
- --p; // safe; diff is 0 for surrogates
- QString s(d->size, Qt::Uninitialized);
- memcpy(s.d->data(), d->data(), (p - d->data())*sizeof(ushort));
- ushort *pp = s.d->data() + (p - d->data());
- while (p != e) {
- if (QChar::isHighSurrogate(*p) && QChar::isLowSurrogate(p[1])) {
- *pp = *p++;
- prop = qGetProp(QChar::surrogateToUcs4(*pp++, *p));
- } else {
- prop = qGetProp(*p);
- }
- if (prop->caseFoldSpecial) {
- const ushort *specialCase = specialCaseMap + prop->caseFoldDiff;
- ushort length = *specialCase++;
-#if 0
- int pos = pp - s.d->data;
- s.resize(s.d->size + length - 1);
- pp = s.d->data + pos;
- while (length--)
- *pp++ = *specialCase++;
-#else
- //### we currently don't support full case foldings
- Q_ASSERT(length == 1);
- Q_UNUSED(length)
- *pp++ = *specialCase;
-#endif
- } else {
- *pp++ = *p + prop->caseFoldDiff;
- }
- ++p;
- }
+ QStringIterator it(p, e);
+ for ( ; it.hasNext(); it.advanceUnchecked()) {
+ prop = qGetProp(it.peekNextUnchecked());
+ if (Traits::caseDiff(prop))
+ return detachAndConvertCase<Traits>(str, it);
+ }
+ return qMove(str);
+}
+} // namespace QUnicodeTables
- // this restores high surrogate parts eaten above, if any
- while (e != d->data() + d->size)
- *pp++ = *e++;
+QString QString::toLower_helper(const QString &str)
+{
+ return QUnicodeTables::convertCase<QUnicodeTables::LowercaseTraits>(str);
+}
- return s;
- }
- ++p;
- }
- return *this;
+QString QString::toLower_helper(QString &str)
+{
+ return QUnicodeTables::convertCase<QUnicodeTables::LowercaseTraits>(str);
+}
+
+/*!
+ \fn QString QString::toCaseFolded() const
+
+ Returns the case folded equivalent of the string. For most Unicode
+ characters this is the same as toLower().
+*/
+
+QString QString::toCaseFolded_helper(const QString &str)
+{
+ return QUnicodeTables::convertCase<QUnicodeTables::CasefoldTraits>(str);
+}
+
+QString QString::toCaseFolded_helper(QString &str)
+{
+ return QUnicodeTables::convertCase<QUnicodeTables::CasefoldTraits>(str);
}
/*!
+ \fn QString QString::toUpper() const
+
Returns an uppercase copy of the string.
\snippet qstring/main.cpp 81
@@ -5664,65 +5650,33 @@ QString QString::toCaseFolded() const
\sa toLower(), QLocale::toLower()
*/
-QString QString::toUpper() const
-{
- const ushort *p = d->data();
- if (!p)
- return *this;
- const ushort *e = p + d->size;
- // this avoids out of bounds check in the loop
- while (e != p && QChar::isHighSurrogate(*(e - 1)))
- --e;
-
- const QUnicodeTables::Properties *prop;
- while (p != e) {
- if (QChar::isHighSurrogate(*p) && QChar::isLowSurrogate(p[1])) {
- ushort high = *p++;
- prop = qGetProp(QChar::surrogateToUcs4(high, *p));
- } else {
- prop = qGetProp(*p);
- }
- if (prop->upperCaseDiff) {
- if (QChar::isLowSurrogate(*p))
- --p; // safe; diff is 0 for surrogates
- QString s(d->size, Qt::Uninitialized);
- memcpy(s.d->data(), d->data(), (p - d->data())*sizeof(ushort));
- ushort *pp = s.d->data() + (p - d->data());
- while (p != e) {
- if (QChar::isHighSurrogate(*p) && QChar::isLowSurrogate(p[1])) {
- *pp = *p++;
- prop = qGetProp(QChar::surrogateToUcs4(*pp++, *p));
- } else {
- prop = qGetProp(*p);
- }
- if (prop->upperCaseSpecial) {
- const ushort *specialCase = specialCaseMap + prop->upperCaseDiff;
- ushort length = *specialCase++;
- int pos = pp - s.d->data();
- s.resize(s.d->size + length - 1);
- pp = s.d->data() + pos;
- while (length--)
- *pp++ = *specialCase++;
- } else {
- *pp++ = *p + prop->upperCaseDiff;
- }
- ++p;
- }
+QString QString::toUpper_helper(const QString &str)
+{
+ return QUnicodeTables::convertCase<QUnicodeTables::UppercaseTraits>(str);
+}
- // this restores high surrogate parts eaten above, if any
- while (e != d->data() + d->size)
- *pp++ = *e++;
+QString QString::toUpper_helper(QString &str)
+{
+ return QUnicodeTables::convertCase<QUnicodeTables::UppercaseTraits>(str);
+}
- return s;
- }
- ++p;
- }
+/*!
+ \obsolete Use asprintf(), arg() or QTextStream instead.
+*/
+QString &QString::sprintf(const char *cformat, ...)
+{
+ va_list ap;
+ va_start(ap, cformat);
+ *this = vasprintf(cformat, ap);
+ va_end(ap);
return *this;
}
// ### Qt 6: Consider whether this function shouldn't be removed See task 202871.
/*!
+ \since 5.5
+
Safely builds a formatted string from the format string \a cformat
and an arbitrary list of arguments.
@@ -5739,7 +5693,7 @@ QString QString::toUpper() const
a \c{wchar_t*}, and might also produce compiler warnings on platforms
where the size of \c {wchar_t} is not 16 bits.
- \warning We do not recommend using QString::sprintf() in new Qt
+ \warning We do not recommend using QString::asprintf() in new Qt
code. Instead, consider using QTextStream or arg(), both of
which support Unicode strings seamlessly and are type-safe.
Here's an example that uses QTextStream:
@@ -5754,32 +5708,42 @@ QString QString::toUpper() const
\sa arg()
*/
-QString &QString::sprintf(const char *cformat, ...)
+QString QString::asprintf(const char *cformat, ...)
{
va_list ap;
va_start(ap, cformat);
- QString &s = vsprintf(cformat, ap);
+ const QString s = vasprintf(cformat, ap);
va_end(ap);
return s;
}
/*!
- Equivalent method to sprintf(), but takes a va_list \a ap
- instead a list of variable arguments. See the sprintf()
+ \obsolete Use vasprintf(), arg() or QTextStream instead.
+*/
+QString &QString::vsprintf(const char *cformat, va_list ap)
+{
+ return *this = vasprintf(cformat, ap);
+}
+
+/*!
+ \fn QString::vasprintf(const char *cformat, va_list ap)
+ \since 5.5
+
+ Equivalent method to asprintf(), but takes a va_list \a ap
+ instead a list of variable arguments. See the asprintf()
documentation for an explanation of \a cformat.
This method does not call the va_end macro, the caller
is responsible to call va_end on \a ap.
- \sa sprintf()
+ \sa asprintf()
*/
-QString &QString::vsprintf(const char* cformat, va_list ap)
+QString QString::vasprintf(const char *cformat, va_list ap)
{
if (!cformat || !*cformat) {
// Qt 1.x compat
- *this = fromLatin1("");
- return *this;
+ return fromLatin1("");
}
// Parse cformat
@@ -6095,9 +6059,7 @@ QString &QString::vsprintf(const char* cformat, va_list ap)
result.append(subst.rightJustified(width));
}
- *this = result;
-
- return *this;
+ return result;
}
/*!
@@ -6436,14 +6398,7 @@ float QString::toFloat(bool *ok) const
*/
QString &QString::setNum(qlonglong n, int base)
{
-#if defined(QT_CHECK_RANGE)
- if (base < 2 || base > 36) {
- qWarning("QString::setNum: Invalid base (%d)", base);
- base = 10;
- }
-#endif
- *this = QLocaleData::c()->longLongToString(n, -1, base);
- return *this;
+ return *this = number(n, base);
}
/*!
@@ -6451,14 +6406,7 @@ QString &QString::setNum(qlonglong n, int base)
*/
QString &QString::setNum(qulonglong n, int base)
{
-#if defined(QT_CHECK_RANGE)
- if (base < 2 || base > 36) {
- qWarning("QString::setNum: Invalid base (%d)", base);
- base = 10;
- }
-#endif
- *this = QLocaleData::c()->unsLongLongToString(n, -1, base);
- return *this;
+ return *this = number(n, base);
}
/*! \fn QString &QString::setNum(short n, int base)
@@ -6489,32 +6437,7 @@ QString &QString::setNum(qulonglong n, int base)
QString &QString::setNum(double n, char f, int prec)
{
- QLocaleData::DoubleForm form = QLocaleData::DFDecimal;
- uint flags = 0;
-
- if (qIsUpper(f))
- flags = QLocaleData::CapitalEorX;
- f = qToLower(f);
-
- switch (f) {
- case 'f':
- form = QLocaleData::DFDecimal;
- break;
- case 'e':
- form = QLocaleData::DFExponent;
- break;
- case 'g':
- form = QLocaleData::DFSignificantDigits;
- break;
- default:
-#if defined(QT_CHECK_RANGE)
- qWarning("QString::setNum: Invalid format char '%c'", f);
-#endif
- break;
- }
-
- *this = QLocaleData::c()->doubleToString(n, prec, form, -1, flags);
- return *this;
+ return *this = number(n, f, prec);
}
/*!
@@ -6552,9 +6475,7 @@ QString &QString::setNum(double n, char f, int prec)
QString QString::number(long n, int base)
{
- QString s;
- s.setNum(n, base);
- return s;
+ return number(qlonglong(n), base);
}
/*!
@@ -6564,9 +6485,7 @@ QString QString::number(long n, int base)
*/
QString QString::number(ulong n, int base)
{
- QString s;
- s.setNum(n, base);
- return s;
+ return number(qulonglong(n), base);
}
/*!
@@ -6574,9 +6493,7 @@ QString QString::number(ulong n, int base)
*/
QString QString::number(int n, int base)
{
- QString s;
- s.setNum(n, base);
- return s;
+ return number(qlonglong(n), base);
}
/*!
@@ -6584,9 +6501,7 @@ QString QString::number(int n, int base)
*/
QString QString::number(uint n, int base)
{
- QString s;
- s.setNum(n, base);
- return s;
+ return number(qulonglong(n), base);
}
/*!
@@ -6594,9 +6509,13 @@ QString QString::number(uint n, int base)
*/
QString QString::number(qlonglong n, int base)
{
- QString s;
- s.setNum(n, base);
- return s;
+#if defined(QT_CHECK_RANGE)
+ if (base < 2 || base > 36) {
+ qWarning("QString::setNum: Invalid base (%d)", base);
+ base = 10;
+ }
+#endif
+ return QLocaleData::c()->longLongToString(n, -1, base);
}
/*!
@@ -6604,9 +6523,13 @@ QString QString::number(qlonglong n, int base)
*/
QString QString::number(qulonglong n, int base)
{
- QString s;
- s.setNum(n, base);
- return s;
+#if defined(QT_CHECK_RANGE)
+ if (base < 2 || base > 36) {
+ qWarning("QString::setNum: Invalid base (%d)", base);
+ base = 10;
+ }
+#endif
+ return QLocaleData::c()->unsLongLongToString(n, -1, base);
}
@@ -6624,30 +6547,53 @@ QString QString::number(qulonglong n, int base)
*/
QString QString::number(double n, char f, int prec)
{
- QString s;
- s.setNum(n, f, prec);
- return s;
+ QLocaleData::DoubleForm form = QLocaleData::DFDecimal;
+ uint flags = 0;
+
+ if (qIsUpper(f))
+ flags = QLocaleData::CapitalEorX;
+ f = qToLower(f);
+
+ switch (f) {
+ case 'f':
+ form = QLocaleData::DFDecimal;
+ break;
+ case 'e':
+ form = QLocaleData::DFExponent;
+ break;
+ case 'g':
+ form = QLocaleData::DFSignificantDigits;
+ break;
+ default:
+#if defined(QT_CHECK_RANGE)
+ qWarning("QString::setNum: Invalid format char '%c'", f);
+#endif
+ break;
+ }
+
+ return QLocaleData::c()->doubleToString(n, prec, form, -1, flags);
}
namespace {
-template<class ResultList, class StringSource, typename MidMethod, typename Separtor>
-static ResultList splitString(const StringSource &source, MidMethod mid, const Separtor &sep,
+template<class ResultList, class StringSource>
+static ResultList splitString(const StringSource &source, const QChar *sep,
QString::SplitBehavior behavior, Qt::CaseSensitivity cs, const int separatorSize)
{
ResultList list;
int start = 0;
int end;
int extra = 0;
- while ((end = source.indexOf(sep, start + extra, cs)) != -1) {
+ while ((end = qFindString(source.constData(), source.size(), start + extra, sep, separatorSize, cs)) != -1) {
if (start != end || behavior == QString::KeepEmptyParts)
- list.append((source.*mid)(start, end - start));
+ list.append(source.mid(start, end - start));
start = end + separatorSize;
extra = (separatorSize == 0 ? 1 : 0);
}
if (start != source.size() || behavior == QString::KeepEmptyParts)
- list.append((source.*mid)(start, -1));
+ list.append(source.mid(start, -1));
return list;
}
+
} // namespace
/*!
@@ -6670,7 +6616,7 @@ static ResultList splitString(const StringSource &source, MidMethod mid, const S
*/
QStringList QString::split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
{
- return splitString<QStringList>(*this, &QString::mid, sep, behavior, cs, sep.size());
+ return splitString<QStringList>(*this, sep.constData(), behavior, cs, sep.size());
}
/*!
@@ -6693,14 +6639,14 @@ QStringList QString::split(const QString &sep, SplitBehavior behavior, Qt::CaseS
*/
QVector<QStringRef> QString::splitRef(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
{
- return splitString<QVector<QStringRef> >(*this, &QString::midRef, sep, behavior, cs, sep.size());
+ return splitString<QVector<QStringRef> >(QStringRef(this), sep.constData(), behavior, cs, sep.size());
}
/*!
\overload
*/
QStringList QString::split(QChar sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
{
- return splitString<QStringList>(*this, &QString::mid, sep, behavior, cs, 1);
+ return splitString<QStringList>(*this, &sep, behavior, cs, 1);
}
/*!
@@ -6709,7 +6655,7 @@ QStringList QString::split(QChar sep, SplitBehavior behavior, Qt::CaseSensitivit
*/
QVector<QStringRef> QString::splitRef(QChar sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
{
- return splitString<QVector<QStringRef> >(*this, &QString::midRef, sep, behavior, cs, 1);
+ return splitString<QVector<QStringRef> >(QStringRef(this), &sep, behavior, cs, 1);
}
/*!
@@ -6731,7 +6677,7 @@ QVector<QStringRef> QString::splitRef(QChar sep, SplitBehavior behavior, Qt::Cas
*/
QVector<QStringRef> QStringRef::split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const
{
- return splitString<QVector<QStringRef> >(*this, &QStringRef::mid, sep, behavior, cs, sep.size());
+ return splitString<QVector<QStringRef> >(*this, sep.constData(), behavior, cs, sep.size());
}
/*!
@@ -6740,7 +6686,7 @@ QVector<QStringRef> QStringRef::split(const QString &sep, QString::SplitBehavior
*/
QVector<QStringRef> QStringRef::split(QChar sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const
{
- return splitString<QVector<QStringRef> >(*this, &QStringRef::mid, sep, behavior, cs, 1);
+ return splitString<QVector<QStringRef> >(*this, &sep, behavior, cs, 1);
}
#ifndef QT_NO_REGEXP
@@ -7225,7 +7171,7 @@ static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int f
First, \c arg(i) replaces \c %1. Then \c arg(total) replaces \c
%2. Finally, \c arg(fileName) replaces \c %3.
- One advantage of using arg() over sprintf() is that the order of the
+ One advantage of using arg() over asprintf() is that the order of the
numbered place markers can change, if the application's strings are
translated into other languages, but each arg() will still replace
the lowest numbered unreplaced place marker, no matter where it
@@ -7654,49 +7600,79 @@ static int getEscape(const QChar *uc, int *pos, int len, int maxNumber = 999)
return -1;
}
+namespace {
+class ArgMapper {
+ QVarLengthArray<int, 16> argPosToNumberMap; // maps from argument position to number
+public:
+ void found(int n) { argPosToNumberMap.push_back(n); }
+
+ struct AssignmentResult {
+ int numArgs;
+ int lastNumber;
+ };
+
+ AssignmentResult assignArgumentNumberToEachOfTheNs(int numArgs)
+ {
+ std::sort(argPosToNumberMap.begin(), argPosToNumberMap.end());
+ argPosToNumberMap.erase(std::unique(argPosToNumberMap.begin(), argPosToNumberMap.end()),
+ argPosToNumberMap.end());
+
+ if (argPosToNumberMap.size() > numArgs)
+ argPosToNumberMap.resize(numArgs);
+
+ int lastNumber = argPosToNumberMap.empty() ? -1 : argPosToNumberMap.back();
+ int arg = argPosToNumberMap.size();
+
+ const AssignmentResult result = {arg, lastNumber};
+ return result;
+ }
+
+ int numberToArgsIndex(int number) const
+ {
+ if (number != -1) {
+ const int * const it = std::find(argPosToNumberMap.begin(), argPosToNumberMap.end(), number);
+ return it == argPosToNumberMap.end() ? -1 : it - argPosToNumberMap.begin();
+ } else {
+ return -1;
+ }
+ }
+};
+} // unnamed namespace
+
QString QString::multiArg(int numArgs, const QString **args) const
{
QString result;
- QMap<int, int> numbersUsed;
+ ArgMapper mapper;
const QChar *uc = (const QChar *) d->data();
const int len = d->size;
const int end = len - 1;
- int lastNumber = -1;
int i = 0;
- // populate the numbersUsed map with the %n's that actually occur in the string
+ // populate the arg-mapper with the %n's that actually occur in the string
while (i < end) {
if (uc[i] == QLatin1Char('%')) {
int number = getEscape(uc, &i, len);
if (number != -1) {
- numbersUsed.insert(number, -1);
+ mapper.found(number);
continue;
}
}
++i;
}
- // assign an argument number to each of the %n's
- QMap<int, int>::iterator j = numbersUsed.begin();
- QMap<int, int>::iterator jend = numbersUsed.end();
- int arg = 0;
- while (j != jend && arg < numArgs) {
- *j = arg++;
- lastNumber = j.key();
- ++j;
- }
+ const ArgMapper::AssignmentResult r = mapper.assignArgumentNumberToEachOfTheNs(numArgs);
// sanity
- if (numArgs > arg) {
- qWarning("QString::arg: %d argument(s) missing in %s", numArgs - arg, toLocal8Bit().data());
- numArgs = arg;
+ if (numArgs > r.numArgs) {
+ qWarning("QString::arg: %d argument(s) missing in %s", numArgs - r.numArgs, toLocal8Bit().data());
+ numArgs = r.numArgs;
}
i = 0;
while (i < len) {
if (uc[i] == QLatin1Char('%') && i != end) {
- int number = getEscape(uc, &i, len, lastNumber);
- int arg = numbersUsed[number];
+ int number = getEscape(uc, &i, len, r.lastNumber);
+ int arg = mapper.numberToArgsIndex(number);
if (number != -1 && arg != -1) {
result += *args[arg];
continue;
@@ -7942,6 +7918,46 @@ QString &QString::setRawData(const QChar *unicode, int size)
return *this;
}
+/*! \fn QString QString::fromStdU16String(const std::u16string &str)
+ \since 5.5
+
+ Returns a copy of the \a str string. The given string is assumed
+ to be encoded in UTF-16.
+
+ \sa fromUtf16(), fromStdWString(), fromStdU32String()
+*/
+
+/*!
+ \fn std::u16string QString::toStdU16String() const
+ \since 5.5
+
+ Returns a std::u16string object with the data contained in this
+ QString. The Unicode data is the same as returned by the utf16()
+ method.
+
+ \sa utf16(), toStdWString(), toStdU32String()
+*/
+
+/*! \fn QString QString::fromStdU32String(const std::u32string &str)
+ \since 5.5
+
+ Returns a copy of the \a str string. The given string is assumed
+ to be encoded in UCS-4.
+
+ \sa fromUcs4(), fromStdWString(), fromStdU16String()
+*/
+
+/*!
+ \fn std::u32string QString::toStdU32String() const
+ \since 5.5
+
+ Returns a std::u32string object with the data contained in this
+ QString. The Unicode data is the same as returned by the toUcs4()
+ method.
+
+ \sa toUcs4(), toStdWString(), toStdU16String()
+*/
+
/*! \class QLatin1String
\inmodule QtCore
\brief The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
@@ -9843,20 +9859,15 @@ QVector<uint> QStringRef::toUcs4() const
*/
QStringRef QStringRef::trimmed() const
{
- if (m_size == 0 || m_string == 0)
+ const QChar *begin = cbegin();
+ const QChar *end = cend();
+ QStringAlgorithms<const QStringRef>::trimmed_helper_positions(begin, end);
+ if (begin == cbegin() && end == cend())
return *this;
- const QChar *s = m_string->constData() + m_position;
- int start = 0;
- int end = m_size - 1;
- while (start <= end && s[start].isSpace()) // skip white space from start
- start++;
- if (start <= end) { // only white space
- while (end && s[end].isSpace()) // skip white space from end
- end--;
- }
- int l = end - start + 1;
- Q_ASSERT(l >= 0);
- return QStringRef(m_string, m_position + start, l);
+ if (begin == end)
+ return QStringRef();
+ int position = m_position + (begin - cbegin());
+ return QStringRef(m_string, position, end - begin);
}
/*!