Rewrite identifier table

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>
This commit is contained in:
Lars Knoll 2013-06-27 09:04:11 +02:00 committed by The Qt Project
parent 53819f0ca9
commit d33025fe10
7 changed files with 254 additions and 43 deletions

View 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)

View File

@ -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

View File

@ -59,51 +59,29 @@ struct Identifier
struct IdentifierTable
{
ExecutionEngine *engine;
QHash<QString, String *> identifiers;
int alloc;
int size;
int numBits;
String **entries;
void addEntry(String *str);
public:
IdentifierTable(ExecutionEngine *engine) : engine(engine) {}
IdentifierTable(ExecutionEngine *engine);
~IdentifierTable();
String *insert(const QString &s)
{
QHash<QString, String*>::const_iterator it = identifiers.find(s);
if (it != identifiers.constEnd())
return it.value();
String *insertString(const QString &s);
String *str = engine->newString(s);
str->createHashValue();
if (str->subtype == String::StringType_ArrayIndex)
return str;
str->identifier = new Identifier;
str->identifier->string = str->toQString();
str->identifier->hashValue = str->hashValue();
identifiers.insert(s, str);
return str;
}
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();
}
};

View 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);

View 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;
}

View 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();

View 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 \