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:
parent
53819f0ca9
commit
d33025fe10
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
Loading…
Reference in New Issue