From 12905fa30f3b92d28dff0e4dc6f32ea516cc42f6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 1 Jul 2013 15:26:01 +0200 Subject: Cut away 2/3 of the startup time on Linux Optimize the parser of the X11 compose tables. Parsing these was responsible for over 90% of the startup time in Qt 5.1. Change-Id: Ifddc3f30828791e51a755f92791c26ffe43a9cd3 Reviewed-by: Konstantin Ritt Reviewed-by: Lars Knoll --- .../compose/generator/qtablegenerator.cpp | 195 +++++++++++++-------- .../compose/generator/qtablegenerator.h | 22 ++- .../compose/qcomposeplatforminputcontext.cpp | 6 +- .../compose/qcomposeplatforminputcontext.h | 2 +- 4 files changed, 138 insertions(+), 87 deletions(-) (limited to 'src') diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp index f746207cc0..116c6cfa7d 100644 --- a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp +++ b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp @@ -41,7 +41,6 @@ #include "qtablegenerator.h" -#include #include #include #include @@ -54,7 +53,8 @@ #include #endif -//#define DEBUG_GENERATOR +#include // strchr, strncmp, etc. +#include // strncasecmp TableGenerator::TableGenerator() : m_state(NoErrors), m_systemComposeDir(QString()) @@ -111,11 +111,9 @@ void TableGenerator::findComposeFile() // check for the system provided compose files if (!found && cleanState()) { - readLocaleMappings(); + QString table = readLocaleMappings(locale().toUpper().toUtf8()); if (cleanState()) { - - QString table = m_localeToTable.value(locale().toUpper()); if (table.isEmpty()) // no table mappings for the system's locale in the compose.dir m_state = UnsupportedLocale; @@ -174,28 +172,49 @@ QString TableGenerator::locale() const return QLatin1String(name); } -void TableGenerator::readLocaleMappings() +QString TableGenerator::readLocaleMappings(const QByteArray &locale) { QFile mappings(systemComposeDir() + QLatin1String("/compose.dir")); - if (mappings.exists()) { - mappings.open(QIODevice::ReadOnly); - QTextStream in(&mappings); - // formating of compose.dir has some inconsistencies - while (!in.atEnd()) { - QString line = in.readLine(); - if (!line.startsWith("#") && line.size() != 0 && - line.at(0).isLower()) { + QString file; + if (mappings.open(QIODevice::ReadOnly)) { + const int localeNameLength = locale.size(); + const char * const localeData = locale.constData(); - QStringList pair = line.split(QRegExp(QLatin1String("\\s+"))); - QString table = pair.at(0); - if (table.endsWith(QLatin1String(":"))) - table.remove(table.size() - 1, 1); - - m_localeToTable.insert(pair.at(1).toUpper(), table); + char l[1024]; + // formating of compose.dir has some inconsistencies + while (!mappings.atEnd()) { + int read = mappings.readLine(l, sizeof(l)); + if (read <= 0) + break; + + char *line = l; + if (*line >= 'a' && *line <= 'z') { + // file name + while (*line && *line != ':' && *line != ' ' && *line != '\t') + ++line; + if (!*line) + continue; + const char * const composeFileNameEnd = line; + *line = '\0'; + ++line; + + // locale name + while (*line && (*line == ' ' || *line == '\t')) + ++line; + const char * const lc = line; + while (*line && *line != ' ' && *line != '\t' && *line != '\n') + ++line; + *line = '\0'; + + if (localeNameLength == (line - lc) && !strncasecmp(lc, localeData, line - lc)) { + file = QString::fromUtf8(l, composeFileNameEnd - l); + break; + } } } mappings.close(); } + return file; } bool TableGenerator::processFile(QString composeFileName) @@ -215,7 +234,7 @@ TableGenerator::~TableGenerator() { } -QList TableGenerator::composeTable() const +QVector TableGenerator::composeTable() const { return m_composeTable; } @@ -225,15 +244,14 @@ void TableGenerator::parseComposeFile(QFile *composeFile) #ifdef DEBUG_GENERATOR qDebug() << "TableGenerator::parseComposeFile: " << composeFile->fileName(); #endif - QTextStream in(composeFile); - while (!in.atEnd()) { - QString line = in.readLine(); - if (line.startsWith(QLatin1String("<"))) { + char line[1024]; + while (!composeFile->atEnd()) { + composeFile->readLine(line, sizeof(line)); + if (*line == '<') parseKeySequence(line); - } else if (line.startsWith(QLatin1String("include"))) { - parseIncludeInstruction(line); - } + else if (!strncmp(line, "include", 7)) + parseIncludeInstruction(QString::fromUtf8(line)); } composeFile->close(); @@ -290,79 +308,103 @@ ushort TableGenerator::keysymToUtf8(quint32 sym) return QString::fromUtf8(chars).at(0).unicode(); } -quint32 TableGenerator::stringToKeysym(QString keysymName) +static inline int fromBase8(const char *s, const char *end) { - quint32 keysym; - QByteArray keysymArray = keysymName.toLatin1(); - const char *name = keysymArray.constData(); - - if ((keysym = xkb_keysym_from_name(name, (xkb_keysym_flags)0)) == XKB_KEY_NoSymbol) - qWarning() << QString("Qt Warning - invalid keysym: %1").arg(keysymName); + int result = 0; + while (*s && s != end) { + if (*s <= '0' && *s >= '7') + return 0; + result *= 8; + result += *s - '0'; + ++s; + } + return result; +} - return keysym; +static inline int fromBase16(const char *s, const char *end) +{ + int result = 0; + while (*s && s != end) { + result *= 16; + if (*s >= '0' && *s <= '9') + result += *s - '0'; + else if (*s >= 'a' && *s <= 'f') + result += *s - 'a' + 10; + else if (*s >= 'A' && *s <= 'F') + result += *s - 'A' + 10; + else + return 0; + ++s; + } + return result; } -void TableGenerator::parseKeySequence(QString line) +void TableGenerator::parseKeySequence(char *line) { // we are interested in the lines with the following format: // : "♬" U266c # BEAMED SIXTEENTH NOTE - int keysEnd = line.indexOf(QLatin1String(":")); - QString keys = line.left(keysEnd).trimmed(); - - // find the key sequence - QString regexp = QStringLiteral("<[^>]+>"); - QRegularExpression reg(regexp); - QRegularExpressionMatchIterator i = reg.globalMatch(keys); - QStringList keyList; - while (i.hasNext()) { - QRegularExpressionMatch match = i.next(); - QString word = match.captured(0); - keyList << word; - } + char *keysEnd = strchr(line, ':'); + if (!keysEnd) + return; QComposeTableElement elem; - QString quote = QStringLiteral("\""); // find the composed value - strings may be direct text encoded in the locale // for which the compose file is to be used, or an escaped octal or hexadecimal // character code. Octal codes are specified as "\123" and hexadecimal codes as "\0x123a". - int composeValueIndex = line.indexOf(quote, keysEnd) + 1; - const QChar valueType(line.at(composeValueIndex)); + char *composeValue = strchr(keysEnd, '"'); + if (!composeValue) + return; + ++composeValue; + + char *composeValueEnd = strchr(composeValue, '"'); + if (!composeValueEnd) + return; - if (valueType == '\\' && line.at(composeValueIndex + 1).isDigit()) { + if (*composeValue == '\\' && composeValue[1] >= '0' && composeValue[1] <= '9') { // handle octal and hex code values - QChar detectBase(line.at(composeValueIndex + 2)); - QString codeValue = line.mid(composeValueIndex + 1, line.lastIndexOf(quote) - composeValueIndex - 1); + char detectBase = composeValue[2]; if (detectBase == 'x') { // hexadecimal character code - elem.value = keysymToUtf8(codeValue.toUInt(0, 16)); + elem.value = keysymToUtf8(fromBase16(composeValue + 3, composeValueEnd)); } else { // octal character code - QString hexStr = QString::number(codeValue.toUInt(0, 8), 16); - elem.value = keysymToUtf8(hexStr.toUInt(0, 16)); + elem.value = keysymToUtf8(fromBase8(composeValue + 1, composeValueEnd)); } } else { // handle direct text encoded in the locale - elem.value = valueType.unicode(); + if (*composeValue == '\\') + ++composeValue; + elem.value = QString::fromUtf8(composeValue).at(0).unicode(); + ++composeValue; } +#ifdef DEBUG_GENERATOR // find the comment - int commnetIndex = line.lastIndexOf(quote) + 1; - elem.comment = line.mid(commnetIndex).trimmed(); - - // Convert to X11 keysym - int count = keyList.length(); - for (int i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) { - if (i < count) { - QString keysym = keyList.at(i); - keysym.remove(keysym.length() - 1, 1); - keysym.remove(0, 1); + elem.comment = QString::fromUtf8(composeValueEnd + 1).trimmed(); +#endif - if (keysym == QLatin1String("dead_inverted_breve")) - keysym = QStringLiteral("dead_invertedbreve"); - else if (keysym == QLatin1String("dead_double_grave")) - keysym = QStringLiteral("dead_doublegrave"); + // find the key sequence and convert to X11 keysym + char *k = line; + const char *kend = keysEnd; - elem.keys[i] = stringToKeysym(keysym); + for (int i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) { + // find the next pair of angle brackets and get the contents within + while (k < kend && *k != '<') + ++k; + char *sym = ++k; + while (k < kend && *k != '>') + ++k; + *k = '\0'; + if (k < kend) { + elem.keys[i] = xkb_keysym_from_name(sym, (xkb_keysym_flags)0); + if (elem.keys[i] == XKB_KEY_NoSymbol) { + if (!strcmp(sym, "dead_inverted_breve")) + elem.keys[i] = XKB_KEY_dead_invertedbreve; + else if (!strcmp(sym, "dead_double_grave")) + elem.keys[i] = XKB_KEY_dead_doublegrave; + else + qWarning() << QString("Qt Warning - invalid keysym: %1").arg(sym); + } } else { elem.keys[i] = 0; } @@ -372,6 +414,7 @@ void TableGenerator::parseKeySequence(QString line) void TableGenerator::printComposeTable() const { +#ifdef DEBUG_GENERATOR if (composeTable().isEmpty()) return; @@ -393,8 +436,8 @@ void TableGenerator::printComposeTable() const .arg(comma) .arg(elem.comment)); } - qDebug() << "output: \n" << output; +#endif } void TableGenerator::orderComposeTable() diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h index cc1db20432..aa65b7b895 100644 --- a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h +++ b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h @@ -42,19 +42,29 @@ #ifndef QTABLEGENERATOR_H #define QTABLEGENERATOR_H -#include +#include #include #include #include #define QT_KEYSEQUENCE_MAX_LEN 6 +//#define DEBUG_GENERATOR + struct QComposeTableElement { uint keys[QT_KEYSEQUENCE_MAX_LEN]; uint value; +#ifdef DEBUG_GENERATOR QString comment; +#endif }; +#ifndef DEBUG_GENERATOR +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(QComposeTableElement, Q_PRIMITIVE_TYPE); +QT_END_NAMESPACE +#endif + class Compare { public: @@ -97,12 +107,12 @@ public: void printComposeTable() const; void orderComposeTable(); - QList composeTable() const; + QVector composeTable() const; TableState tableState() const { return m_state; } protected: bool processFile(QString composeFileName); - void parseKeySequence(QString line); + void parseKeySequence(char *line); void parseIncludeInstruction(QString line); void findComposeFile(); @@ -110,16 +120,14 @@ protected: QString systemComposeDir(); ushort keysymToUtf8(quint32 sym); - quint32 stringToKeysym(QString keysymName); - void readLocaleMappings(); + QString readLocaleMappings(const QByteArray &locale); void initPossibleLocations(); bool cleanState() const { return ((m_state & NoErrors) == NoErrors); } QString locale() const; private: - QList m_composeTable; - QMap m_localeToTable; + QVector m_composeTable; TableState m_state; QString m_systemComposeDir; QList m_possibleLocations; diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp index 433c9eec37..611b9fdd9b 100644 --- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp @@ -169,11 +169,11 @@ static bool isDuplicate(const QComposeTableElement &lhs, const QComposeTableElem bool QComposeInputContext::checkComposeTable() { - QList::iterator it = - qLowerBound(m_composeTable.begin(), m_composeTable.end(), m_composeBuffer, Compare()); + QVector::const_iterator it = + qLowerBound(m_composeTable.constBegin(), m_composeTable.constEnd(), m_composeBuffer, Compare()); // prevent dereferencing an 'end' iterator, which would result in a crash - if (it == m_composeTable.end()) + if (it == m_composeTable.constEnd()) it -= 1; QComposeTableElement elem = *it; diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h index 1ced2f8ded..def63486a7 100644 --- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h +++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h @@ -75,7 +75,7 @@ protected: private: QObject *m_focusObject; - QList m_composeTable; + QVector m_composeTable; uint m_composeBuffer[QT_KEYSEQUENCE_MAX_LEN + 1]; TableGenerator::TableState m_tableState; }; -- cgit v1.2.3