From ee85e9cc10bc6874c892b09fa54b5dbd79854069 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 6 Jul 2011 09:21:10 +0200 Subject: Support for compile time generated QStrings use with QString string(QStringLiteral("foo")) or QString string(qs("foo")). qs(...) can in almost all places replace QLatin1String. the Macro qs requires c++0x support or a compiler where sizeof(wchar_t) == 2. In case these conditions are not met, qs simply falls back to QLatin1String. The commit also cleans up the QStringData structure significantly, now using only 16 bytes for the header on 32 bit systems. The bitflags to store unicode directionality of the string and whether the string would require complex text processing to render are removed. They don't really belong here and the result can be calculated very fast in any case. Added an internal QRefCount class the encapsulates the reference counting semantics required where a negative refcount is never changed. This is required to be able to put the string data into a constant POD object. Change-Id: I5b1395213deca48b2d582a036a0e8a8358d26d14 Reviewed-on: http://codereview.qt.nokia.com/1218 Reviewed-by: Olivier Goffart Reviewed-by: Lars Knoll --- src/corelib/tools/qstring.cpp | 439 +++++++++++++++++++----------------------- src/corelib/tools/qstring.h | 130 +++++++++---- 2 files changed, 287 insertions(+), 282 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index afb396ce50..eb5804b254 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -797,10 +797,8 @@ const QString::Null QString::null = { }; \sa split() */ -QString::Data QString::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), - 0, 0, shared_null.array, 0, 0, 0, 0, 0, {0} }; -QString::Data QString::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), - 0, 0, shared_empty.array, 0, 0, 0, 0, 0, {0} }; +const QConstStringData<1> QString::shared_null = (const QConstStringData<1>) { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; +const QConstStringData<1> QString::shared_empty = (const QConstStringData<1>) { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; int QString::grow(int size) { @@ -1044,20 +1042,15 @@ int QString::toWCharArray(wchar_t *array) const QString::QString(const QChar *unicode, int size) { if (!unicode) { - d = &shared_null; - d->ref.ref(); + d = const_cast(&shared_null.str); } else if (size <= 0) { - d = &shared_empty; - d->ref.ref(); + d = const_cast(&shared_empty.str); } else { - d = (Data*) qMalloc(sizeof(Data)+size*sizeof(QChar)); + d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; - d->alloc = d->size = size; - d->clean = d->simpletext = d->righttoleft = d->capacity = 0; - d->data = d->array; - memcpy(d->array, unicode, size * sizeof(QChar)); - d->array[size] = '\0'; + *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + memcpy(d->data(), unicode, size * sizeof(QChar)); + d->data()[size] = '\0'; } } @@ -1073,24 +1066,19 @@ QString::QString(const QChar *unicode, int size) QString::QString(const QChar *unicode) { if (!unicode) { - d = &shared_null; - d->ref.ref(); + d = const_cast(&shared_null.str); } else { int size = 0; while (unicode[size] != 0) ++size; if (!size) { - d = &shared_empty; - d->ref.ref(); + d = const_cast(&shared_empty.str); } else { - d = (Data*) qMalloc(sizeof(Data)+size*sizeof(QChar)); + d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; - d->alloc = d->size = size; - d->clean = d->simpletext = d->righttoleft = d->capacity = 0; - d->data = d->array; - memcpy(d->array, unicode, size * sizeof(QChar)); - d->array[size] = '\0'; + *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + memcpy(d->data(), unicode, size * sizeof(QChar)); + d->data()[size] = '\0'; } } } @@ -1105,18 +1093,14 @@ QString::QString(const QChar *unicode) QString::QString(int size, QChar ch) { if (size <= 0) { - d = &shared_empty; - d->ref.ref(); + d = const_cast(&shared_empty.str); } else { - d = (Data*) qMalloc(sizeof(Data)+size*sizeof(QChar)); + d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; - d->alloc = d->size = size; - d->clean = d->simpletext = d->righttoleft = d->capacity = 0; - d->data = d->array; - d->array[size] = '\0'; - ushort *i = d->array + size; - ushort *b = d->array; + *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + d->data()[size] = '\0'; + ushort *i = d->data() + size; + ushort *b = d->data(); const ushort value = ch.unicode(); while (i != b) *--i = value; @@ -1131,13 +1115,10 @@ QString::QString(int size, QChar ch) */ QString::QString(int size, Qt::Initialization) { - d = (Data*) qMalloc(sizeof(Data)+size*sizeof(QChar)); + d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; - d->alloc = d->size = size; - d->clean = d->simpletext = d->righttoleft = d->capacity = 0; - d->data = d->array; - d->array[size] = '\0'; + *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + d->data()[size] = '\0'; } /*! \fn QString::QString(const QLatin1String &str) @@ -1152,15 +1133,11 @@ QString::QString(int size, Qt::Initialization) */ QString::QString(QChar ch) { - void *buf = qMalloc(sizeof(Data) + sizeof(QChar)); - Q_CHECK_PTR(buf); - d = reinterpret_cast(buf); - d->ref = 1; - d->alloc = d->size = 1; - d->clean = d->simpletext = d->righttoleft = d->capacity = 0; - d->data = d->array; - d->array[0] = ch.unicode(); - d->array[1] = '\0'; + d = (Data *) qMalloc(sizeof(Data) + 2*sizeof(QChar)); + Q_CHECK_PTR(d); + *d = (Data) { Q_REFCOUNT_INITIALIZER(1), 1, 1, false, { 0 } }; + d->data()[0] = ch.unicode(); + d->data()[1] = '\0'; } /*! \fn QString::QString(const QByteArray &ba) @@ -1256,21 +1233,23 @@ void QString::resize(int size) if (size < 0) size = 0; - if (size == 0 && !d->capacity) { - Data *x = &shared_empty; - x->ref.ref(); + if (d->offset && d->ref == 1 && size < d->size) { + d->size = size; + return; + } + + if (size == 0 && !d->capacityReserved) { + Data *x = const_cast(&shared_empty.str); if (!d->ref.deref()) QString::free(d); d = x; } else { if (d->ref != 1 || size > d->alloc || - (!d->capacity && size < d->size && size < d->alloc >> 1)) + (!d->capacityReserved && size < d->size && size < d->alloc >> 1)) realloc(grow(size)); if (d->alloc >= size) { d->size = size; - if (d->data == d->array) { - d->array[size] = '\0'; - } + d->data()[size] = '\0'; } } } @@ -1328,28 +1307,21 @@ void QString::resize(int size) // ### Qt 5: rename reallocData() to avoid confusion. 197625 void QString::realloc(int alloc) { - if (d->ref != 1 || d->data != d->array) { - Data *x = static_cast(qMalloc(sizeof(Data) + alloc * sizeof(QChar))); + if (d->ref != 1 || d->offset) { + Data *x = static_cast(qMalloc(sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(x); - x->size = qMin(alloc, d->size); - ::memcpy(x->array, d->data, x->size * sizeof(QChar)); - x->array[x->size] = 0; - x->ref = 1; - x->alloc = alloc; - x->clean = d->clean; - x->simpletext = d->simpletext; - x->righttoleft = d->righttoleft; - x->capacity = d->capacity; - x->data = x->array; + *x = (Data){ Q_REFCOUNT_INITIALIZER(1), qMin(alloc, d->size), alloc, d->capacityReserved, { 0 } }; + ::memcpy(x->data(), d->data(), x->size * sizeof(QChar)); + x->data()[x->size] = 0; if (!d->ref.deref()) QString::free(d); d = x; } else { - Data *p = static_cast(qRealloc(d, sizeof(Data) + alloc * sizeof(QChar))); + Data *p = static_cast(qRealloc(d, sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(p); d = p; d->alloc = alloc; - d->data = d->array; + d->offset = 0; } } @@ -1363,8 +1335,8 @@ void QString::expand(int i) int sz = d->size; resize(qMax(i + 1, sz)); if (d->size - 1 > sz) { - ushort *n = d->data + d->size - 1; - ushort *e = d->data + sz; + ushort *n = d->data() + d->size - 1; + ushort *e = d->data() + sz; while (n != e) * --n = ' '; } @@ -1482,9 +1454,9 @@ QString &QString::insert(int i, const QLatin1String &str) int len = qstrlen(str.latin1()); expand(qMax(d->size, i) + len - 1); - ::memmove(d->data + i + len, d->data + i, (d->size - i - len) * sizeof(QChar)); + ::memmove(d->data() + i + len, d->data() + i, (d->size - i - len) * sizeof(QChar)); for (int j = 0; j < len; ++j) - d->data[i + j] = s[j]; + d->data()[i + j] = s[j]; return *this; } @@ -1501,7 +1473,7 @@ QString& QString::insert(int i, const QChar *unicode, int size) return *this; const ushort *s = (const ushort *)unicode; - if (s >= d->data && s < d->data + d->alloc) { + if (s >= d->data() && s < d->data() + d->alloc) { // Part of me - take a copy ushort *tmp = static_cast(qMalloc(size * sizeof(QChar))); Q_CHECK_PTR(tmp); @@ -1513,8 +1485,8 @@ QString& QString::insert(int i, const QChar *unicode, int size) expand(qMax(d->size, i) + size - 1); - ::memmove(d->data + i + size, d->data + i, (d->size - i - size) * sizeof(QChar)); - memcpy(d->data + i, s, size * sizeof(QChar)); + ::memmove(d->data() + i + size, d->data() + i, (d->size - i - size) * sizeof(QChar)); + memcpy(d->data() + i, s, size * sizeof(QChar)); return *this; } @@ -1532,8 +1504,8 @@ QString& QString::insert(int i, QChar ch) if (i < 0) return *this; expand(qMax(i, d->size)); - ::memmove(d->data + i + 1, d->data + i, (d->size - i) * sizeof(QChar)); - d->data[i] = ch.unicode(); + ::memmove(d->data() + i + 1, d->data() + i, (d->size - i) * sizeof(QChar)); + d->data()[i] = ch.unicode(); return *this; } @@ -1557,15 +1529,15 @@ QString& QString::insert(int i, QChar ch) */ QString &QString::append(const QString &str) { - if (str.d != &shared_null) { - if (d == &shared_null) { + if (str.d != &shared_null.str) { + if (d == &shared_null.str) { operator=(str); } else { if (d->ref != 1 || d->size + str.d->size > d->alloc) realloc(grow(d->size + str.d->size)); - memcpy(d->data + d->size, str.d->data, str.d->size * sizeof(QChar)); + memcpy(d->data() + d->size, str.d->data(), str.d->size * sizeof(QChar)); d->size += str.d->size; - d->data[d->size] = '\0'; + d->data()[d->size] = '\0'; } } return *this; @@ -1583,7 +1555,7 @@ QString &QString::append(const QLatin1String &str) int len = qstrlen((char *)s); if (d->ref != 1 || d->size + len > d->alloc) realloc(grow(d->size + len)); - ushort *i = d->data + d->size; + ushort *i = d->data() + d->size; while ((*i++ = *s++)) ; d->size += len; @@ -1626,8 +1598,8 @@ QString &QString::append(QChar ch) { if (d->ref != 1 || d->size + 1 > d->alloc) realloc(grow(d->size + 1)); - d->data[d->size++] = ch.unicode(); - d->data[d->size] = '\0'; + d->data()[d->size++] = ch.unicode(); + d->data()[d->size] = '\0'; return *this; } @@ -1707,7 +1679,7 @@ QString &QString::remove(int pos, int len) resize(pos); // truncate } else if (len > 0) { detach(); - memmove(d->data + pos, d->data + pos + len, + memmove(d->data() + pos, d->data() + pos + len, (d->size - pos - len + 1) * sizeof(ushort)); d->size -= len; } @@ -1756,14 +1728,14 @@ QString &QString::remove(QChar ch, Qt::CaseSensitivity cs) ushort c = ch.unicode(); if (cs == Qt::CaseSensitive) { while (i < d->size) - if (d->data[i] == ch) + if (d->data()[i] == ch) remove(i, 1); else i++; } else { c = foldCase(c); while (i < d->size) - if (foldCase(d->data[i]) == c) + if (foldCase(d->data()[i]) == c) remove(i, 1); else i++; @@ -1859,10 +1831,10 @@ QString &QString::replace(const QString &before, const QString &after, Qt::CaseS */ void QString::replace_helper(uint *indices, int nIndices, int blen, const QChar *after, int alen) { - // copy *after in case it lies inside our own d->data area + // copy *after in case it lies inside our own d->data() area // (which we could possibly invalidate via a realloc or corrupt via memcpy operations.) QChar *afterBuffer = const_cast(after); - if (after >= reinterpret_cast(d->data) && after < reinterpret_cast(d->data) + d->size) { + if (after >= reinterpret_cast(d->data()) && after < reinterpret_cast(d->data()) + d->size) { afterBuffer = static_cast(qMalloc(alen*sizeof(QChar))); Q_CHECK_PTR(afterBuffer); ::memcpy(afterBuffer, after, alen*sizeof(QChar)); @@ -1873,30 +1845,30 @@ void QString::replace_helper(uint *indices, int nIndices, int blen, const QChar // replace in place detach(); for (int i = 0; i < nIndices; ++i) - memcpy(d->data + indices[i], afterBuffer, alen * sizeof(QChar)); + memcpy(d->data() + indices[i], afterBuffer, alen * sizeof(QChar)); } else if (alen < blen) { // replace from front detach(); uint to = indices[0]; if (alen) - memcpy(d->data+to, after, alen*sizeof(QChar)); + memcpy(d->data()+to, after, alen*sizeof(QChar)); to += alen; uint movestart = indices[0] + blen; for (int i = 1; i < nIndices; ++i) { int msize = indices[i] - movestart; if (msize > 0) { - memmove(d->data + to, d->data + movestart, msize * sizeof(QChar)); + memmove(d->data() + to, d->data() + movestart, msize * sizeof(QChar)); to += msize; } if (alen) { - memcpy(d->data + to, afterBuffer, alen*sizeof(QChar)); + memcpy(d->data() + to, afterBuffer, alen*sizeof(QChar)); to += alen; } movestart = indices[i] + blen; } int msize = d->size - movestart; if (msize > 0) - memmove(d->data + to, d->data + movestart, msize * sizeof(QChar)); + memmove(d->data() + to, d->data() + movestart, msize * sizeof(QChar)); resize(d->size - nIndices*(blen-alen)); } else { // replace from back @@ -1910,9 +1882,9 @@ void QString::replace_helper(uint *indices, int nIndices, int blen, const QChar int movestart = indices[nIndices] + blen; int insertstart = indices[nIndices] + nIndices*(alen-blen); int moveto = insertstart + alen; - memmove(d->data + moveto, d->data + movestart, + memmove(d->data() + moveto, d->data() + movestart, (moveend - movestart)*sizeof(QChar)); - memcpy(d->data + insertstart, afterBuffer, alen*sizeof(QChar)); + memcpy(d->data() + insertstart, afterBuffer, alen*sizeof(QChar)); moveend = movestart-blen; } } @@ -1994,7 +1966,7 @@ QString& QString::replace(QChar ch, const QString &after, Qt::CaseSensitivity cs return remove(ch, cs); if (after.d->size == 1) - return replace(ch, after.d->data[0], cs); + return replace(ch, after.d->data()[0], cs); if (d->size == 0) return *this; @@ -2007,13 +1979,13 @@ QString& QString::replace(QChar ch, const QString &after, Qt::CaseSensitivity cs uint pos = 0; if (cs == Qt::CaseSensitive) { while (pos < 1023 && index < d->size) { - if (d->data[index] == cc) + if (d->data()[index] == cc) indices[pos++] = index; index++; } } else { while (pos < 1023 && index < d->size) { - if (QChar::toCaseFolded(d->data[index]) == cc) + if (QChar::toCaseFolded(d->data()[index]) == cc) indices[pos++] = index; index++; } @@ -2045,7 +2017,7 @@ QString& QString::replace(QChar before, QChar after, Qt::CaseSensitivity cs) ushort b = before.unicode(); if (d->size) { detach(); - ushort *i = d->data; + ushort *i = d->data(); const ushort *e = i + d->size; if (cs == Qt::CaseSensitive) { for (; i != e; ++i) @@ -2170,7 +2142,7 @@ bool QString::operator==(const QString &other) const if (d->size != other.d->size) return false; - return qMemEquals(d->data, other.d->data, d->size); + return qMemEquals(d->data(), other.d->data(), d->size); } /*! @@ -2178,7 +2150,7 @@ bool QString::operator==(const QString &other) const */ bool QString::operator==(const QLatin1String &other) const { - const ushort *uc = d->data; + const ushort *uc = d->data(); const ushort *e = uc + d->size; const uchar *c = (uchar *)other.latin1(); @@ -2240,7 +2212,7 @@ bool QString::operator<(const QString &other) const */ bool QString::operator<(const QLatin1String &other) const { - const ushort *uc = d->data; + const ushort *uc = d->data(); const ushort *e = uc + d->size; const uchar *c = (uchar *) other.latin1(); @@ -2342,7 +2314,7 @@ bool QString::operator<(const QLatin1String &other) const */ bool QString::operator>(const QLatin1String &other) const { - const ushort *uc = d->data;; + const ushort *uc = d->data();; const ushort *e = uc + d->size; const uchar *c = (uchar *) other.latin1(); @@ -2687,7 +2659,7 @@ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) c { const int sl = str.d->size; if (sl == 1) - return lastIndexOf(QChar(str.d->data[0]), from, cs); + return lastIndexOf(QChar(str.d->data()[0]), from, cs); const int l = d->size; if (from < 0) @@ -2700,7 +2672,7 @@ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) c if (from > delta) from = delta; - return lastIndexOfHelper(d->data, from, str.d->data, str.d->size, cs); + return lastIndexOfHelper(d->data(), from, str.d->data(), str.d->size, cs); } /*! @@ -2743,7 +2715,7 @@ int QString::lastIndexOf(const QLatin1String &str, int from, Qt::CaseSensitivity for (int i = 0; i < sl; ++i) s[i] = str.latin1()[i]; - return lastIndexOfHelper(d->data, from, s.data(), sl, cs); + return lastIndexOfHelper(d->data(), from, s.data(), sl, cs); } /*! @@ -2789,7 +2761,7 @@ int QString::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs if (from > delta) from = delta; - return lastIndexOfHelper(d->data, from, reinterpret_cast(str.unicode()), + return lastIndexOfHelper(d->data(), from, reinterpret_cast(str.unicode()), str.size(), cs); } @@ -2940,14 +2912,14 @@ QString& QString::replace(const QRegExp &rx, const QString &after) while (i < pos) { int copyend = replacements[i].pos; int size = copyend - copystart; - memcpy(uc, d->data + copystart, size * sizeof(QChar)); + memcpy(uc, d->data() + copystart, size * sizeof(QChar)); uc += size; - memcpy(uc, after.d->data, al * sizeof(QChar)); + memcpy(uc, after.d->data(), al * sizeof(QChar)); uc += al; copystart = copyend + replacements[i].length; i++; } - memcpy(uc, d->data + copystart, (d->size - copystart) * sizeof(QChar)); + memcpy(uc, d->data() + copystart, (d->size - copystart) * sizeof(QChar)); newstring.resize(newlen); *this = newstring; caretMode = QRegExp::CaretWontMatch; @@ -3366,7 +3338,7 @@ QString QString::left(int n) const { if (n >= d->size || n < 0) return *this; - return QString((const QChar*) d->data, n); + return QString((const QChar*) d->data(), n); } /*! @@ -3384,7 +3356,7 @@ QString QString::right(int n) const { if (n >= d->size || n < 0) return *this; - return QString((const QChar*) d->data + d->size - n, n); + return QString((const QChar*) d->data() + d->size - n, n); } /*! @@ -3406,7 +3378,7 @@ QString QString::right(int n) const QString QString::mid(int position, int n) const { - if (d == &shared_null || position >= d->size) + if (d == &shared_null.str || position >= d->size) return QString(); if (n < 0) n = d->size - position; @@ -3418,7 +3390,7 @@ QString QString::mid(int position, int n) const n = d->size - position; if (position == 0 && n == d->size) return *this; - return QString((const QChar*) d->data + position, n); + return QString((const QChar*) d->data() + position, n); } /*! @@ -3456,8 +3428,8 @@ bool QString::startsWith(const QChar &c, Qt::CaseSensitivity cs) const { return d->size && (cs == Qt::CaseSensitive - ? d->data[0] == c - : foldCase(d->data[0]) == foldCase(c.unicode())); + ? d->data()[0] == c + : foldCase(d->data()[0]) == foldCase(c.unicode())); } /*! @@ -3530,8 +3502,8 @@ bool QString::endsWith(const QChar &c, Qt::CaseSensitivity cs) const { return d->size && (cs == Qt::CaseSensitive - ? d->data[d->size - 1] == c - : foldCase(d->data[d->size - 1]) == foldCase(c.unicode())); + ? d->data()[d->size - 1] == c + : foldCase(d->data()[d->size - 1]) == foldCase(c.unicode())); } /*! \fn const char *QString::ascii() const @@ -3765,22 +3737,17 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) { Data *d; if (!str) { - d = &shared_null; - d->ref.ref(); + d = const_cast(&shared_null.str); } else if (size == 0 || (!*str && size < 0)) { - d = &shared_empty; - d->ref.ref(); + d = const_cast(&shared_empty.str); } else { if (size < 0) size = qstrlen(str); - d = static_cast(qMalloc(sizeof(Data) + size * sizeof(QChar))); + d = static_cast(qMalloc(sizeof(Data) + (size+1) * sizeof(QChar))); Q_CHECK_PTR(d); - d->ref = 1; - d->alloc = d->size = size; - d->clean = d->simpletext = d->righttoleft = d->capacity = 0; - d->data = d->array; - d->array[size] = '\0'; - ushort *dst = d->data; + *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + d->data()[size] = '\0'; + ushort *dst = d->data(); /* SIMD: * Unpacking with SSE has been shown to improve performance on recent CPUs * The same method gives no improvement with NEON. @@ -3818,11 +3785,9 @@ QString::Data *QString::fromAscii_helper(const char *str, int size) if (codecForCStrings) { Data *d; if (!str) { - d = &shared_null; - d->ref.ref(); + d = const_cast(&shared_null.str); } else if (size == 0 || (!*str && size < 0)) { - d = &shared_empty; - d->ref.ref(); + d = const_cast(&shared_empty.str); } else { if (size < 0) size = qstrlen(str); @@ -3995,7 +3960,7 @@ QString& QString::setUnicode(const QChar *unicode, int size) { resize(size); if (unicode && size) - memcpy(d->data, unicode, size * sizeof(QChar)); + memcpy(d->data(), unicode, size * sizeof(QChar)); return *this; } @@ -4034,7 +3999,7 @@ QString QString::simplified() const if (d->size == 0) return *this; - const QChar * const start = reinterpret_cast(d->data); + const QChar * const start = reinterpret_cast(d->data()); const QChar *from = start; const QChar *fromEnd = start + d->size; forever { @@ -4043,8 +4008,7 @@ QString QString::simplified() const break; if (++from == fromEnd) { // All-whitespace string - shared_empty.ref.ref(); - return QString(&shared_empty, 0); + return QString(shared_empty); } } // This loop needs no underflow check, as we already determined that @@ -4079,7 +4043,7 @@ QString QString::simplified() const // of already simplified characters - at least one, obviously - // without a trailing space. QString result((fromEnd - from) + copyCount, Qt::Uninitialized); - QChar *to = reinterpret_cast(result.d->data); + QChar *to = reinterpret_cast(result.d->data()); ::memcpy(to, copyFrom, copyCount * 2); to += copyCount; fromEnd--; @@ -4100,7 +4064,7 @@ QString QString::simplified() const } done: *to++ = ch; - result.truncate(to - reinterpret_cast(result.d->data)); + result.truncate(to - reinterpret_cast(result.d->data())); return result; } @@ -4124,7 +4088,7 @@ QString QString::trimmed() const { if (d->size == 0) return *this; - const QChar *s = (const QChar*)d->data; + const QChar *s = (const QChar*)d->data(); if (!s->isSpace() && !s[d->size-1].isSpace()) return *this; int start = 0; @@ -4137,8 +4101,7 @@ QString QString::trimmed() const } int l = end - start + 1; if (l <= 0) { - shared_empty.ref.ref(); - return QString(&shared_empty, 0); + return QString(shared_empty); } return QString(s + start, l); } @@ -4250,8 +4213,8 @@ QString& QString::fill(QChar ch, int size) { resize(size < 0 ? d->size : size); if (d->size) { - QChar *i = (QChar*)d->data + d->size; - QChar *b = (QChar*)d->data; + QChar *i = (QChar*)d->data() + d->size; + QChar *b = (QChar*)d->data(); while (i != b) *--i = ch; } @@ -4597,7 +4560,7 @@ int QString::compare(const QString &other, Qt::CaseSensitivity cs) const { if (cs == Qt::CaseSensitive) return ucstrcmp(constData(), length(), other.constData(), other.length()); - return ucstricmp(d->data, d->data + d->size, other.d->data, other.d->data + other.d->size); + return ucstricmp(d->data(), d->data() + d->size, other.d->data(), other.d->data() + other.d->size); } /*! @@ -4828,12 +4791,9 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1, const ushort *QString::utf16() const { - if (d->data != d->array) { - QString *that = const_cast(this); - that->realloc(); // ensure '\\0'-termination for ::fromRawData strings - return that->d->data; - } - return d->array; + if (d->offset) + const_cast(this)->realloc(); // ensure '\\0'-termination for ::fromRawData strings + return d->data(); } /*! @@ -4862,8 +4822,8 @@ QString QString::leftJustified(int width, QChar fill, bool truncate) const if (padlen > 0) { result.resize(len+padlen); if (len) - memcpy(result.d->data, d->data, sizeof(QChar)*len); - QChar *uc = (QChar*)result.d->data + len; + memcpy(result.d->data(), d->data(), sizeof(QChar)*len); + QChar *uc = (QChar*)result.d->data() + len; while (padlen--) * uc++ = fill; } else { @@ -4900,11 +4860,11 @@ QString QString::rightJustified(int width, QChar fill, bool truncate) const int padlen = width - len; if (padlen > 0) { result.resize(len+padlen); - QChar *uc = (QChar*)result.d->data; + QChar *uc = (QChar*)result.d->data(); while (padlen--) * uc++ = fill; if (len) - memcpy(uc, d->data, sizeof(QChar)*len); + memcpy(uc, d->data(), sizeof(QChar)*len); } else { if (truncate) result = left(width); @@ -4924,7 +4884,7 @@ QString QString::rightJustified(int width, QChar fill, bool truncate) const QString QString::toLower() const { - const ushort *p = d->data; + const ushort *p = d->data(); if (!p) return *this; if (!d->size) @@ -4939,7 +4899,7 @@ QString QString::toLower() const } #endif - const ushort *e = d->data + d->size; + const ushort *e = d->data() + d->size; // this avoids one out of bounds check in the loop if (QChar(*p).isLowSurrogate()) @@ -4952,17 +4912,17 @@ QString QString::toLower() const const QUnicodeTables::Properties *prop = qGetProp(c); if (prop->lowerCaseDiff || prop->lowerCaseSpecial) { 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); + memcpy(s.d->data(), d->data(), (p - d->data())*sizeof(ushort)); + ushort *pp = s.d->data() + (p - d->data()); while (p < e) { uint c = *p; if (QChar(c).isLowSurrogate() && QChar(*(p - 1)).isHighSurrogate()) c = QChar::surrogateToUcs4(*(p - 1), c); prop = qGetProp(c); if (prop->lowerCaseSpecial) { - int pos = pp - s.d->data; + int pos = pp - s.d->data(); s.resize(s.d->size + SPECIAL_CASE_MAX_LEN); - pp = s.d->data + pos; + pp = s.d->data() + pos; const ushort *specialCase = specialCaseMap + prop->lowerCaseDiff; while (*specialCase) *pp++ = *specialCase++; @@ -4971,7 +4931,7 @@ QString QString::toLower() const } ++p; } - s.truncate(pp - s.d->data); + s.truncate(pp - s.d->data()); return s; } ++p; @@ -4988,11 +4948,11 @@ QString QString::toCaseFolded() const if (!d->size) return *this; - const ushort *p = d->data; + const ushort *p = d->data(); if (!p) return *this; - const ushort *e = d->data + d->size; + const ushort *e = d->data() + d->size; uint last = 0; while (p < e) { @@ -5000,9 +4960,9 @@ QString QString::toCaseFolded() const if (folded != *p) { QString s(*this); s.detach(); - ushort *pp = s.d->data + (p - d->data); - const ushort *ppe = s.d->data + s.d->size; - last = pp > s.d->data ? *(pp - 1) : 0; + ushort *pp = s.d->data() + (p - d->data()); + const ushort *ppe = s.d->data() + s.d->size; + last = pp > s.d->data() ? *(pp - 1) : 0; while (pp < ppe) { *pp = foldCase(*pp, last); ++pp; @@ -5024,7 +4984,7 @@ QString QString::toCaseFolded() const QString QString::toUpper() const { - const ushort *p = d->data; + const ushort *p = d->data(); if (!p) return *this; if (!d->size) @@ -5039,7 +4999,7 @@ QString QString::toUpper() const } #endif - const ushort *e = d->data + d->size; + const ushort *e = d->data() + d->size; // this avoids one out of bounds check in the loop if (QChar(*p).isLowSurrogate()) @@ -5052,17 +5012,17 @@ QString QString::toUpper() const const QUnicodeTables::Properties *prop = qGetProp(c); if (prop->upperCaseDiff || prop->upperCaseSpecial) { 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); + memcpy(s.d->data(), d->data(), (p - d->data())*sizeof(ushort)); + ushort *pp = s.d->data() + (p - d->data()); while (p < e) { uint c = *p; if (QChar(c).isLowSurrogate() && QChar(*(p - 1)).isHighSurrogate()) c = QChar::surrogateToUcs4(*(p - 1), c); prop = qGetProp(c); if (prop->upperCaseSpecial) { - int pos = pp - s.d->data; + int pos = pp - s.d->data(); s.resize(s.d->size + SPECIAL_CASE_MAX_LEN); - pp = s.d->data + pos; + pp = s.d->data() + pos; const ushort *specialCase = specialCaseMap + prop->upperCaseDiff; while (*specialCase) *pp++ = *specialCase++; @@ -5071,7 +5031,7 @@ QString QString::toUpper() const } ++p; } - s.truncate(pp - s.d->data); + s.truncate(pp - s.d->data()); return s; } ++p; @@ -6196,19 +6156,19 @@ QString QString::repeated(int times) const if (result.d->alloc != resultSize) return QString(); // not enough memory - memcpy(result.d->data, d->data, d->size * sizeof(ushort)); + memcpy(result.d->data(), d->data(), d->size * sizeof(ushort)); int sizeSoFar = d->size; - ushort *end = result.d->data + sizeSoFar; + ushort *end = result.d->data() + sizeSoFar; const int halfResultSize = resultSize >> 1; while (sizeSoFar <= halfResultSize) { - memcpy(end, result.d->data, sizeSoFar * sizeof(ushort)); + memcpy(end, result.d->data(), sizeSoFar * sizeof(ushort)); end += sizeSoFar; sizeSoFar <<= 1; } - memcpy(end, result.d->data, (resultSize - sizeSoFar) * sizeof(ushort)); - result.d->data[resultSize] = '\0'; + memcpy(end, result.d->data(), (resultSize - sizeSoFar) * sizeof(ushort)); + result.d->data()[resultSize] = '\0'; result.d->size = resultSize; return result; } @@ -6902,7 +6862,7 @@ QString QString::multiArg(int numArgs, const QString **args) const { QString result; QMap numbersUsed; - const QChar *uc = (const QChar *) d->data; + const QChar *uc = (const QChar *) d->data(); const int len = d->size; const int end = len - 1; int lastNumber = -1; @@ -6951,62 +6911,50 @@ QString QString::multiArg(int numArgs, const QString **args) const return result; } -static bool isStringRightToLeft(const ushort *p, const ushort *end) -{ - bool righttoleft = false; - while (p < end) { - switch(QChar::direction(*p)) - { - case QChar::DirL: - goto end; - case QChar::DirR: - case QChar::DirAL: - righttoleft = true; - goto end; - default: - break; - } - ++p; - } - end: - return righttoleft; -} -/*! \internal - */ -void QString::updateProperties() const +/*! \fn bool QString::isSimpleText() const + + \internal +*/ +bool QString::isSimpleText() const { - ushort *p = d->data; - ushort *end = p + d->size; - d->simpletext = true; + const ushort *p = d->data(); + const ushort * const end = p + d->size; while (p < end) { ushort uc = *p; // sort out regions of complex text formatting if (uc > 0x058f && (uc < 0x1100 || uc > 0xfb0f)) { - d->simpletext = false; + return false; } p++; } - d->righttoleft = isStringRightToLeft(d->data, d->data + d->size); - d->clean = true; -} - -bool QString::isRightToLeft() const -{ - return isStringRightToLeft(d->data, d->data + d->size); + return true; } -/*! \fn bool QString::isSimpleText() const - - \internal -*/ - /*! \fn bool QString::isRightToLeft() const Returns true if the string is read right to left. */ - +bool QString::isRightToLeft() const +{ + const ushort *p = d->data(); + const ushort * const end = p + d->size; + while (p < end) { + switch(QChar::direction(*p)) + { + case QChar::DirL: + return false; + case QChar::DirR: + case QChar::DirAL: + return true; + default: + break; + } + ++p; + } + return false; +} /*! \fn QChar *QString::data() @@ -7122,17 +7070,13 @@ bool QString::isRightToLeft() const QString QString::fromRawData(const QChar *unicode, int size) { Data *x = static_cast(qMalloc(sizeof(Data))); + *x = (Data){ Q_REFCOUNT_INITIALIZER(1), size, 0, false, { 0 } }; Q_CHECK_PTR(x); if (unicode) { - x->data = (ushort *)unicode; + x->offset = (const ushort *)unicode - (x->d + sizeof(qptrdiff)/sizeof(ushort)); } else { - x->data = x->array; size = 0; } - x->ref = 1; - x->alloc = x->size = size; - *x->array = '\0'; - x->clean = x->simpletext = x->righttoleft = x->capacity = 0; return QString(x, 0); } @@ -7152,18 +7096,16 @@ QString QString::fromRawData(const QChar *unicode, int size) */ QString &QString::setRawData(const QChar *unicode, int size) { - if (d->ref != 1 || (d->data == d->array && d->alloc)) { + if (d->ref != 1 || d->alloc) { *this = fromRawData(unicode, size); } else { if (unicode) { - d->data = (ushort *)unicode; + d->size = size; + d->offset = (const ushort *)unicode - (d->d + sizeof(qptrdiff)/sizeof(ushort)); } else { - d->data = d->array; - size = 0; + d->offset = 0; + d->size = 0; } - d->alloc = d->size = size; - *d->array = '\0'; - d->clean = d->simpletext = d->righttoleft = d->capacity = 0; } return *this; } @@ -7212,7 +7154,7 @@ QString &QString::setRawData(const QChar *unicode, int size) \snippet doc/src/snippets/code/src_corelib_tools_qstring.cpp 6 - \sa QString, QLatin1Char + \sa QString, QLatin1Char, QStringLiteral */ /*! \fn QLatin1String::QLatin1String(const char *str) @@ -8345,7 +8287,7 @@ QStringRef QString::rightRef(int n) const QStringRef QString::midRef(int position, int n) const { - if (d == &shared_null || position >= d->size) + if (d == &shared_null.str || position >= d->size) return QStringRef(); if (n < 0) n = d->size - position; @@ -9071,4 +9013,21 @@ QVector QStringRef::toUcs4() const return v; } +/*! + \macro QStringLiteral(str) + \relates QString + + The macro generates the data for a QString out of \a str at compile time if the compiler supports it. + Creating a QString from it is free in this case, and the generated string data is stored in + the read-only segment of the compiled object file. + + Using QStringLiteral instead of a double quoted ascii literal can significantly speed up creation + of QString's from data known at compile time. + + If the compiler is c++0x enabled the string \a str can actually contain unicode data. + + For compilers not supporting the creation of compile time strings, QStringLiteral will fall back to + QLatin1String. +*/ + QT_END_NAMESPACE diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 154012d132..81dff3d309 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -44,7 +44,7 @@ #include #include -#include +#include #include #ifndef QT_NO_STL @@ -77,9 +77,65 @@ class QLatin1String; class QStringRef; template class QVector; +struct QStringData { + QtPrivate::RefCount ref; + int size; + uint alloc : 31; + uint capacityReserved : 1; + union { + qptrdiff offset; // will always work as we add/subtract from a ushort ptr + ushort d[sizeof(qptrdiff)/sizeof(ushort)]; + }; + inline ushort *data() { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } + inline const ushort *data() const { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } +}; + + +#if defined(Q_COMPILER_UNICODE_STRINGS) + +template struct QConstStringData +{ + const QStringData str; + const char16_t data[n]; + operator const QStringData &() const { return str; } +}; +#define QStringLiteral(str) (const QConstStringData) \ +{ { Q_REFCOUNT_INITIALIZER(-1), sizeof(u"" str)/2 -1, 0, 0, { 0 } }, u"" str } + +// wchar_t is 2 bytes +#elif defined(Q_OS_WIN) || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) || defined(WCHAR_MAX) && (WCHAR_MAX - 0 < 65536) + +template struct QConstStringData +{ + const QStringData str; + const wchar_t data[n]; + operator const QStringData &() const { return str; } +}; +#define QStringLiteral(str) (const QConstStringData) \ +{ { Q_REFCOUNT_INITIALIZER(-1), sizeof(L"" str)/2 -1, 0, 0, { 0 } }, L"" str } + +// fallback, uses QLatin1String as next best options +#else + +template struct QConstStringData +{ + const QStringData str; + const ushort data[n]; + operator const QStringData &() const { return str; } +}; +#define QStringLiteral(str) QLatin1String(str) + +#endif + +#ifndef QT_NO_KEYWORDS +#define qs(str) QStringLiteral(str) +#endif + class Q_CORE_EXPORT QString { public: + typedef QStringData Data; + inline QString(); QString(const QChar *unicode, int size); // Qt5: don't cap size < 0 explicit QString(const QChar *unicode); // Qt5: merge with the above @@ -108,7 +164,7 @@ public: int capacity() const; inline void reserve(int size); - inline void squeeze() { if (d->size < d->alloc || d->ref != 1) realloc(); d->capacity = 0;} + inline void squeeze() { if (d->size < d->alloc || d->ref != 1) realloc(); d->capacityReserved = false;} inline const QChar *unicode() const; inline QChar *data(); @@ -262,8 +318,8 @@ public: inline QString &operator+=(QChar c) { if (d->ref != 1 || d->size + 1 > d->alloc) realloc(grow(d->size + 1)); - d->data[d->size++] = c.unicode(); - d->data[d->size] = '\0'; + d->data()[d->size++] = c.unicode(); + d->data()[d->size] = '\0'; return *this; } @@ -491,15 +547,17 @@ public: // compatibility struct Null { }; static const Null null; - inline QString(const Null &): d(&shared_null) { d->ref.ref(); } + inline QString(const Null &): d(const_cast(&shared_null.str)) {} inline QString &operator=(const Null &) { *this = QString(); return *this; } - inline bool isNull() const { return d == &shared_null; } + inline bool isNull() const { return d == &shared_null.str; } - bool isSimpleText() const { if (!d->clean) updateProperties(); return d->simpletext; } + bool isSimpleText() const; bool isRightToLeft() const; QString(int size, Qt::Initialization); + template + inline QString(const QConstStringData &dd) : d(const_cast(&dd.str)) {} private: #if defined(QT_NO_CAST_FROM_ASCII) && !defined(Q_NO_DECLARED_NOT_DEFINED) @@ -511,22 +569,11 @@ private: QString &operator=(const QByteArray &a); #endif - struct Data { - QBasicAtomicInt ref; - int alloc, size; - ushort *data; // QT5: put that after the bit field to fill alignment gap; don't use sizeof any more then - ushort clean : 1; - ushort simpletext : 1; - ushort righttoleft : 1; - ushort capacity : 1; - ushort reserved : 11; - // ### Qt5: try to ensure that "array" is aligned to 16 bytes on both 32- and 64-bit - ushort array[1]; - }; - static Data shared_null; - static Data shared_empty; + static const QConstStringData<1> shared_null; + static const QConstStringData<1> shared_empty; Data *d; - QString(Data *dd, int /*dummy*/) : d(dd) {} + inline QString(Data *dd, int /*dummy*/) : d(dd) {} + #ifndef QT_NO_TEXTCODEC static QTextCodec *codecForCStrings; #endif @@ -605,23 +652,23 @@ inline QString::QString(const QLatin1String &aLatin1) : d(fromLatin1_helper(aLat inline int QString::length() const { return d->size; } inline const QChar QString::at(int i) const -{ Q_ASSERT(uint(i) < uint(size())); return d->data[i]; } +{ Q_ASSERT(uint(i) < uint(size())); return d->data()[i]; } inline const QChar QString::operator[](int i) const -{ Q_ASSERT(uint(i) < uint(size())); return d->data[i]; } +{ Q_ASSERT(uint(i) < uint(size())); return d->data()[i]; } inline const QChar QString::operator[](uint i) const -{ Q_ASSERT(i < uint(size())); return d->data[i]; } +{ Q_ASSERT(i < uint(size())); return d->data()[i]; } inline bool QString::isEmpty() const { return d->size == 0; } inline const QChar *QString::unicode() const -{ return reinterpret_cast(d->data); } +{ return reinterpret_cast(d->data()); } inline const QChar *QString::data() const -{ return reinterpret_cast(d->data); } +{ return reinterpret_cast(d->data()); } inline QChar *QString::data() -{ detach(); return reinterpret_cast(d->data); } +{ detach(); return reinterpret_cast(d->data()); } inline const QChar *QString::constData() const -{ return reinterpret_cast(d->data); } +{ return reinterpret_cast(d->data()); } inline void QString::detach() -{ if (d->ref != 1 || d->data != d->array) realloc(); } +{ if (d->ref != 1 || d->offset) realloc(); } inline bool QString::isDetached() const { return d->ref == 1; } inline QString &QString::operator=(const QLatin1String &s) @@ -703,10 +750,10 @@ public: // all this is not documented: We just say "like QChar" and let it be. inline operator QChar() const - { return i < s.d->size ? s.d->data[i] : 0; } + { return i < s.d->size ? s.d->data()[i] : 0; } inline QCharRef &operator=(const QChar &c) { if (i >= s.d->size) s.expand(i); else s.detach(); - s.d->data[i] = c.unicode(); return *this; } + s.d->data()[i] = c.unicode(); return *this; } // An operator= for each QChar cast constructors #ifndef QT_NO_CAST_FROM_ASCII @@ -773,9 +820,9 @@ inline void QCharRef::setRow(uchar arow) { QChar(*this).setRow(arow); } inline void QCharRef::setCell(uchar acell) { QChar(*this).setCell(acell); } -inline QString::QString() : d(&shared_null) { d->ref.ref(); } +inline QString::QString() : d(const_cast(&shared_null.str)) {} inline QString::~QString() { if (!d->ref.deref()) free(d); } -inline void QString::reserve(int asize) { if (d->ref != 1 || asize > d->alloc) realloc(asize); d->capacity = 1;} +inline void QString::reserve(int asize) { if (d->ref != 1 || asize > d->alloc) realloc(asize); d->capacityReserved = true;} inline QString &QString::setUtf16(const ushort *autf16, int asize) { return setUnicode(reinterpret_cast(autf16), asize); } inline QCharRef QString::operator[](int i) @@ -783,17 +830,17 @@ inline QCharRef QString::operator[](int i) inline QCharRef QString::operator[](uint i) { return QCharRef(*this, i); } inline QString::iterator QString::begin() -{ detach(); return reinterpret_cast(d->data); } +{ detach(); return reinterpret_cast(d->data()); } inline QString::const_iterator QString::begin() const -{ return reinterpret_cast(d->data); } +{ return reinterpret_cast(d->data()); } inline QString::const_iterator QString::constBegin() const -{ return reinterpret_cast(d->data); } +{ return reinterpret_cast(d->data()); } inline QString::iterator QString::end() -{ detach(); return reinterpret_cast(d->data + d->size); } +{ detach(); return reinterpret_cast(d->data() + d->size); } inline QString::const_iterator QString::end() const -{ return reinterpret_cast(d->data + d->size); } +{ return reinterpret_cast(d->data() + d->size); } inline QString::const_iterator QString::constEnd() const -{ return reinterpret_cast(d->data + d->size); } +{ return reinterpret_cast(d->data() + d->size); } inline QBool QString::contains(const QString &s, Qt::CaseSensitivity cs) const { return QBool(indexOf(s, 0, cs) != -1); } inline QBool QString::contains(const QStringRef &s, Qt::CaseSensitivity cs) const @@ -1024,7 +1071,7 @@ public: inline const QChar *unicode() const { if (!m_string) - return reinterpret_cast(QString::shared_null.data); + return reinterpret_cast(QString::shared_null.str.data()); return m_string->unicode() + m_position; } inline const QChar *data() const { return unicode(); } @@ -1154,7 +1201,6 @@ inline QBool QStringRef::contains(const QStringRef &s, Qt::CaseSensitivity cs) c { return QBool(indexOf(s, 0, cs) != -1); } - QT_END_NAMESPACE QT_END_HEADER -- cgit v1.2.3