Rewrite identifier table
authorLars Knoll <lars.knoll@digia.com>
Thu, 27 Jun 2013 07:04:11 +0000 (09:04 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 28 Jun 2013 12:56:14 +0000 (14:56 +0200)
Use a hand written hash table for the identifiers. This
saves quite some memory, speeds up the table, and allows
converting 8bit strings into identifiers.

Change-Id: Id289764097b50bf6c1fc83b1b8c930b5e62a7538
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/qml/v4/qv4engine.cpp
src/qml/qml/v4/qv4identifier.cpp [new file with mode: 0644]
src/qml/qml/v4/qv4identifier_p.h
src/qml/qml/v4/qv4internalclass.cpp
src/qml/qml/v4/qv4string.cpp
src/qml/qml/v4/qv4string_p.h
src/qml/qml/v4/v4.pri

index 0139a05..035d707 100644 (file)
@@ -430,7 +430,7 @@ String *ExecutionEngine::newString(const QString &s)
 
 String *ExecutionEngine::newIdentifier(const QString &text)
 {
-    return identifierTable->insert(text);
+    return identifierTable->insertString(text);
 }
 
 Object *ExecutionEngine::newStringObject(const Value &value)
diff --git a/src/qml/qml/v4/qv4identifier.cpp b/src/qml/qml/v4/qv4identifier.cpp
new file mode 100644 (file)
index 0000000..e3c70ff
--- /dev/null
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qv4identifier_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+static const uchar prime_deltas[] = {
+    0,  0,  1,  3,  1,  5,  3,  3,  1,  9,  7,  5,  3,  9, 25,  3,
+    1, 21,  3, 21,  7, 15,  9,  5,  3, 29, 15,  0,  0,  0,  0,  0
+};
+
+static inline int primeForNumBits(int numBits)
+{
+    return (1 << numBits) + prime_deltas[numBits];
+}
+
+IdentifierTable::IdentifierTable(ExecutionEngine *engine)
+    : engine(engine)
+    , size(0)
+    , numBits(8)
+{
+    alloc = primeForNumBits(numBits);
+    entries = (String **)malloc(alloc*sizeof(String *));
+    memset(entries, 0, alloc*sizeof(String *));
+}
+
+IdentifierTable::~IdentifierTable()
+{
+    free(entries);
+}
+
+void IdentifierTable::addEntry(String *str)
+{
+    uint hash = str->hashValue();
+
+    if (str->subtype >= String::StringType_UInt)
+        return;
+
+    str->identifier = new Identifier;
+    str->identifier->string = str->toQString();
+    str->identifier->hashValue = hash;
+
+    bool grow = (alloc <= size*2);
+
+    if (grow) {
+        ++numBits;
+        int newAlloc = primeForNumBits(numBits);
+        String **newEntries = (String **)malloc(newAlloc*sizeof(String *));
+        memset(newEntries, 0, newAlloc*sizeof(String *));
+        for (uint i = 0; i < alloc; ++i) {
+            String *e = entries[i];
+            if (!e)
+                continue;
+            uint idx = e->stringHash % newAlloc;
+            while (newEntries[idx]) {
+                ++idx;
+                idx %= newAlloc;
+            }
+            newEntries[idx] = e;
+        }
+        free(entries);
+        entries = newEntries;
+        alloc = newAlloc;
+    }
+
+    uint idx = hash % alloc;
+    while (entries[idx]) {
+        ++idx;
+        idx %= alloc;
+    }
+    entries[idx] = str;
+    ++size;
+}
+
+
+
+String *IdentifierTable::insertString(const QString &s)
+{
+    uint hash = String::createHashValue(s.constData(), s.length());
+    uint idx = hash % alloc;
+    while (String *e = entries[idx]) {
+        if (e->stringHash == hash && e->toQString() == s)
+            return e;
+        ++idx;
+        idx %= alloc;
+    }
+
+    String *str = engine->newString(s);
+    addEntry(str);
+    return str;
+}
+
+
+Identifier *IdentifierTable::identifier(String *str)
+{
+    if (str->identifier)
+        return str->identifier;
+    uint hash = str->hashValue();
+    if (str->subtype >= String::StringType_UInt)
+        return 0;
+
+    uint idx = hash % alloc;
+    while (String *e = entries[idx]) {
+        if (e->stringHash == hash && e->isEqualTo(str)) {
+            str->identifier = e->identifier;
+            return e->identifier;
+        }
+        ++idx;
+        idx %= alloc;
+    }
+
+    addEntry(str);
+    return str->identifier;
+}
+
+Identifier *IdentifierTable::identifier(const QString &s)
+{
+    return insertString(s)->identifier;
+}
+
+Identifier *IdentifierTable::identifier(const char *s, int len)
+{
+    uint hash = String::createHashValue(s, len);
+    if (hash == UINT_MAX)
+        return identifier(QString::fromUtf8(s, len));
+
+    QLatin1String latin(s, len);
+    uint idx = hash % alloc;
+    while (String *e = entries[idx]) {
+        if (e->stringHash == hash && e->toQString() == latin)
+            return e->identifier;
+        ++idx;
+        idx %= alloc;
+    }
+
+    String *str = engine->newString(QString::fromLatin1(s, len));
+    addEntry(str);
+    return str->identifier;
+}
+
+}
+
+QT_END_NAMESPACE
index b778df8..b8a2e7c 100644 (file)
@@ -59,51 +59,29 @@ struct Identifier
 struct IdentifierTable
 {
     ExecutionEngine *engine;
-    QHash<QString, String *> identifiers;
-public:
 
-    IdentifierTable(ExecutionEngine *engine) : engine(engine) {}
+    int alloc;
+    int size;
+    int numBits;
+    String **entries;
 
-    String *insert(const QString &s)
-    {
-        QHash<QString, String*>::const_iterator it = identifiers.find(s);
-        if (it != identifiers.constEnd())
-            return it.value();
+    void addEntry(String *str);
 
-        String *str = engine->newString(s);
-        str->createHashValue();
-        if (str->subtype == String::StringType_ArrayIndex)
-            return str;
+public:
 
-        str->identifier = new Identifier;
-        str->identifier->string = str->toQString();
-        str->identifier->hashValue = str->hashValue();
-        identifiers.insert(s, str);
+    IdentifierTable(ExecutionEngine *engine);
+    ~IdentifierTable();
 
-        return str;
-    }
+    String *insertString(const QString &s);
 
-    void toIdentifier(String *str) {
-        if (str->identifier)
-            return;
-        if (str->subtype == String::StringType_Unknown)
-            str->createHashValue();
-        if (str->subtype == String::StringType_ArrayIndex)
-            return;
-        QHash<QString, String*>::const_iterator it = identifiers.find(str->toQString());
-        if (it != identifiers.constEnd()) {
-            str->identifier = (*it)->identifier;
-            return;
-        }
-        str->identifier = new Identifier;
-        str->identifier->string = str->toQString();
-        str->identifier->hashValue = str->hashValue();
-        identifiers.insert(str->toQString(), str);
-    }
+    Identifier *identifier(String *str);
+    Identifier *identifier(const QString &s);
+    Identifier *identifier(const char *s, int len);
 
     void mark() {
-        for (QHash<QString, String *>::const_iterator it = identifiers.constBegin(); it != identifiers.constEnd(); ++it)
-            (*it)->mark();
+        for (uint i = 0; i < alloc; ++i)
+            if (entries[i])
+                entries[i]->mark();
     }
 };
 
index 84b17a4..a933637 100644 (file)
@@ -181,7 +181,7 @@ InternalClass *InternalClass::addMember(String *string, PropertyAttributes data,
 {
 //    qDebug() << "InternalClass::addMember()" << string->toQString() << size << hex << (uint)data.m_all << data.type();
     data.resolve();
-    engine->identifierTable->toIdentifier(string);
+    engine->identifierTable->identifier(string);
 
     if (propertyTable.lookup(string->identifier) < size)
         return changeMember(string, data, index);
@@ -237,7 +237,7 @@ void InternalClass::removeMember(Object *object, Identifier *id)
 
 uint InternalClass::find(String *string)
 {
-    engine->identifierTable->toIdentifier(string);
+    engine->identifierTable->identifier(string);
     const Identifier *id = string->identifier;
 
     uint index = propertyTable.lookup(id);
index ad321bc..e4b9676 100644 (file)
@@ -74,6 +74,33 @@ static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok)
     return i;
 }
 
+static uint toArrayIndex(const char *ch, const char *end, bool *ok)
+{
+    *ok = false;
+    uint i = *ch - '0';
+    if (i > 9)
+        return UINT_MAX;
+    ++ch;
+    // reject "01", "001", ...
+    if (i == 0 && ch != end)
+        return UINT_MAX;
+
+    while (ch < end) {
+        uint x = *ch - '0';
+        if (x > 9)
+            return UINT_MAX;
+        uint n = i*10 + x;
+        if (n < i)
+            // overflow
+            return UINT_MAX;
+        i = n;
+        ++ch;
+    }
+    *ok = true;
+    return i;
+}
+
+
 const ManagedVTable String::static_vtbl =
 {
     call,
@@ -210,7 +237,7 @@ uint String::toUInt(bool *ok) const
 
 void String::makeIdentifierImpl()
 {
-    engine()->identifierTable->toIdentifier(this);
+    engine()->identifierTable->identifier(this);
 }
 
 void String::createHashValue() const
@@ -254,3 +281,24 @@ uint String::createHashValue(const QChar *ch, int length)
 
     return h;
 }
+
+uint String::createHashValue(const char *ch, int length)
+{
+    const char *end = ch + length;
+
+    // array indices get their number as hash value
+    bool ok;
+    uint stringHash = toArrayIndex(ch, end, &ok);
+    if (ok)
+        return stringHash;
+
+    uint h = 0xffffffff;
+    while (ch < end) {
+        if (*ch >= 0x80)
+            return UINT_MAX;
+        h = 31 * h + *ch;
+        ++ch;
+    }
+
+    return h;
+}
index 84f2b23..629fa50 100644 (file)
@@ -110,6 +110,7 @@ struct Q_QML_EXPORT String : public Managed {
 
     void createHashValue() const;
     static uint createHashValue(const QChar *ch, int length);
+    static uint createHashValue(const char *ch, int length);
 
     bool startsWithUpper() const {
         return _text.length() && _text.at(0).isUpper();
index 8b695a1..7ccc881 100644 (file)
@@ -24,6 +24,7 @@ SOURCES += \
     $$PWD/qv4isel_p.cpp \
     $$PWD/qv4debugging.cpp \
     $$PWD/qv4lookup.cpp \
+    $$PWD/qv4identifier.cpp \
     $$PWD/qv4mm.cpp \
     $$PWD/qv4managed.cpp \
     $$PWD/qv4internalclass.cpp \