Fix quadratic behavior in QMetaObjectBuilder when writing string table
QHash::key() is O(n) and we're calling it n times. That can make repeated calls to the meta object builder very slow, as for example QQmlPropertyMap when inserting properties repeatedly. Fortunately this is easy to fix, as the value in the hash map is also the index, so we can simply iterate over the hash once. With the exception of the class name, which we have to treat specially to ensure that it is always the first entry in the string table. Task-number: QTBUG-32720 Change-Id: Ic954c45c454107feee83216131f601cc69d4c63b Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
parent
0c936ca83c
commit
105d10dea9
@ -1075,8 +1075,14 @@ int QMetaObjectBuilder::indexOfClassInfo(const QByteArray& name)
|
||||
\brief The QMetaStringTable class can generate a meta-object string table at runtime.
|
||||
*/
|
||||
|
||||
QMetaStringTable::QMetaStringTable()
|
||||
: m_index(0) {}
|
||||
QMetaStringTable::QMetaStringTable(const QByteArray &className)
|
||||
: m_index(0)
|
||||
, m_className(className)
|
||||
{
|
||||
const int index = enter(m_className);
|
||||
Q_ASSERT(index == 0);
|
||||
Q_UNUSED(index);
|
||||
}
|
||||
|
||||
// Enters the given value into the string table (if it hasn't already been
|
||||
// entered). Returns the index of the string.
|
||||
@ -1106,30 +1112,45 @@ int QMetaStringTable::blobSize() const
|
||||
return size;
|
||||
}
|
||||
|
||||
static void writeString(char *out, int i, const QByteArray &str,
|
||||
const int offsetOfStringdataMember, int &stringdataOffset)
|
||||
{
|
||||
int size = str.size();
|
||||
qptrdiff offset = offsetOfStringdataMember + stringdataOffset
|
||||
- i * sizeof(QByteArrayData);
|
||||
const QByteArrayData data =
|
||||
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset);
|
||||
|
||||
memcpy(out + i * sizeof(QByteArrayData), &data, sizeof(QByteArrayData));
|
||||
|
||||
memcpy(out + offsetOfStringdataMember + stringdataOffset, str.constData(), size);
|
||||
out[offsetOfStringdataMember + stringdataOffset + size] = '\0';
|
||||
|
||||
stringdataOffset += size + 1;
|
||||
}
|
||||
|
||||
// Writes strings to string data struct.
|
||||
// The struct consists of an array of QByteArrayData, followed by a char array
|
||||
// containing the actual strings. This format must match the one produced by
|
||||
// moc (see generator.cpp).
|
||||
void QMetaStringTable::writeBlob(char *out)
|
||||
void QMetaStringTable::writeBlob(char *out) const
|
||||
{
|
||||
Q_ASSERT(!(reinterpret_cast<quintptr>(out) & (preferredAlignment()-1)));
|
||||
|
||||
int offsetOfStringdataMember = m_entries.size() * sizeof(QByteArrayData);
|
||||
int stringdataOffset = 0;
|
||||
for (int i = 0; i < m_entries.size(); ++i) {
|
||||
const QByteArray &str = m_entries.key(i);
|
||||
int size = str.size();
|
||||
qptrdiff offset = offsetOfStringdataMember + stringdataOffset
|
||||
- i * sizeof(QByteArrayData);
|
||||
const QByteArrayData data =
|
||||
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset);
|
||||
|
||||
memcpy(out + i * sizeof(QByteArrayData), &data, sizeof(QByteArrayData));
|
||||
// qt_metacast expects the first string in the string table to be the class name.
|
||||
writeString(out, /*index*/0, m_className, offsetOfStringdataMember, stringdataOffset);
|
||||
|
||||
memcpy(out + offsetOfStringdataMember + stringdataOffset, str.constData(), size);
|
||||
out[offsetOfStringdataMember + stringdataOffset + size] = '\0';
|
||||
for (Entries::ConstIterator it = m_entries.constBegin(), end = m_entries.constEnd();
|
||||
it != end; ++it) {
|
||||
const int i = it.value();
|
||||
if (i == 0)
|
||||
continue;
|
||||
const QByteArray &str = it.key();
|
||||
|
||||
stringdataOffset += size + 1;
|
||||
writeString(out, i, str, offsetOfStringdataMember, stringdataOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1270,8 +1291,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
|
||||
// Reset the current data position to just past the QMetaObjectPrivate.
|
||||
dataIndex = MetaObjectPrivateFieldCount;
|
||||
|
||||
QMetaStringTable strings;
|
||||
strings.enter(d->className);
|
||||
QMetaStringTable strings(d->className);
|
||||
|
||||
// Output the class infos,
|
||||
Q_ASSERT(!buf || dataIndex == pmeta->classInfoData);
|
||||
|
@ -323,18 +323,19 @@ private:
|
||||
class Q_CORE_EXPORT QMetaStringTable
|
||||
{
|
||||
public:
|
||||
QMetaStringTable();
|
||||
QMetaStringTable(const QByteArray &className);
|
||||
|
||||
int enter(const QByteArray &value);
|
||||
|
||||
static int preferredAlignment();
|
||||
int blobSize() const;
|
||||
void writeBlob(char *out);
|
||||
void writeBlob(char *out) const;
|
||||
|
||||
private:
|
||||
typedef QHash<QByteArray, int> Entries; // string --> index mapping
|
||||
Entries m_entries;
|
||||
int m_index;
|
||||
QByteArray m_className;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::AddMembers)
|
||||
|
@ -445,8 +445,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
|
||||
data_size += 2 + mm.inputTypes.count() + mm.outputTypes.count();
|
||||
idata.resize(data_size + 1);
|
||||
|
||||
QMetaStringTable strings;
|
||||
strings.enter(className.toLatin1());
|
||||
QMetaStringTable strings(className.toLatin1());
|
||||
|
||||
int offset = header->methodData;
|
||||
int parametersOffset = offset + header->methodCount * 5;
|
||||
|
@ -75,6 +75,8 @@ private slots:
|
||||
void usage_connect();
|
||||
void usage_templateConnect();
|
||||
|
||||
void classNameFirstInStringData();
|
||||
|
||||
private:
|
||||
static bool checkForSideEffects
|
||||
(const QMetaObjectBuilder& builder,
|
||||
@ -1694,6 +1696,20 @@ void tst_QMetaObjectBuilder::usage_templateConnect()
|
||||
QVERIFY(!con);
|
||||
}
|
||||
|
||||
void tst_QMetaObjectBuilder::classNameFirstInStringData()
|
||||
{
|
||||
QMetaObjectBuilder builder;
|
||||
builder.addMetaObject(&SomethingOfEverything::staticMetaObject);
|
||||
builder.setClassName(QByteArrayLiteral("TestClass"));
|
||||
QMetaObject *mo = builder.toMetaObject();
|
||||
|
||||
QByteArrayDataPtr header;
|
||||
header.ptr = const_cast<QByteArrayData*>(mo->d.stringdata);
|
||||
QCOMPARE(QByteArray(header), QByteArrayLiteral("TestClass"));
|
||||
|
||||
free(mo);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QMetaObjectBuilder)
|
||||
|
||||
#include "tst_qmetaobjectbuilder.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user