forked from qt-creator/qt-creator
optimize expandVariableReferences()
- nest conditionals properly to avoid unnecessary tests - avoid usage of QString::append(QChar) to save allocations
This commit is contained in:
@@ -1376,6 +1376,80 @@ void ProFileEvaluator::Private::doVariableReplace(QString *str)
|
|||||||
*str = expandVariableReferences(*str).join(ProFileOption::field_sep);
|
*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(
|
QStringList ProFileEvaluator::Private::expandVariableReferences(
|
||||||
const QString &str, bool do_semicolon)
|
const QString &str, bool do_semicolon)
|
||||||
{
|
{
|
||||||
@@ -1405,20 +1479,20 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(
|
|||||||
const QChar *str_data = str.data();
|
const QChar *str_data = str.data();
|
||||||
const int str_len = str.length();
|
const int str_len = str.length();
|
||||||
|
|
||||||
ushort term;
|
|
||||||
QString var, args;
|
QString var, args;
|
||||||
|
|
||||||
int replaced = 0;
|
bool replaced = false;
|
||||||
QString current;
|
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) {
|
for (int i = 0; i < str_len; ++i) {
|
||||||
unicode = str_data[i].unicode();
|
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) {
|
if (unicode == DOLLAR) {
|
||||||
term = 0;
|
if (str_len > i+2 && str_data[i+1].unicode() == DOLLAR) {
|
||||||
var.clear();
|
++i;
|
||||||
args.clear();
|
ushort term = 0;
|
||||||
enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
|
enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
|
||||||
unicode = str_data[++i].unicode();
|
unicode = str_data[++i].unicode();
|
||||||
if (unicode == LSQUARE) {
|
if (unicode == LSQUARE) {
|
||||||
@@ -1434,6 +1508,7 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(
|
|||||||
var_type = ENVIRON;
|
var_type = ENVIRON;
|
||||||
term = RPAREN;
|
term = RPAREN;
|
||||||
}
|
}
|
||||||
|
int name_start = i;
|
||||||
forever {
|
forever {
|
||||||
if (!(unicode & (0xFF<<8)) &&
|
if (!(unicode & (0xFF<<8)) &&
|
||||||
unicode != DOT && unicode != UNDERSCORE &&
|
unicode != DOT && unicode != UNDERSCORE &&
|
||||||
@@ -1441,14 +1516,15 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(
|
|||||||
(unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
|
(unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
|
||||||
(unicode < '0' || unicode > '9'))
|
(unicode < '0' || unicode > '9'))
|
||||||
break;
|
break;
|
||||||
var.append(QChar(unicode));
|
|
||||||
if (++i == str_len)
|
if (++i == str_len)
|
||||||
break;
|
break;
|
||||||
unicode = str_data[i].unicode();
|
unicode = str_data[i].unicode();
|
||||||
// at this point, i points to either the 'term' or 'next' character (which is in 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) {
|
if (var_type == VAR && unicode == LPAREN) {
|
||||||
var_type = FUNCTION;
|
var_type = FUNCTION;
|
||||||
|
name_start = i + 1;
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
forever {
|
forever {
|
||||||
if (++i == str_len)
|
if (++i == str_len)
|
||||||
@@ -1461,8 +1537,8 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(
|
|||||||
break;
|
break;
|
||||||
--depth;
|
--depth;
|
||||||
}
|
}
|
||||||
args.append(QChar(unicode));
|
|
||||||
}
|
}
|
||||||
|
args = QString(str_data + name_start, i - name_start);
|
||||||
if (++i < str_len)
|
if (++i < str_len)
|
||||||
unicode = str_data[i].unicode();
|
unicode = str_data[i].unicode();
|
||||||
else
|
else
|
||||||
@@ -1483,8 +1559,6 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(
|
|||||||
// move the 'cursor' back to the last char of the thing we were looking at
|
// move the 'cursor' back to the last char of the thing we were looking at
|
||||||
--i;
|
--i;
|
||||||
}
|
}
|
||||||
// since i never points to the 'next' character, there is no reason for this to be set
|
|
||||||
unicode = 0;
|
|
||||||
|
|
||||||
QStringList replacement;
|
QStringList replacement;
|
||||||
if (var_type == ENVIRON) {
|
if (var_type == ENVIRON) {
|
||||||
@@ -1496,69 +1570,51 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(
|
|||||||
} else if (var_type == VAR) {
|
} else if (var_type == VAR) {
|
||||||
replacement = values(var);
|
replacement = values(var);
|
||||||
}
|
}
|
||||||
if (!(replaced++) && start_var)
|
|
||||||
current = str.left(start_var);
|
|
||||||
if (!replacement.isEmpty()) {
|
if (!replacement.isEmpty()) {
|
||||||
if (quote) {
|
if (quote) {
|
||||||
current += replacement.join(ProFileOption::field_sep);
|
appendString(replacement.join(ProFileOption::field_sep),
|
||||||
|
¤t, &ptr, &pending);
|
||||||
} else {
|
} else {
|
||||||
current += replacement.takeFirst();
|
appendString(replacement.first(), ¤t, &ptr, &pending);
|
||||||
if (!replacement.isEmpty()) {
|
if (replacement.size() > 1) {
|
||||||
if (!current.isEmpty())
|
flushCurrent(&ret, ¤t, &ptr, &pending);
|
||||||
ret.append(current);
|
pending = replacement.last();
|
||||||
current = replacement.takeLast();
|
if (replacement.size() > 2) {
|
||||||
if (!replacement.isEmpty())
|
// FIXME: ret.reserve(ret.size() + replacement.size() - 2);
|
||||||
ret += replacement;
|
for (int i = 1; i < replacement.size() - 1; ++i)
|
||||||
|
ret << replacement.at(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
replaced = true;
|
||||||
if (replaced)
|
|
||||||
current.append(QLatin1Char('$'));
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (quote && unicode == quote) {
|
|
||||||
unicode = 0;
|
|
||||||
quote = 0;
|
|
||||||
} else if (unicode == BACKSLASH) {
|
} else if (unicode == BACKSLASH) {
|
||||||
bool escape = false;
|
static const char symbols[] = "[]{}()$\\'\"";
|
||||||
const char *symbols = "[]{}()$\\'\"";
|
ushort unicode2 = str_data[i+1].unicode();
|
||||||
for (const char *s = symbols; *s; ++s) {
|
if (!(unicode2 & 0xff00) && strchr(symbols, unicode2)) {
|
||||||
if (str_data[i+1].unicode() == (ushort)*s) {
|
unicode = unicode2;
|
||||||
i++;
|
++i;
|
||||||
escape = true;
|
|
||||||
if (!(replaced++))
|
|
||||||
current = str.left(start_var);
|
|
||||||
current.append(str.at(i));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} else if (quote) {
|
||||||
|
if (unicode == quote) {
|
||||||
|
quote = 0;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (escape || !replaced)
|
} else {
|
||||||
unicode =0;
|
if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
|
||||||
} else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
|
|
||||||
quote = unicode;
|
quote = unicode;
|
||||||
unicode = 0;
|
continue;
|
||||||
if (!(replaced++) && i)
|
} else if ((do_semicolon && unicode == SEMICOLON) ||
|
||||||
current = str.left(i);
|
unicode == SPACE || unicode == TAB) {
|
||||||
} else if (!quote && ((do_semicolon && unicode == SEMICOLON) ||
|
flushCurrent(&ret, ¤t, &ptr, &pending);
|
||||||
unicode == SPACE || unicode == TAB)) {
|
continue;
|
||||||
unicode = 0;
|
|
||||||
if (!(replaced++) && i)
|
|
||||||
current = str.left(i);
|
|
||||||
if (!current.isEmpty()) {
|
|
||||||
ret.append(current);
|
|
||||||
current.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (replaced && unicode)
|
appendChar(unicode, ¤t, &ptr, &pending);
|
||||||
current.append(QChar(unicode));
|
|
||||||
}
|
|
||||||
if (!replaced) {
|
|
||||||
if (!str.isEmpty())
|
|
||||||
ret.append(str);
|
|
||||||
} else if (!current.isEmpty()) {
|
|
||||||
ret.append(current);
|
|
||||||
}
|
}
|
||||||
|
flushFinal(&ret, current, ptr, pending, str, replaced);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user