diff --git a/src/shared/proparser/profileevaluator.cpp b/src/shared/proparser/profileevaluator.cpp index bf7b06005d77a23a9a8f6ae73224f53bca7242da..1f9ba88dbafcfca518e5f31406140e8dc4de13ca 100644 --- a/src/shared/proparser/profileevaluator.cpp +++ b/src/shared/proparser/profileevaluator.cpp @@ -1376,6 +1376,80 @@ void ProFileEvaluator::Private::doVariableReplace(QString *str) *str = expandVariableReferences(*str).join(ProFileOption::field_sep); } +// Be fast even for debug builds +#ifdef __GNUC__ +# define ALWAYS_INLINE __attribute__((always_inline)) +#else +# define ALWAYS_INLINE +#endif + +// The (QChar*)current->constData() constructs below avoid pointless detach() calls +static inline void ALWAYS_INLINE appendChar(ushort unicode, + QString *current, QChar **ptr, QString *pending) +{ + if (!pending->isEmpty()) { + int len = pending->size(); + current->resize(current->size() + len); + ::memcpy((QChar*)current->constData(), pending->constData(), len * 2); + pending->clear(); + *ptr = (QChar*)current->constData() + len; + } + *(*ptr)++ = QChar(unicode); +} + +static void appendString(const QString &string, + QString *current, QChar **ptr, QString *pending) +{ + if (string.isEmpty()) + return; + QChar *uc = (QChar*)current->constData(); + int len; + if (*ptr != uc) { + len = *ptr - uc; + current->resize(current->size() + string.size()); + } else if (!pending->isEmpty()) { + len = pending->size(); + current->resize(current->size() + len + string.size()); + ::memcpy((QChar*)current->constData(), pending->constData(), len * 2); + pending->clear(); + } else { + *pending = string; + return; + } + *ptr = (QChar*)current->constData() + len; + ::memcpy(*ptr, string.data(), string.size() * 2); + *ptr += string.size(); +} + +static void flushCurrent(QStringList *ret, + QString *current, QChar **ptr, QString *pending) +{ + QChar *uc = (QChar*)current->constData(); + int len = *ptr - uc; + if (len) { + ret->append(QString(uc, len)); + *ptr = uc; + } else if (!pending->isEmpty()) { + ret->append(*pending); + pending->clear(); + } +} + +static inline void flushFinal(QStringList *ret, + const QString ¤t, const QChar *ptr, const QString &pending, + const QString &str, bool replaced) +{ + int len = ptr - current.data(); + if (len) { + if (!replaced && len == str.size()) + ret->append(str); + else + ret->append(QString(current.data(), len)); + } else if (!pending.isEmpty()) { + ret->append(pending); + } +} + QStringList ProFileEvaluator::Private::expandVariableReferences( const QString &str, bool do_semicolon) { @@ -1405,20 +1479,20 @@ QStringList ProFileEvaluator::Private::expandVariableReferences( const QChar *str_data = str.data(); const int str_len = str.length(); - ushort term; QString var, args; - int replaced = 0; - QString current; + bool replaced = false; + QString current; // Buffer for successively assembled string segments + current.resize(str.size()); + QChar *ptr = current.data(); + QString pending; // Buffer for string segments from variables + // Only one of the above buffers can be filled at a given time. for (int i = 0; i < str_len; ++i) { unicode = str_data[i].unicode(); - const int start_var = i; - if (unicode == DOLLAR && str_len > i+2) { - unicode = str_data[++i].unicode(); - if (unicode == DOLLAR) { - term = 0; - var.clear(); - args.clear(); + if (unicode == DOLLAR) { + if (str_len > i+2 && str_data[i+1].unicode() == DOLLAR) { + ++i; + ushort term = 0; enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR; unicode = str_data[++i].unicode(); if (unicode == LSQUARE) { @@ -1434,6 +1508,7 @@ QStringList ProFileEvaluator::Private::expandVariableReferences( var_type = ENVIRON; term = RPAREN; } + int name_start = i; forever { if (!(unicode & (0xFF<<8)) && unicode != DOT && unicode != UNDERSCORE && @@ -1441,14 +1516,15 @@ QStringList ProFileEvaluator::Private::expandVariableReferences( (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') && (unicode < '0' || unicode > '9')) break; - var.append(QChar(unicode)); if (++i == str_len) break; unicode = str_data[i].unicode(); // at this point, i points to either the 'term' or 'next' character (which is in unicode) } + var = QString::fromRawData(str_data + name_start, i - name_start); if (var_type == VAR && unicode == LPAREN) { var_type = FUNCTION; + name_start = i + 1; int depth = 0; forever { if (++i == str_len) @@ -1461,8 +1537,8 @@ QStringList ProFileEvaluator::Private::expandVariableReferences( break; --depth; } - args.append(QChar(unicode)); } + args = QString(str_data + name_start, i - name_start); if (++i < str_len) unicode = str_data[i].unicode(); else @@ -1483,8 +1559,6 @@ QStringList ProFileEvaluator::Private::expandVariableReferences( // move the 'cursor' back to the last char of the thing we were looking at --i; } - // since i never points to the 'next' character, there is no reason for this to be set - unicode = 0; QStringList replacement; if (var_type == ENVIRON) { @@ -1496,69 +1570,51 @@ QStringList ProFileEvaluator::Private::expandVariableReferences( } else if (var_type == VAR) { replacement = values(var); } - if (!(replaced++) && start_var) - current = str.left(start_var); if (!replacement.isEmpty()) { if (quote) { - current += replacement.join(ProFileOption::field_sep); + appendString(replacement.join(ProFileOption::field_sep), + ¤t, &ptr, &pending); } else { - current += replacement.takeFirst(); - if (!replacement.isEmpty()) { - if (!current.isEmpty()) - ret.append(current); - current = replacement.takeLast(); - if (!replacement.isEmpty()) - ret += replacement; + appendString(replacement.first(), ¤t, &ptr, &pending); + if (replacement.size() > 1) { + flushCurrent(&ret, ¤t, &ptr, &pending); + pending = replacement.last(); + if (replacement.size() > 2) { + // FIXME: ret.reserve(ret.size() + replacement.size() - 2); + for (int i = 1; i < replacement.size() - 1; ++i) + ret << replacement.at(i); + } } } + replaced = true; } - } else { - if (replaced) - current.append(QLatin1Char('$')); + continue; } - } - if (quote && unicode == quote) { - unicode = 0; - quote = 0; } else if (unicode == BACKSLASH) { - bool escape = false; - const char *symbols = "[]{}()$\\'\""; - for (const char *s = symbols; *s; ++s) { - if (str_data[i+1].unicode() == (ushort)*s) { - i++; - escape = true; - if (!(replaced++)) - current = str.left(start_var); - current.append(str.at(i)); - break; - } + static const char symbols[] = "[]{}()$\\'\""; + ushort unicode2 = str_data[i+1].unicode(); + if (!(unicode2 & 0xff00) && strchr(symbols, unicode2)) { + unicode = unicode2; + ++i; + } + } else if (quote) { + if (unicode == quote) { + quote = 0; + continue; } - if (escape || !replaced) - unicode =0; - } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) { - quote = unicode; - unicode = 0; - if (!(replaced++) && i) - current = str.left(i); - } else if (!quote && ((do_semicolon && unicode == SEMICOLON) || - unicode == SPACE || unicode == TAB)) { - unicode = 0; - if (!(replaced++) && i) - current = str.left(i); - if (!current.isEmpty()) { - ret.append(current); - current.clear(); + } else { + if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) { + quote = unicode; + continue; + } else if ((do_semicolon && unicode == SEMICOLON) || + unicode == SPACE || unicode == TAB) { + flushCurrent(&ret, ¤t, &ptr, &pending); + continue; } } - if (replaced && unicode) - current.append(QChar(unicode)); - } - if (!replaced) { - if (!str.isEmpty()) - ret.append(str); - } else if (!current.isEmpty()) { - ret.append(current); + appendChar(unicode, ¤t, &ptr, &pending); } + flushFinal(&ret, current, ptr, pending, str, replaced); return ret; }