Optimize qmldir parsing
authorMartin Jones <martin.jones@nokia.com>
Fri, 18 May 2012 05:53:51 +0000 (15:53 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 24 May 2012 00:35:03 +0000 (02:35 +0200)
From 840K instr to 340K instr for large project.

Change-Id: Ib1e4d5ea94001b6650211b96f262db28a05d8260
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
src/qml/qml/qqmldirparser.cpp
src/qml/qml/qqmldirparser_p.h
src/qml/qml/qqmltypeloader.cpp

index 2fb47cc..0805a24 100644 (file)
 #include "qqmlglobal_p.h"
 
 #include <QtQml/qqmlfile.h>
-#include <QtCore/QTextStream>
-#include <QtCore/QFile>
 #include <QtCore/QtDebug>
 
 QT_BEGIN_NAMESPACE
 
+static int parseInt(const QStringRef &str, bool *ok)
+{
+    int pos = 0;
+    int number = 0;
+    while (pos < str.length() && str.at(pos).isDigit()) {
+        if (pos != 0)
+            number *= 10;
+        number += str.at(pos).unicode() - '0';
+        ++pos;
+    }
+    if (pos != str.length())
+        *ok = false;
+    else
+        *ok = true;
+    return number;
+}
+
 QQmlDirParser::QQmlDirParser()
-    : _isParsed(false)
 {
 }
 
@@ -59,79 +73,69 @@ QQmlDirParser::~QQmlDirParser()
 {
 }
 
-QString QQmlDirParser::source() const
-{
-    return _source;
+inline static void scanSpace(const QChar *&ch) {
+    while (ch->isSpace() && !ch->isNull() && *ch != QLatin1Char('\n'))
+        ++ch;
 }
 
-void QQmlDirParser::setSource(const QString &source)
-{
-    _isParsed = false;
-    _source = source;
+inline static void scanToEnd(const QChar *&ch) {
+    while (*ch != QLatin1Char('\n') && !ch->isNull())
+        ++ch;
 }
 
-bool QQmlDirParser::isParsed() const
-{
-    return _isParsed;
+inline static void scanWord(const QChar *&ch) {
+    while (!ch->isSpace() && !ch->isNull())
+        ++ch;
 }
 
 /*!
 \a url is used for generating errors.
 */
-bool QQmlDirParser::parse()
+bool QQmlDirParser::parse(const QString &source)
 {
-    if (_isParsed)
-        return true;
-
-    _isParsed = true;
     _errors.clear();
     _plugins.clear();
     _components.clear();
     _scripts.clear();
 
-    QTextStream stream(&_source);
     int lineNumber = 0;
 
-    forever {
+    const QChar *ch = source.constData();
+    while (!ch->isNull()) {
         ++lineNumber;
 
-        const QString line = stream.readLine();
-        if (line.isNull())
+        const QChar *lineStart = ch;
+
+        scanSpace(ch);
+        if (*ch == QLatin1Char('\n')) {
+            ++ch;
+            continue;
+        }
+        if (ch->isNull())
             break;
 
         QString sections[3];
         int sectionCount = 0;
 
-        int index = 0;
-        const int length = line.length();
-
-        while (index != length) {
-            const QChar ch = line.at(index);
-
-            if (ch.isSpace()) {
-                do { ++index; }
-                while (index != length && line.at(index).isSpace());
-
-            } else if (ch == QLatin1Char('#')) {
-                // recognized a comment
+        do {
+            if (*ch == QLatin1Char('#')) {
+                scanToEnd(ch);
                 break;
-
+            }
+            const QChar *start = ch;
+            scanWord(ch);
+            if (sectionCount < 3) {
+                sections[sectionCount++] = source.mid(start-source.constData(), ch-start);
             } else {
-                const int start = index;
-
-                do { ++index; }
-                while (index != length && !line.at(index).isSpace());
-
-                const QString lexeme = line.mid(start, index - start);
-
-                if (sectionCount >= 3) {
-                    reportError(lineNumber, start, QLatin1String("unexpected token"));
-
-                } else {
-                    sections[sectionCount++] = lexeme;
-                }
+                reportError(lineNumber, start-lineStart, QLatin1String("unexpected token"));
+                scanToEnd(ch);
+                break;
             }
-        }
+            scanSpace(ch);
+        } while (*ch != QLatin1Char('\n') && !ch->isNull());
+
+        if (!ch->isNull())
+            ++ch;
 
         if (sectionCount == 0) {
             continue; // no sections, no party.
@@ -182,10 +186,10 @@ bool QQmlDirParser::parse()
                 reportError(lineNumber, -1, QLatin1String("unexpected '.'"));
             } else {
                 bool validVersionNumber = false;
-                const int majorVersion = version.left(dotIndex).toInt(&validVersionNumber);
+                const int majorVersion = parseInt(QStringRef(&version, 0, dotIndex), &validVersionNumber);
 
                 if (validVersionNumber) {
-                    const int minorVersion = version.mid(dotIndex + 1).toInt(&validVersionNumber);
+                    const int minorVersion = parseInt(QStringRef(&version, dotIndex+1, version.length()-dotIndex-1), &validVersionNumber);
 
                     if (validVersionNumber) {
                         const QString &fileName = sections[2];
index 4de39e0..a4fbf57 100644 (file)
@@ -70,11 +70,7 @@ public:
     QQmlDirParser();
     ~QQmlDirParser();
 
-    QString source() const;
-    void setSource(const QString &source);
-
-    bool isParsed() const;
-    bool parse();
+    bool parse(const QString &source);
 
     bool hasError() const;
     void setError(const QQmlError &);
@@ -143,14 +139,12 @@ private:
 
 private:
     QList<QQmlError> _errors;
-    QString _source;
     QHash<QHashedStringRef,Component> _components; // multi hash
     QList<Script> _scripts;
     QList<Plugin> _plugins;
 #ifdef QT_CREATOR
     QList<TypeInfo> _typeInfos;
 #endif
-    unsigned _isParsed: 1;
 };
 
 typedef QHash<QHashedStringRef,QQmlDirParser::Component> QQmlDirComponents;
index 228f2f9..f76ded6 100644 (file)
@@ -1509,8 +1509,7 @@ const QQmlDirParser *QQmlTypeLoader::qmlDirParser(const QString &filePath,
             if (file.isError()) {
                 ERROR(NOT_READABLE_ERROR.arg(filePath));
             } else {
-                qmldirParser->setSource(QString::fromUtf8(file.data(), file.size()));
-                qmldirParser->parse();
+                qmldirParser->parse(QString::fromUtf8(file.data(), file.size()));
             }
 
         } else {
@@ -1533,13 +1532,11 @@ const QQmlDirParser *QQmlTypeLoader::qmlDirParser(const QString &filePath,
                     if (file.isError()) {
                         ERROR(NOT_READABLE_ERROR.arg(filePath));
                     } else {
-                        qmldirParser->setSource(QString::fromUtf8(file.data(), file.size()));
-                        qmldirParser->parse();
+                        qmldirParser->parse(QString::fromUtf8(file.data(), file.size()));
                     }
                 } else {
                     data += file.readAll();
-                    qmldirParser->setSource(QString::fromUtf8(data));
-                    qmldirParser->parse();
+                    qmldirParser->parse(QString::fromUtf8(data));
                 }
             } else {
                 ERROR(NOT_READABLE_ERROR.arg(filePath));
@@ -2158,8 +2155,7 @@ const QQmlDirComponents &QQmlQmldirData::dirComponents() const
 void QQmlQmldirData::dataReceived(const Data &data)
 {
     QQmlDirParser parser;
-    parser.setSource(QString::fromUtf8(data.data(), data.size()));
-    parser.parse();
+    parser.parse(QString::fromUtf8(data.data(), data.size()));
     m_components = parser.components();
 }