diff options
Diffstat (limited to 'src/corelib')
36 files changed, 2757 insertions, 854 deletions
diff --git a/src/corelib/json/qjsonvalue.cpp b/src/corelib/json/qjsonvalue.cpp index b4a689da60..2eedef67ce 100644 --- a/src/corelib/json/qjsonvalue.cpp +++ b/src/corelib/json/qjsonvalue.cpp @@ -400,7 +400,7 @@ QString QJsonValue::toString() const if (t != String) return QString(); stringData->ref.ref(); // the constructor below doesn't add a ref. - return QString(*(const QConstStringData<1> *)stringData); + return QString(*(const QStaticStringData<1> *)stringData); } /*! diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index cacd999869..a8c21657cb 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -146,6 +146,90 @@ QT_BEGIN_NAMESPACE static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); } +static inline const QByteArrayData &stringData(const QMetaObject *mo, int index) +{ + Q_ASSERT(priv(mo->d.data)->revision >= 7); + const QByteArrayData &data = mo->d.stringdata[index]; + Q_ASSERT(data.ref.isStatic()); + Q_ASSERT(data.alloc == 0); + Q_ASSERT(data.capacityReserved == 0); + Q_ASSERT(data.size >= 0); + return data; +} + +static inline QByteArray toByteArray(const QByteArrayData &d) +{ + return QByteArray(reinterpret_cast<const QStaticByteArrayData<0> &>(d)); +} + +static inline const char *legacyString(const QMetaObject *mo, int index) +{ + Q_ASSERT(priv(mo->d.data)->revision <= 6); + return reinterpret_cast<const char *>(mo->d.stringdata) + index; +} + +static inline const char *rawStringData(const QMetaObject *mo, int index) +{ + if (priv(mo->d.data)->revision >= 7) + return stringData(mo, index).data(); + else + return legacyString(mo, index); +} + +static inline int stringSize(const QMetaObject *mo, int index) +{ + if (priv(mo->d.data)->revision >= 7) + return stringData(mo, index).size; + else + return qstrlen(legacyString(mo, index)); +} + +static inline QByteArray typeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + if (typeInfo & IsUnresolvedType) { + return toByteArray(stringData(mo, typeInfo & TypeNameIndexMask)); + } else { + // ### Use the QMetaType::typeName() that returns QByteArray + const char *t = QMetaType::typeName(typeInfo); + return QByteArray::fromRawData(t, qstrlen(t)); + } +} + +static inline const char *rawTypeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + return typeNameFromTypeInfo(mo, typeInfo).constData(); +} + +static inline int typeFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + if (!(typeInfo & IsUnresolvedType)) + return typeInfo; + return QMetaType::type(toByteArray(stringData(mo, typeInfo & TypeNameIndexMask))); +} + +class QMetaMethodPrivate : public QMetaMethod +{ +public: + static const QMetaMethodPrivate *get(const QMetaMethod *q) + { return static_cast<const QMetaMethodPrivate *>(q); } + + inline QByteArray signature() const; + inline QByteArray name() const; + inline int typesDataIndex() const; + inline const char *rawReturnTypeName() const; + inline int returnType() const; + inline int parameterCount() const; + inline int parametersDataIndex() const; + inline uint parameterTypeInfo(int index) const; + inline int parameterType(int index) const; + inline void getParameterTypes(int *types) const; + inline QList<QByteArray> parameterTypes() const; + inline QList<QByteArray> parameterNames() const; + inline QByteArray tag() const; + +private: + QMetaMethodPrivate(); +}; /*! \since 4.5 @@ -249,12 +333,14 @@ int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) } /*! - \fn const char *QMetaObject::className() const - Returns the class name. \sa superClass() */ +const char *QMetaObject::className() const +{ + return rawStringData(this, 0); +} /*! \fn QMetaObject *QMetaObject::superClass() const @@ -307,7 +393,7 @@ const QObject *QMetaObject::cast(const QObject *obj) const */ QString QMetaObject::tr(const char *s, const char *c, int n) const { - return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::DefaultCodec, n); + return QCoreApplication::translate(rawStringData(this, 0), s, c, QCoreApplication::DefaultCodec, n); } /*! @@ -315,7 +401,7 @@ QString QMetaObject::tr(const char *s, const char *c, int n) const */ QString QMetaObject::trUtf8(const char *s, const char *c, int n) const { - return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::UnicodeUTF8, n); + return QCoreApplication::translate(rawStringData(this, 0), s, c, QCoreApplication::UnicodeUTF8, n); } #endif // QT_NO_TRANSLATION @@ -495,7 +581,37 @@ int QMetaObject::classInfoCount() const return n; } +// Returns true if the method defined by the given meta-object&handle +// matches the given name, argument count and argument types, otherwise +// returns false. +static bool methodMatch(const QMetaObject *m, int handle, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + Q_ASSERT(priv(m->d.data)->revision >= 7); + if (int(m->d.data[handle + 1]) != argc) + return false; + + if (toByteArray(stringData(m, m->d.data[handle])) != name) + return false; + + int paramsIndex = m->d.data[handle + 2] + 1; + for (int i = 0; i < argc; ++i) { + uint typeInfo = m->d.data[paramsIndex + i]; + if (types[i].type()) { + if (types[i].type() != typeFromTypeInfo(m, typeInfo)) + return false; + } else { + if (types[i].name() != typeNameFromTypeInfo(m, typeInfo)) + return false; + } + } + + return true; +} + /** \internal +* \obsolete * helper function for indexOf{Method,Slot,Signal}, returns the relative index of the method within * the baseObject * \a MethodType might be MethodSignal or MethodSlot, or 0 to match everything. @@ -506,6 +622,8 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, const char *method, bool normalizeStringData) { + QByteArray methodName; + QArgumentTypeArray methodArgumentTypes; for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { int i = (MethodType == MethodSignal && priv(m->d.data)->revision >= 4) ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); @@ -513,15 +631,26 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, ? (priv(m->d.data)->signalCount) : 0; if (!normalizeStringData) { for (; i >= end; --i) { - const char *stringdata = m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i]; - if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { - *baseObject = m; - return i; + if (priv(m->d.data)->revision >= 7) { + if (methodName.isEmpty()) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodArgumentTypes); + int handle = priv(m->d.data)->methodData + 5*i; + if (methodMatch(m, handle, methodName, methodArgumentTypes.size(), + methodArgumentTypes.constData())) { + *baseObject = m; + return i; + } + } else { + const char *stringdata = legacyString(m, m->d.data[priv(m->d.data)->methodData + 5*i]); + if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { + *baseObject = m; + return i; + } } } } else if (priv(m->d.data)->revision < 5) { for (; i >= end; --i) { - const char *stringdata = (m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5 * i]); + const char *stringdata = legacyString(m, m->d.data[priv(m->d.data)->methodData + 5 * i]); const QByteArray normalizedSignature = QMetaObject::normalizedSignature(stringdata); if (normalizedSignature == method) { *baseObject = m; @@ -533,6 +662,34 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, return -1; } +/** \internal +* helper function for indexOf{Method,Slot,Signal}, returns the relative index of the method within +* the baseObject +* \a MethodType might be MethodSignal or MethodSlot, or 0 to match everything. +*/ +template<int MethodType> +static inline int indexOfMethodRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { + Q_ASSERT(priv(m->d.data)->revision >= 7); + int i = (MethodType == MethodSignal) + ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); + const int end = (MethodType == MethodSlot) + ? (priv(m->d.data)->signalCount) : 0; + + for (; i >= end; --i) { + int handle = priv(m->d.data)->methodData + 5*i; + if (methodMatch(m, handle, name, argc, types)) { + *baseObject = m; + return i; + } + } + } + return -1; +} + /*! \since 4.5 @@ -548,10 +705,16 @@ int QMetaObject::indexOfConstructor(const char *constructor) const { if (priv(d.data)->revision < 2) return -1; - for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { - const char *data = d.stringdata + d.data[priv(d.data)->constructorData + 5*i]; - if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { - return i; + else if (priv(d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(constructor, types); + return QMetaObjectPrivate::indexOfConstructor(this, name, types.size(), types.constData()); + } else { + for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { + const char *data = legacyString(this, d.data[priv(d.data)->constructorData + 5*i]); + if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { + return i; + } } } return -1; @@ -568,16 +731,60 @@ int QMetaObject::indexOfConstructor(const char *constructor) const int QMetaObject::indexOfMethod(const char *method) const { const QMetaObject *m = this; - int i = indexOfMethodRelative<0>(&m, method, false); - if (i < 0) { - m = this; - i = indexOfMethodRelative<0>(&m, method, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(method, types); + i = indexOfMethodRelative<0>(&m, name, types.size(), types.constData()); + } else { + i = indexOfMethodRelative<0>(&m, method, false); + if (i < 0) { + m = this; + i = indexOfMethodRelative<0>(&m, method, true); + } } if (i >= 0) i += m->methodOffset(); return i; } +// Parses a string of comma-separated types into QArgumentTypes. +static void argumentTypesFromString(const char *str, const char *end, + QArgumentTypeArray &types) +{ + Q_ASSERT(str <= end); + while (str != end) { + if (!types.isEmpty()) + ++str; // Skip comma + const char *begin = str; + int level = 0; + while (str != end && (level > 0 || *str != ',')) { + if (*str == '<') + ++level; + else if (*str == '>') + --level; + ++str; + } + types += QArgumentType(QByteArray(begin, str - begin)); + } +} + +// Given a method \a signature (e.g. "foo(int,double)"), this function +// populates the argument \a types array and returns the method name. +QByteArray QMetaObjectPrivate::decodeMethodSignature( + const char *signature, QArgumentTypeArray &types) +{ + const char *lparens = strchr(signature, '('); + if (!lparens) + return QByteArray(); + const char *rparens = strchr(lparens + 1, ')'); + if (!rparens || *(rparens+1)) + return QByteArray(); + int nameLength = lparens - signature; + argumentTypesFromString(lparens + 1, rparens, types); + return QByteArray::fromRawData(signature, nameLength); +} + /*! Finds \a signal and returns its index; otherwise returns -1. @@ -592,10 +799,17 @@ int QMetaObject::indexOfMethod(const char *method) const int QMetaObject::indexOfSignal(const char *signal) const { const QMetaObject *m = this; - int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); - if (i < 0) { - m = this; - i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(signal, types); + i = QMetaObjectPrivate::indexOfSignalRelative(&m, name, types.size(), types.constData()); + } else { + i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); + if (i < 0) { + m = this; + i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); + } } if (i >= 0) i += m->methodOffset(); @@ -603,6 +817,7 @@ int QMetaObject::indexOfSignal(const char *signal) const } /*! \internal + \obsolete Same as QMetaObject::indexOfSignal, but the result is the local offset to the base object. \a baseObject will be adjusted to the enclosing QMetaObject, or 0 if the signal is not found @@ -618,12 +833,37 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, int conflict = m->d.superdata->indexOfMethod(signal); if (conflict >= 0) qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", - signal, m->d.superdata->d.stringdata, m->d.stringdata); + signal, rawStringData(m->d.superdata, 0), rawStringData(m, 0)); } #endif return i; } +/*! \internal + Same as QMetaObject::indexOfSignal, but the result is the local offset to the base object. + + \a baseObject will be adjusted to the enclosing QMetaObject, or 0 if the signal is not found +*/ +int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + int i = indexOfMethodRelative<MethodSignal>(baseObject, name, argc, types); +#ifndef QT_NO_DEBUG + const QMetaObject *m = *baseObject; + if (i >= 0 && m && m->d.superdata) { + int conflict = indexOfMethod(m->d.superdata, name, argc, types); + if (conflict >= 0) { + QMetaMethod conflictMethod = m->d.superdata->method(conflict); + qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", + conflictMethod.methodSignature().constData(), + rawStringData(m->d.superdata, 0), rawStringData(m, 0)); + } + } + #endif + return i; +} + /*! Finds \a slot and returns its index; otherwise returns -1. @@ -635,9 +875,16 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, int QMetaObject::indexOfSlot(const char *slot) const { const QMetaObject *m = this; - int i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, false); - if (i < 0) - i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(slot, types); + i = QMetaObjectPrivate::indexOfSlotRelative(&m, name, types.size(), types.constData()); + } else { + i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, false); + if (i < 0) + i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, true); + } if (i >= 0) i += m->methodOffset(); return i; @@ -651,10 +898,108 @@ int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m, return indexOfMethodRelative<MethodSlot>(m, slot, normalizeStringData); } +// same as indexOfSignalRelative but for slots. +int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + return indexOfMethodRelative<MethodSlot>(m, name, argc, types); +} + +int QMetaObjectPrivate::indexOfSignal(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfSignalRelative(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfSlot(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfSlotRelative(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfMethod(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfMethodRelative<0>(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfConstructor(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + for (int i = priv(m->d.data)->constructorCount-1; i >= 0; --i) { + int handle = priv(m->d.data)->constructorData + 5*i; + if (methodMatch(m, handle, name, argc, types)) + return i; + } + return -1; +} + +/*! + \internal + + Returns true if the \a signalTypes and \a methodTypes are + compatible; otherwise returns false. +*/ +bool QMetaObjectPrivate::checkConnectArgs(int signalArgc, const QArgumentType *signalTypes, + int methodArgc, const QArgumentType *methodTypes) +{ + if (signalArgc < methodArgc) + return false; + for (int i = 0; i < methodArgc; ++i) { + if (signalTypes[i] != methodTypes[i]) + return false; + } + return true; +} + +/*! + \internal + + Returns true if the \a signal and \a method arguments are + compatible; otherwise returns false. +*/ +bool QMetaObjectPrivate::checkConnectArgs(const QMetaMethodPrivate *signal, + const QMetaMethodPrivate *method) +{ + if (signal->methodType() != QMetaMethod::Signal) + return false; + if (signal->parameterCount() < method->parameterCount()) + return false; + const QMetaObject *smeta = signal->enclosingMetaObject(); + const QMetaObject *rmeta = method->enclosingMetaObject(); + for (int i = 0; i < method->parameterCount(); ++i) { + uint sourceTypeInfo = signal->parameterTypeInfo(i); + uint targetTypeInfo = method->parameterTypeInfo(i); + if ((sourceTypeInfo & IsUnresolvedType) + || (targetTypeInfo & IsUnresolvedType)) { + QByteArray sourceName = typeNameFromTypeInfo(smeta, sourceTypeInfo); + QByteArray targetName = typeNameFromTypeInfo(rmeta, targetTypeInfo); + if (sourceName != targetName) + return false; + } else { + int sourceType = typeFromTypeInfo(smeta, sourceTypeInfo); + int targetType = typeFromTypeInfo(rmeta, targetTypeInfo); + if (sourceType != targetType) + return false; + } + } + return true; +} + static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, const char *name) { while (self) { - if (strcmp(self->d.stringdata, name) == 0) + if (strcmp(rawStringData(self, 0), name) == 0) return self; if (self->d.extradata) { const QMetaObject **e; @@ -690,7 +1035,7 @@ int QMetaObject::indexOfEnumerator(const char *name) const while (m) { const QMetaObjectPrivate *d = priv(m->d.data); for (int i = d->enumeratorCount - 1; i >= 0; --i) { - const char *prop = m->d.stringdata + m->d.data[d->enumeratorData + 4*i]; + const char *prop = rawStringData(m, m->d.data[d->enumeratorData + 4*i]); if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) { i += m->enumeratorOffset(); return i; @@ -713,7 +1058,7 @@ int QMetaObject::indexOfProperty(const char *name) const while (m) { const QMetaObjectPrivate *d = priv(m->d.data); for (int i = d->propertyCount-1; i >= 0; --i) { - const char *prop = m->d.stringdata + m->d.data[d->propertyData + 3*i]; + const char *prop = rawStringData(m, m->d.data[d->propertyData + 3*i]); if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) { i += m->propertyOffset(); return i; @@ -744,8 +1089,7 @@ int QMetaObject::indexOfClassInfo(const char *name) const const QMetaObject *m = this; while (m && i < 0) { for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i) - if (strcmp(name, m->d.stringdata - + m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) { + if (strcmp(name, rawStringData(m, m->d.data[priv(m->d.data)->classInfoData + 2*i])) == 0) { i += m->classInfoOffset(); break; } @@ -829,16 +1173,20 @@ QMetaProperty QMetaObject::property(int index) const if (i >= 0 && i < priv(d.data)->propertyCount) { int handle = priv(d.data)->propertyData + 3*i; int flags = d.data[handle + 2]; - const char *type = d.stringdata + d.data[handle + 1]; result.mobj = this; result.handle = handle; result.idx = i; if (flags & EnumOrFlag) { + const char *type; + if (priv(d.data)->revision >= 7) + type = rawTypeNameFromTypeInfo(this, d.data[handle + 1]); + else + type = legacyString(this, d.data[handle + 1]); result.menum = enumerator(indexOfEnumerator(type)); if (!result.menum.isValid()) { const char *enum_name = type; - const char *scope_name = d.stringdata; + const char *scope_name = rawStringData(this, 0); char *scope_buffer = 0; const char *colon = strrchr(enum_name, ':'); @@ -934,6 +1282,21 @@ bool QMetaObject::checkConnectArgs(const char *signal, const char *method) return false; } +/*! + \since 5.0 + \overload + + Returns true if the \a signal and \a method arguments are + compatible; otherwise returns false. +*/ +bool QMetaObject::checkConnectArgs(const QMetaMethod &signal, + const QMetaMethod &method) +{ + return QMetaObjectPrivate::checkConnectArgs( + QMetaMethodPrivate::get(&signal), + QMetaMethodPrivate::get(&method)); +} + static void qRemoveWhitespace(const char *s, char *d) { char last = 0; @@ -1230,7 +1593,7 @@ bool QMetaObject::invokeMethod(QObject *obj, \ingroup objectmodel - A QMetaMethod has a methodType(), a signature(), a list of + A QMetaMethod has a methodType(), a methodSignature(), a list of parameterTypes() and parameterNames(), a return typeName(), a tag(), and an access() specifier. You can use invoke() to invoke the method on an arbitrary QObject. @@ -1275,72 +1638,284 @@ bool QMetaObject::invokeMethod(QObject *obj, \internal */ +QByteArray QMetaMethodPrivate::signature() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QByteArray result; + result.reserve(256); + result += name(); + result += '('; + QList<QByteArray> argTypes = parameterTypes(); + for (int i = 0; i < argTypes.size(); ++i) { + if (i) + result += ','; + result += argTypes.at(i); + } + result += ')'; + return result; +} + +QByteArray QMetaMethodPrivate::name() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return toByteArray(stringData(mobj, mobj->d.data[handle])); +} + +int QMetaMethodPrivate::typesDataIndex() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[handle + 2]; +} + +const char *QMetaMethodPrivate::rawReturnTypeName() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + uint typeInfo = mobj->d.data[typesDataIndex()]; + if (typeInfo & IsUnresolvedType) + return rawStringData(mobj, typeInfo & TypeNameIndexMask); + else { + if (typeInfo == QMetaType::Void) { + // QMetaMethod::typeName() is documented to return an empty string + // if the return type is void, but QMetaType::typeName() returns + // "void". + return ""; + } + return QMetaType::typeName(typeInfo); + } +} + +int QMetaMethodPrivate::returnType() const +{ + return parameterType(-1); +} + +int QMetaMethodPrivate::parameterCount() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[handle + 1]; +} + +int QMetaMethodPrivate::parametersDataIndex() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return typesDataIndex() + 1; +} + +uint QMetaMethodPrivate::parameterTypeInfo(int index) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[parametersDataIndex() + index]; +} + +int QMetaMethodPrivate::parameterType(int index) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return typeFromTypeInfo(mobj, parameterTypeInfo(index)); +} + +void QMetaMethodPrivate::getParameterTypes(int *types) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + int dataIndex = parametersDataIndex(); + int argc = parameterCount(); + for (int i = 0; i < argc; ++i) { + int id = typeFromTypeInfo(mobj, mobj->d.data[dataIndex++]); + *(types++) = id; + } +} + +QList<QByteArray> QMetaMethodPrivate::parameterTypes() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QList<QByteArray> list; + int argc = parameterCount(); + int paramsIndex = parametersDataIndex(); + for (int i = 0; i < argc; ++i) + list += typeNameFromTypeInfo(mobj, mobj->d.data[paramsIndex + i]); + return list; +} + +QList<QByteArray> QMetaMethodPrivate::parameterNames() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QList<QByteArray> list; + int argc = parameterCount(); + int namesIndex = parametersDataIndex() + argc; + for (int i = 0; i < argc; ++i) + list += toByteArray(stringData(mobj, mobj->d.data[namesIndex + i])); + return list; +} + +QByteArray QMetaMethodPrivate::tag() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return toByteArray(stringData(mobj, mobj->d.data[handle + 3])); +} + /*! + \since 5.0 + Returns the signature of this method (e.g., \c{setValue(double)}). \sa parameterTypes(), parameterNames() */ -const char *QMetaMethod::signature() const +QByteArray QMetaMethod::methodSignature() const +{ + if (!mobj) + return QByteArray(); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->signature(); + } else { + const char *sig = rawStringData(mobj, mobj->d.data[handle]); + return QByteArray::fromRawData(sig, qstrlen(sig)); + } +} + +/*! + \since 5.0 + + Returns the name of this method. + + \sa methodSignature(), parameterCount() +*/ +QByteArray QMetaMethod::name() const +{ + if (!mobj) + return QByteArray(); + return QMetaMethodPrivate::get(this)->name(); +} + +/*! + \since 5.0 + + Returns the return type of this method. + + The return value is one of the types that are registered + with QMetaType, or QMetaType::UnknownType if the type is not registered. + + \sa parameterType(), QMetaType, typeName() +*/ +int QMetaMethod::returnType() const + { + if (!mobj) + return QMetaType::UnknownType; + return QMetaMethodPrivate::get(this)->returnType(); +} + +/*! + \since 5.0 + + Returns the number of parameters of this method. + + \sa parameterType(), parameterNames() +*/ +int QMetaMethod::parameterCount() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return QMetaMethodPrivate::get(this)->parameterCount(); +} + +/*! + \since 5.0 + + Returns the type of the parameter at the given \a index. + + The return value is one of the types that are registered + with QMetaType, or QMetaType::UnknownType if the type is not registered. + + \sa parameterCount(), returnType(), QMetaType +*/ +int QMetaMethod::parameterType(int index) const +{ + if (!mobj || index < 0) + return QMetaType::UnknownType; + if (index >= QMetaMethodPrivate::get(this)->parameterCount()) + return QMetaType::UnknownType; + return QMetaMethodPrivate::get(this)->parameterType(index); +} + +/*! + \since 5.0 + \internal + + Gets the parameter \a types of this method. The storage + for \a types must be able to hold parameterCount() items. + + \sa parameterCount(), returnType(), parameterType() +*/ +void QMetaMethod::getParameterTypes(int *types) const +{ + if (!mobj) + return; + QMetaMethodPrivate::get(this)->getParameterTypes(types); } /*! Returns a list of parameter types. - \sa parameterNames(), signature() + \sa parameterNames(), methodSignature() */ QList<QByteArray> QMetaMethod::parameterTypes() const { if (!mobj) return QList<QByteArray>(); - return QMetaObjectPrivate::parameterTypeNamesFromSignature( - mobj->d.stringdata + mobj->d.data[handle]); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->parameterTypes(); + } else { + return QMetaObjectPrivate::parameterTypeNamesFromSignature( + legacyString(mobj, mobj->d.data[handle])); + } } /*! Returns a list of parameter names. - \sa parameterTypes(), signature() + \sa parameterTypes(), methodSignature() */ QList<QByteArray> QMetaMethod::parameterNames() const { QList<QByteArray> list; if (!mobj) return list; - const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; - if (*names == 0) { - // do we have one or zero arguments? - const char *signature = mobj->d.stringdata + mobj->d.data[handle]; - while (*signature && *signature != '(') - ++signature; - if (*++signature != ')') - list += QByteArray(); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->parameterNames(); } else { - --names; - do { - const char *begin = ++names; - while (*names && *names != ',') - ++names; - list += QByteArray(begin, names - begin); - } while (*names); + const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); + if (*names == 0) { + // do we have one or zero arguments? + const char *signature = rawStringData(mobj, mobj->d.data[handle]); + while (*signature && *signature != '(') + ++signature; + if (*++signature != ')') + list += QByteArray(); + } else { + --names; + do { + const char *begin = ++names; + while (*names && *names != ',') + ++names; + list += QByteArray(begin, names - begin); + } while (*names); + } + return list; } - return list; } /*! - Returns the return type of this method, or an empty string if the + Returns the return type name of this method, or an empty string if the return type is \e void. */ const char *QMetaMethod::typeName() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 2]; + if (priv(mobj->d.data)->revision >= 7) + return QMetaMethodPrivate::get(this)->rawReturnTypeName(); + else + return legacyString(mobj, mobj->d.data[handle + 2]); } /*! @@ -1377,7 +1952,10 @@ const char *QMetaMethod::tag() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 3]; + if (priv(mobj->d.data)->revision >= 7) + return QMetaMethodPrivate::get(this)->tag().constData(); + else + return legacyString(mobj, mobj->d.data[handle + 3]); } @@ -1578,12 +2156,14 @@ bool QMetaMethod::invoke(QObject *object, break; } int metaMethodArgumentCount = 0; - { + if (priv(mobj->d.data)->revision >= 7) { + metaMethodArgumentCount = QMetaMethodPrivate::get(this)->parameterCount(); + } else { // based on QMetaObject::parameterNames() - const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); if (*names == 0) { // do we have one or zero arguments? - const char *signature = mobj->d.stringdata + mobj->d.data[handle]; + const char *signature = rawStringData(mobj, mobj->d.data[handle]); while (*signature && *signature != '(') ++signature; if (*++signature != ')') @@ -1661,7 +2241,7 @@ bool QMetaMethod::invoke(QObject *object, for (int i = 1; i < paramCount; ++i) { types[i] = QMetaType::type(typeNames[i]); - if (types[i]) { + if (types[i] != QMetaType::UnknownType) { args[i] = QMetaType::create(types[i], param[i]); ++nargs; } else if (param[i]) { @@ -1803,7 +2383,7 @@ const char *QMetaEnum::name() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -1831,7 +2411,7 @@ const char *QMetaEnum::key(int index) const int count = mobj->d.data[handle + 2]; int data = mobj->d.data[handle + 3]; if (index >= 0 && index < count) - return mobj->d.stringdata + mobj->d.data[data + 2*index]; + return rawStringData(mobj, mobj->d.data[data + 2*index]); return 0; } @@ -1878,7 +2458,7 @@ bool QMetaEnum::isFlag() const */ const char *QMetaEnum::scope() const { - return mobj?mobj->d.stringdata : 0; + return mobj?rawStringData(mobj, 0) : 0; } /*! @@ -1910,8 +2490,8 @@ int QMetaEnum::keyToValue(const char *key, bool *ok) const int count = mobj->d.data[handle + 2]; int data = mobj->d.data[handle + 3]; for (int i = 0; i < count; ++i) { - if ((!scope || (qstrlen(mobj->d.stringdata) == scope && strncmp(qualified_key, mobj->d.stringdata, scope) == 0)) - && strcmp(key, mobj->d.stringdata + mobj->d.data[data + 2*i]) == 0) { + if ((!scope || (stringSize(mobj, 0) == int(scope) && strncmp(qualified_key, rawStringData(mobj, 0), scope) == 0)) + && strcmp(key, rawStringData(mobj, mobj->d.data[data + 2*i])) == 0) { if (ok != 0) *ok = true; return mobj->d.data[data + 2*i + 1]; @@ -1936,7 +2516,7 @@ const char* QMetaEnum::valueToKey(int value) const int data = mobj->d.data[handle + 3]; for (int i = 0; i < count; ++i) if (value == (int)mobj->d.data[data + 2*i + 1]) - return mobj->d.stringdata + mobj->d.data[data + 2*i]; + return rawStringData(mobj, mobj->d.data[data + 2*i]); return 0; } @@ -1979,8 +2559,8 @@ int QMetaEnum::keysToValue(const char *keys, bool *ok) const } int i; for (i = count-1; i >= 0; --i) - if ((!scope || (qstrlen(mobj->d.stringdata) == scope && strncmp(qualified_key.constData(), mobj->d.stringdata, scope) == 0)) - && strcmp(key, mobj->d.stringdata + mobj->d.data[data + 2*i]) == 0) { + if ((!scope || (stringSize(mobj, 0) == int(scope) && strncmp(qualified_key.constData(), rawStringData(mobj, 0), scope) == 0)) + && strcmp(key, rawStringData(mobj, mobj->d.data[data + 2*i])) == 0) { value |= mobj->d.data[data + 2*i + 1]; break; } @@ -2013,7 +2593,10 @@ QByteArray QMetaEnum::valueToKeys(int value) const v = v & ~k; if (!keys.isEmpty()) keys += '|'; - keys += mobj->d.stringdata + mobj->d.data[data + 2*i]; + if (priv(mobj->d.data)->revision >= 7) + keys += toByteArray(stringData(mobj, mobj->d.data[data + 2*i])); + else + keys += legacyString(mobj, mobj->d.data[data + 2*i]); } } return keys; @@ -2092,7 +2675,7 @@ const char *QMetaProperty::name() const if (!mobj) return 0; int handle = priv(mobj->d.data)->propertyData + 3*idx; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -2105,7 +2688,10 @@ const char *QMetaProperty::typeName() const if (!mobj) return 0; int handle = priv(mobj->d.data)->propertyData + 3*idx; - return mobj->d.stringdata + mobj->d.data[handle + 1]; + if (priv(mobj->d.data)->revision >= 7) + return rawTypeNameFromTypeInfo(mobj, mobj->d.data[handle + 1]); + else + return legacyString(mobj, mobj->d.data[handle + 1]); } /*! @@ -2119,14 +2705,21 @@ QVariant::Type QMetaProperty::type() const if (!mobj) return QVariant::Invalid; int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - uint type = flags >> 24; - if (type) + uint type; + if (priv(mobj->d.data)->revision >= 7) { + type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]); + if (type >= QMetaType::User) + return QVariant::UserType; + } else { + uint flags = mobj->d.data[handle + 2]; + type = flags >> 24; + } + if (type != QMetaType::UnknownType) return QVariant::Type(type); if (isEnumType()) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if (enumMetaTypeId == 0) + if (enumMetaTypeId == QMetaType::UnknownType) return QVariant::Int; } #ifdef QT_COORD_TYPE @@ -2142,18 +2735,29 @@ QVariant::Type QMetaProperty::type() const \since 4.2 Returns this property's user type. The return value is one - of the values that are registered with QMetaType, or 0 if + of the values that are registered with QMetaType, or QMetaType::UnknownType if the type is not registered. \sa type(), QMetaType, typeName() */ int QMetaProperty::userType() const { - QVariant::Type tp = type(); - if (tp != QVariant::UserType) - return tp; + if (!mobj) + return QMetaType::UnknownType; + if (priv(mobj->d.data)->revision >= 7) { + int handle = priv(mobj->d.data)->propertyData + 3*idx; + int type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]); + if (type != QMetaType::UnknownType) + return type; + } else { + QVariant::Type tp = type(); + if (tp != QVariant::UserType) + return tp; + } if (isEnumType()) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); + if (enumMetaTypeId == QMetaType::UnknownType) + return QVariant::Int; // Match behavior of QMetaType::type() return enumMetaTypeId; } return QMetaType::type(typeName()); @@ -2249,18 +2853,30 @@ QVariant QMetaProperty::read(const QObject *object) const with QMetaType) */ int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if (enumMetaTypeId != 0) + if (enumMetaTypeId != QMetaType::UnknownType) t = enumMetaTypeId; } else { int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - const char *typeName = mobj->d.stringdata + mobj->d.data[handle + 1]; - t = (flags >> 24); - if (t == QVariant::Invalid) - t = QMetaType::type(typeName); - if (t == QVariant::Invalid) - t = QVariant::nameToType(typeName); - if (t == QVariant::Invalid) { + const char *typeName = 0; + if (priv(mobj->d.data)->revision >= 7) { + uint typeInfo = mobj->d.data[handle + 1]; + if (!(typeInfo & IsUnresolvedType)) + t = typeInfo; + else { + typeName = rawStringData(mobj, typeInfo & TypeNameIndexMask); + t = QMetaType::type(typeName); + } + } else { + uint flags = mobj->d.data[handle + 2]; + t = (flags >> 24); + if (t == QMetaType::UnknownType) { + typeName = legacyString(mobj, mobj->d.data[handle + 1]); + t = QMetaType::type(typeName); + if (t == QMetaType::UnknownType) + t = QVariant::nameToType(typeName); + } + } + if (t == QMetaType::UnknownType) { qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property '%s::%s'", typeName, mobj->className(), name()); return QVariant(); } @@ -2315,17 +2931,29 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const return false; } else if (v.type() != QVariant::Int && v.type() != QVariant::UInt) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if ((enumMetaTypeId == 0) || (v.userType() != enumMetaTypeId) || !v.constData()) + if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData()) return false; v = QVariant(*reinterpret_cast<const int *>(v.constData())); } v.convert(QVariant::Int); } else { int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - t = flags >> 24; - if (t == QVariant::Invalid) { - const char *typeName = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *typeName = 0; + if (priv(mobj->d.data)->revision >= 7) { + uint typeInfo = mobj->d.data[handle + 1]; + if (!(typeInfo & IsUnresolvedType)) + t = typeInfo; + else { + typeName = rawStringData(mobj, typeInfo & TypeNameIndexMask); + t = QMetaType::type(typeName); + } + } else { + uint flags = mobj->d.data[handle + 2]; + t = flags >> 24; + typeName = legacyString(mobj, mobj->d.data[handle + 1]); + } + if (t == QMetaType::UnknownType) { + const char *typeName = rawStringData(mobj, mobj->d.data[handle + 1]); const char *vtypeName = value.typeName(); if (vtypeName && strcmp(typeName, vtypeName) == 0) t = value.userType(); @@ -2691,7 +3319,7 @@ const char *QMetaClassInfo::name() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -2703,7 +3331,7 @@ const char* QMetaClassInfo::value() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 1]; + return rawStringData(mobj, mobj->d.data[handle + 1]); } /*! diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 9e51af7556..095b196dca 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -57,8 +57,13 @@ class Q_CORE_EXPORT QMetaMethod public: inline QMetaMethod() : mobj(0),handle(0) {} - const char *signature() const; + QByteArray methodSignature() const; + QByteArray name() const; const char *typeName() const; + int returnType() const; + int parameterCount() const; + int parameterType(int index) const; + void getParameterTypes(int *types) const; QList<QByteArray> parameterTypes() const; QList<QByteArray> parameterNames() const; const char *tag() const; @@ -137,8 +142,16 @@ public: inline bool isValid() const { return mobj != 0; } private: +#if QT_DEPRECATED_SINCE(5,0) + // signature() has been renamed to methodSignature() in Qt 5. + // Warning, that function returns a QByteArray; check the life time if + // you convert to char*. + char *signature(struct renamedInQt5_warning_checkTheLifeTime * = 0) Q_DECL_EQ_DELETE; +#endif + const QMetaObject *mobj; uint handle; + friend class QMetaMethodPrivate; friend struct QMetaObject; friend struct QMetaObjectPrivate; friend class QObject; diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 59a5c5f280..ff8dccc4dd 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -105,11 +105,64 @@ enum MetaObjectFlags { RequiresVariantMetaObject = 0x02 }; +enum MetaDataFlags { + IsUnresolvedType = 0x80000000, + TypeNameIndexMask = 0x7FFFFFFF +}; + +class QArgumentType +{ +public: + QArgumentType(int type) + : _type(type) + {} + QArgumentType(const QByteArray &name) + : _type(QMetaType::type(name.constData())), _name(name) + {} + QArgumentType() + : _type(0) + {} + int type() const + { return _type; } + QByteArray name() const + { + if (_type && _name.isEmpty()) + const_cast<QArgumentType *>(this)->_name = QMetaType::typeName(_type); + return _name; + } + bool operator==(const QArgumentType &other) const + { + if (_type) + return _type == other._type; + else if (other._type) + return false; + else + return _name == other._name; + } + bool operator!=(const QArgumentType &other) const + { + if (_type) + return _type != other._type; + else if (other._type) + return true; + else + return _name != other._name; + } + +private: + int _type; + QByteArray _name; +}; + +template <class T, int> class QVarLengthArray; +typedef QVarLengthArray<QArgumentType, 10> QArgumentTypeArray; + +class QMetaMethodPrivate; class QMutex; struct QMetaObjectPrivate { - enum { OutputRevision = 6 }; // Used by moc and qmetaobjectbuilder + enum { OutputRevision = 7 }; // Used by moc, qmetaobjectbuilder and qdbus int revision; int className; @@ -122,6 +175,7 @@ struct QMetaObjectPrivate int signalCount; //since revision 4 // revision 5 introduces changes in normalized signatures, no new members // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself + // revision 7 is Qt 5 static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); } @@ -134,6 +188,27 @@ struct QMetaObjectPrivate bool normalizeStringData); static int originalClone(const QMetaObject *obj, int local_method_index); + static QByteArray decodeMethodSignature(const char *signature, + QArgumentTypeArray &types); + static int indexOfSignalRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types); + static int indexOfSlotRelative(const QMetaObject **m, + const QByteArray &name, int argc, + const QArgumentType *types); + static int indexOfSignal(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfSlot(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfMethod(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfConstructor(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static bool checkConnectArgs(int signalArgc, const QArgumentType *signalTypes, + int methodArgc, const QArgumentType *methodTypes); + static bool checkConnectArgs(const QMetaMethodPrivate *signal, + const QMetaMethodPrivate *method); + static QList<QByteArray> parameterTypeNamesFromSignature(const char *signature); #ifndef QT_NO_QOBJECT diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 8bece6636b..13cd1a684a 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -78,26 +78,17 @@ QT_BEGIN_NAMESPACE */ // copied from moc's generator.cpp -uint qvariant_nameToType(const char* name) +bool isBuiltinType(const QByteArray &type) { - if (!name) - return 0; - - uint tp = QMetaType::type(name); - return tp < QMetaType::User ? tp : 0; -} - -/* - Returns true if the type is a QVariant types. -*/ -bool isVariantType(const char* type) -{ - return qvariant_nameToType(type) != 0; + int id = QMetaType::type(type); + if (!id && !type.isEmpty() && type != "void") + return false; + return (id < QMetaType::User); } +// copied from qmetaobject.cpp static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); } -// end of copied lines from qmetaobject.cpp class QMetaMethodBuilderPrivate { @@ -136,6 +127,21 @@ public: { attributes = ((attributes & ~AccessMask) | (int)value); } + + QList<QByteArray> parameterTypes() const + { + return QMetaObjectPrivate::parameterTypeNamesFromSignature(signature); + } + + int parameterCount() const + { + return parameterTypes().size(); + } + + QByteArray name() const + { + return signature.left(qMax(signature.indexOf('('), 0)); + } }; class QMetaPropertyBuilderPrivate @@ -458,13 +464,13 @@ QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QMetaMethod& prototype) { QMetaMethodBuilder method; if (prototype.methodType() == QMetaMethod::Method) - method = addMethod(prototype.signature()); + method = addMethod(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Signal) - method = addSignal(prototype.signature()); + method = addSignal(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Slot) - method = addSlot(prototype.signature()); + method = addSlot(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Constructor) - method = addConstructor(prototype.signature()); + method = addConstructor(prototype.methodSignature()); method.setReturnType(prototype.typeName()); method.setParameterNames(prototype.parameterNames()); method.setTag(prototype.tag()); @@ -535,7 +541,7 @@ QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QByteArray& signatur QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QMetaMethod& prototype) { Q_ASSERT(prototype.methodType() == QMetaMethod::Constructor); - QMetaMethodBuilder ctor = addConstructor(prototype.signature()); + QMetaMethodBuilder ctor = addConstructor(prototype.methodSignature()); ctor.setReturnType(prototype.typeName()); ctor.setParameterNames(prototype.parameterNames()); ctor.setTag(prototype.tag()); @@ -588,7 +594,7 @@ QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty& protot if (prototype.hasNotifySignal()) { // Find an existing method for the notify signal, or add a new one. QMetaMethod method = prototype.notifySignal(); - int index = indexOfMethod(method.signature()); + int index = indexOfMethod(method.methodSignature()); if (index == -1) index = addMethod(method).index(); d->properties[property._index].notifySignal = index; @@ -1070,75 +1076,82 @@ int QMetaObjectBuilder::indexOfClassInfo(const QByteArray& name) #define ALIGN(size,type) \ (size) = ((size) + sizeof(type) - 1) & ~(sizeof(type) - 1) -class MetaStringTable +/*! + \class QMetaStringTable + \internal + \brief The QMetaStringTable class can generate a meta-object string table at runtime. +*/ + +QMetaStringTable::QMetaStringTable() + : m_index(0) {} + +// Enters the given value into the string table (if it hasn't already been +// entered). Returns the index of the string. +int QMetaStringTable::enter(const QByteArray &value) { -public: - typedef QHash<QByteArray, int> Entries; // string --> offset mapping - typedef Entries::const_iterator const_iterator; - Entries::const_iterator constBegin() const - { return m_entries.constBegin(); } - Entries::const_iterator constEnd() const - { return m_entries.constEnd(); } + Entries::iterator it = m_entries.find(value); + if (it != m_entries.end()) + return it.value(); + int pos = m_index; + m_entries.insert(value, pos); + ++m_index; + return pos; +} - MetaStringTable() : m_offset(0) {} +int QMetaStringTable::preferredAlignment() +{ +#ifdef Q_ALIGNOF + return Q_ALIGNOF(QByteArrayData); +#else + return sizeof(void *); +#endif +} - int enter(const QByteArray &value) - { - Entries::iterator it = m_entries.find(value); - if (it != m_entries.end()) - return it.value(); - int pos = m_offset; - m_entries.insert(value, pos); - m_offset += value.size() + 1; - return pos; - } +// Returns the size (in bytes) required for serializing this string table. +int QMetaStringTable::blobSize() const +{ + int size = m_entries.size() * sizeof(QByteArrayData); + Entries::const_iterator it; + for (it = m_entries.constBegin(); it != m_entries.constEnd(); ++it) + size += it.key().size() + 1; + return size; +} - int arraySize() const { return m_offset; } +// 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) +{ + Q_ASSERT(!(reinterpret_cast<quintptr>(out) & (preferredAlignment()-1))); -private: - Entries m_entries; - int m_offset; -}; + 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_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset }; -// Build the parameter array string for a method. -static QByteArray buildParameterNames - (const QByteArray& signature, const QList<QByteArray>& parameterNames) -{ - // If the parameter name list is specified, then concatenate them. - if (!parameterNames.isEmpty()) { - QByteArray names; - bool first = true; - foreach (const QByteArray &name, parameterNames) { - if (first) - first = false; - else - names += (char)','; - names += name; - } - return names; - } + memcpy(out + i * sizeof(QByteArrayData), &data, sizeof(QByteArrayData)); - // Count commas in the signature, excluding those inside template arguments. - int index = signature.indexOf('('); - if (index < 0) - return QByteArray(); - ++index; - if (index >= signature.size()) - return QByteArray(); - if (signature[index] == ')') - return QByteArray(); - int count = 1; - int brackets = 0; - while (index < signature.size() && signature[index] != ',') { - char ch = signature[index++]; - if (ch == '<') - ++brackets; - else if (ch == '>') - --brackets; - else if (ch == ',' && brackets <= 0) - ++count; + memcpy(out + offsetOfStringdataMember + stringdataOffset, str.constData(), size); + out[offsetOfStringdataMember + stringdataOffset + size] = '\0'; + + stringdataOffset += size + 1; } - return QByteArray(count - 1, ','); +} + +// Returns the sum of all parameters (including return type) for the given +// \a methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +static int aggregateParameterCount(const QList<QMetaMethodBuilderPrivate> &methods) +{ + int sum = 0; + for (int i = 0; i < methods.size(); ++i) + sum += methods.at(i).parameterCount() + 1; // +1 for return type + return sum; } // Build a QMetaObject in "buf" based on the information in "d". @@ -1151,6 +1164,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_UNUSED(expectedSize); // Avoid warning in release mode int size = 0; int dataIndex; + int paramsIndex; int enumIndex; int index; bool hasRevisionedMethods = d->hasRevisionedMethods(); @@ -1181,8 +1195,13 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, break; } } + int methodParametersDataSize = + ((aggregateParameterCount(d->methods) + + aggregateParameterCount(d->constructors)) * 2) // types and parameter names + - d->methods.size() // return "parameters" don't have names + - d->constructors.size(); // "this" parameters don't have names if (buf) { - Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 6, "QMetaObjectBuilder should generate the same version as moc"); + Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 7, "QMetaObjectBuilder should generate the same version as moc"); pmeta->revision = QMetaObjectPrivate::OutputRevision; pmeta->flags = d->flags; pmeta->className = 0; // Class name is always the first string. @@ -1197,6 +1216,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, dataIndex += 5 * d->methods.size(); if (hasRevisionedMethods) dataIndex += d->methods.size(); + paramsIndex = dataIndex; + dataIndex += methodParametersDataSize; pmeta->propertyCount = d->properties.size(); pmeta->propertyData = dataIndex; @@ -1218,6 +1239,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, dataIndex += 5 * d->methods.size(); if (hasRevisionedMethods) dataIndex += d->methods.size(); + paramsIndex = dataIndex; + dataIndex += methodParametersDataSize; dataIndex += 3 * d->properties.size(); if (hasNotifySignals) dataIndex += d->properties.size(); @@ -1240,13 +1263,14 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Find the start of the data and string tables. int *data = reinterpret_cast<int *>(pmeta); size += dataIndex * sizeof(int); + ALIGN(size, void *); char *str = reinterpret_cast<char *>(buf + size); if (buf) { if (relocatable) { - meta->d.stringdata = reinterpret_cast<const char *>((quintptr)size); + meta->d.stringdata = reinterpret_cast<const QByteArrayData *>((quintptr)size); meta->d.data = reinterpret_cast<uint *>((quintptr)pmetaSize); } else { - meta->d.stringdata = str; + meta->d.stringdata = reinterpret_cast<const QByteArrayData *>(str); meta->d.data = reinterpret_cast<uint *>(data); } } @@ -1254,7 +1278,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Reset the current data position to just past the QMetaObjectPrivate. dataIndex = MetaObjectPrivateFieldCount; - MetaStringTable strings; + QMetaStringTable strings; strings.enter(d->className); // Output the class infos, @@ -1273,24 +1297,21 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_ASSERT(!buf || dataIndex == pmeta->methodData); for (index = 0; index < d->methods.size(); ++index) { QMetaMethodBuilderPrivate *method = &(d->methods[index]); - int sig = strings.enter(method->signature); - int params; - QByteArray names = buildParameterNames - (method->signature, method->parameterNames); - params = strings.enter(names); - int ret = strings.enter(method->returnType); + int name = strings.enter(method->name()); + int argc = method->parameterCount(); int tag = strings.enter(method->tag); int attrs = method->attributes; if (buf) { - data[dataIndex] = sig; - data[dataIndex + 1] = params; - data[dataIndex + 2] = ret; + data[dataIndex] = name; + data[dataIndex + 1] = argc; + data[dataIndex + 2] = paramsIndex; data[dataIndex + 3] = tag; data[dataIndex + 4] = attrs; if (method->methodType() == QMetaMethod::Signal) pmeta->signalCount++; } dataIndex += 5; + paramsIndex += 1 + argc * 2; } if (hasRevisionedMethods) { for (index = 0; index < d->methods.size(); ++index) { @@ -1301,23 +1322,59 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, } } + // Output the method parameters in the class. + Q_ASSERT(!buf || dataIndex == pmeta->methodData + d->methods.size() * 5 + + (hasRevisionedMethods ? d->methods.size() : 0)); + for (int x = 0; x < 2; ++x) { + QList<QMetaMethodBuilderPrivate> &methods = (x == 0) ? d->methods : d->constructors; + for (index = 0; index < methods.size(); ++index) { + QMetaMethodBuilderPrivate *method = &(methods[index]); + QList<QByteArray> paramTypeNames = method->parameterTypes(); + int paramCount = paramTypeNames.size(); + for (int i = -1; i < paramCount; ++i) { + const QByteArray &typeName = (i < 0) ? method->returnType : paramTypeNames.at(i); + int typeInfo; + if (isBuiltinType(typeName)) + typeInfo = QMetaType::type(typeName); + else + typeInfo = IsUnresolvedType | strings.enter(typeName); + if (buf) + data[dataIndex] = typeInfo; + ++dataIndex; + } + + QList<QByteArray> paramNames = method->parameterNames; + while (paramNames.size() < paramCount) + paramNames.append(QByteArray()); + for (int i = 0; i < paramCount; ++i) { + int stringIndex = strings.enter(paramNames.at(i)); + if (buf) + data[dataIndex] = stringIndex; + ++dataIndex; + } + } + } + // Output the properties in the class. Q_ASSERT(!buf || dataIndex == pmeta->propertyData); for (index = 0; index < d->properties.size(); ++index) { QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); int name = strings.enter(prop->name); - int type = strings.enter(prop->type); + + int typeInfo; + if (isBuiltinType(prop->type)) + typeInfo = QMetaType::type(prop->type); + else + typeInfo = IsUnresolvedType | strings.enter(prop->type); + int flags = prop->flags; - if (!isVariantType(prop->type)) { + if (!isBuiltinType(prop->type)) flags |= EnumOrFlag; - } else { - flags |= qvariant_nameToType(prop->type) << 24; - } if (buf) { data[dataIndex] = name; - data[dataIndex + 1] = type; + data[dataIndex + 1] = typeInfo; data[dataIndex + 2] = flags; } dataIndex += 3; @@ -1372,34 +1429,25 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_ASSERT(!buf || dataIndex == pmeta->constructorData); for (index = 0; index < d->constructors.size(); ++index) { QMetaMethodBuilderPrivate *method = &(d->constructors[index]); - int sig = strings.enter(method->signature); - int params; - QByteArray names = buildParameterNames - (method->signature, method->parameterNames); - params = strings.enter(names); - int ret = strings.enter(method->returnType); + int name = strings.enter(method->name()); + int argc = method->parameterCount(); int tag = strings.enter(method->tag); int attrs = method->attributes; if (buf) { - data[dataIndex] = sig; - data[dataIndex + 1] = params; - data[dataIndex + 2] = ret; + data[dataIndex] = name; + data[dataIndex + 1] = argc; + data[dataIndex + 2] = paramsIndex; data[dataIndex + 3] = tag; data[dataIndex + 4] = attrs; } dataIndex += 5; + paramsIndex += 1 + argc * 2; } - size += strings.arraySize(); + size += strings.blobSize(); - if (buf) { - // Write strings to string data array. - MetaStringTable::const_iterator it; - for (it = strings.constBegin(); it != strings.constEnd(); ++it) { - memcpy(str + it.value(), it.key().constData(), it.key().size()); - str[it.value() + it.key().size()] = '\0'; - } - } + if (buf) + strings.writeBlob(str); // Output the zero terminator in the data array. if (buf) @@ -1508,7 +1556,7 @@ void QMetaObjectBuilder::fromRelocatableData(QMetaObject *output, quintptr dataOffset = (quintptr)dataMo->d.data; output->d.superdata = superclass; - output->d.stringdata = buf + stringdataOffset; + output->d.stringdata = reinterpret_cast<const QByteArrayData *>(buf + stringdataOffset); output->d.data = reinterpret_cast<const uint *>(buf + dataOffset); output->d.extradata = 0; } @@ -1896,7 +1944,7 @@ QByteArray QMetaMethodBuilder::returnType() const is empty, then the method's return type is \c{void}. The \a value will be normalized before it is added to the method. - \sa returnType(), signature() + \sa returnType(), parameterTypes(), signature() */ void QMetaMethodBuilder::setReturnType(const QByteArray& value) { @@ -1906,6 +1954,20 @@ void QMetaMethodBuilder::setReturnType(const QByteArray& value) } /*! + Returns the list of parameter types for this method. + + \sa returnType(), parameterNames() +*/ +QList<QByteArray> QMetaMethodBuilder::parameterTypes() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->parameterTypes(); + else + return QList<QByteArray>(); +} + +/*! Returns the list of parameter names for this method. \sa setParameterNames() diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index 86bc354164..4d766a9197 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -56,6 +56,7 @@ #include <QtCore/qobject.h> #include <QtCore/qmetaobject.h> #include <QtCore/qdatastream.h> +#include <QtCore/qhash.h> #include <QtCore/qmap.h> @@ -203,6 +204,7 @@ public: QByteArray returnType() const; void setReturnType(const QByteArray& value); + QList<QByteArray> parameterTypes() const; QList<QByteArray> parameterNames() const; void setParameterNames(const QList<QByteArray>& value); @@ -318,6 +320,23 @@ private: QMetaEnumBuilderPrivate *d_func() const; }; +class Q_CORE_EXPORT QMetaStringTable +{ +public: + QMetaStringTable(); + + int enter(const QByteArray &value); + + static int preferredAlignment(); + int blobSize() const; + void writeBlob(char *out); + +private: + typedef QHash<QByteArray, int> Entries; // string --> index mapping + Entries m_entries; + int m_index; +}; + Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::AddMembers) Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::MetaObjectFlags) diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 410a5cc712..a5dc179113 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -242,6 +242,7 @@ template<> struct TypeDefinition<QRegExp> { static const bool IsAvailable = fals \value QEasingCurve QEasingCurve \value User Base value for user types + \value UnknownType This is an invalid type id. It is returned from QMetaType for types that are not registered \omitvalue FirstGuiType \omitvalue FirstWidgetsType @@ -311,7 +312,7 @@ static const struct { const char * typeName; int typeNameLength; int type; } typ QT_FOR_EACH_STATIC_TYPE(QT_ADD_STATIC_METATYPE) QT_FOR_EACH_STATIC_ALIAS_TYPE(QT_ADD_STATIC_METATYPE_ALIASES_ITER) QT_FOR_EACH_STATIC_HACKS_TYPE(QT_ADD_STATIC_METATYPE_HACKS_ITER) - {0, 0, QMetaType::Void} + {0, 0, QMetaType::UnknownType} }; Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeGuiHelper = 0; @@ -348,10 +349,7 @@ Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) void QMetaType::registerStreamOperators(const char *typeName, SaveOperator saveOp, LoadOperator loadOp) { - int idx = type(typeName); - if (!idx) - return; - registerStreamOperators(idx, saveOp, loadOp); + registerStreamOperators(type(typeName), saveOp, loadOp); } /*! \internal @@ -434,7 +432,7 @@ static int qMetaTypeCustomType_unlocked(const char *typeName, int length) { const QVector<QCustomTypeInfo> * const ct = customTypes(); if (!ct) - return 0; + return QMetaType::UnknownType; for (int v = 0; v < ct->count(); ++v) { const QCustomTypeInfo &customInfo = ct->at(v); @@ -445,7 +443,7 @@ static int qMetaTypeCustomType_unlocked(const char *typeName, int length) return v + QMetaType::User; } } - return 0; + return QMetaType::UnknownType; } /*! \internal @@ -488,11 +486,11 @@ int QMetaType::registerType(const char *typeName, Deleter deleter, int previousSize = 0; int previousFlags = 0; - if (!idx) { + if (idx == UnknownType) { QWriteLocker locker(customTypesLock()); idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QCustomTypeInfo inf; inf.typeName = normalizedTypeName; inf.creator = creator; @@ -558,12 +556,12 @@ int QMetaType::registerTypedef(const char* typeName, int aliasId) int idx = qMetaTypeStaticType(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QWriteLocker locker(customTypesLock()); idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QCustomTypeInfo inf; inf.typeName = normalizedTypeName; inf.alias = aliasId; @@ -592,17 +590,20 @@ int QMetaType::registerTypedef(const char* typeName, int aliasId) */ bool QMetaType::isRegistered(int type) { - if (type >= 0 && type < User) { - // predefined type + // predefined type + if ((type >= FirstCoreType && type <= LastCoreType) + || (type >= FirstGuiType && type <= LastGuiType) + || (type >= FirstWidgetsType && type <= LastWidgetsType)) { return true; } + QReadLocker locker(customTypesLock()); const QVector<QCustomTypeInfo> * const ct = customTypes(); return ((type >= User) && (ct && ct->count() > type - User) && !ct->at(type - User).typeName.isEmpty()); } /*! - Returns a handle to the type called \a typeName, or 0 if there is + Returns a handle to the type called \a typeName, or QMetaType::UnknownType if there is no such type. \sa isRegistered(), typeName(), Type @@ -611,17 +612,17 @@ int QMetaType::type(const char *typeName) { int length = qstrlen(typeName); if (!length) - return 0; + return UnknownType; int type = qMetaTypeStaticType(typeName, length); - if (!type) { + if (type == UnknownType) { QReadLocker locker(customTypesLock()); type = qMetaTypeCustomType_unlocked(typeName, length); #ifndef QT_NO_QOBJECT - if (!type) { + if (type == UnknownType) { const NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); type = qMetaTypeStaticType(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!type) { + if (type == UnknownType) { type = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); } @@ -652,6 +653,7 @@ bool QMetaType::save(QDataStream &stream, int type, const void *data) return false; switch(type) { + case QMetaType::UnknownType: case QMetaType::Void: case QMetaType::VoidStar: case QMetaType::QObjectStar: @@ -857,6 +859,7 @@ bool QMetaType::load(QDataStream &stream, int type, void *data) return false; switch(type) { + case QMetaType::UnknownType: case QMetaType::Void: case QMetaType::VoidStar: case QMetaType::QObjectStar: @@ -1154,6 +1157,7 @@ void *QMetaType::create(int type, const void *copy) case QMetaType::QModelIndex: return new NS(QModelIndex)(*static_cast<const NS(QModelIndex)*>(copy)); #endif + case QMetaType::UnknownType: case QMetaType::Void: return 0; default: @@ -1257,6 +1261,7 @@ void *QMetaType::create(int type, const void *copy) case QMetaType::QModelIndex: return new NS(QModelIndex); #endif + case QMetaType::UnknownType: case QMetaType::Void: return 0; default: @@ -1318,6 +1323,7 @@ public: template<typename T> void delegate(const T *where) { DestroyerImpl<T>::Destroy(m_type, const_cast<T*>(where)); } void delegate(const void *) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType *where) { customTypeDestroyer(m_type, (void*)where); } private: @@ -1381,6 +1387,7 @@ public: template<typename T> void *delegate(const T *copy) { return ConstructorImpl<T>::Construct(m_type, m_where, copy); } void *delegate(const void *) { return m_where; } + void *delegate(const QMetaTypeSwitcher::UnknownType*) { return m_where; } void *delegate(const QMetaTypeSwitcher::NotBuiltinType *copy) { return customTypeConstructor(m_type, m_where, copy); } private: @@ -1470,6 +1477,7 @@ public: template<typename T> void delegate(const T *where) { DestructorImpl<T>::Destruct(m_type, const_cast<T*>(where)); } void delegate(const void *) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType *where) { customTypeDestructor(m_type, (void*)where); } private: @@ -1539,6 +1547,7 @@ public: template<typename T> int delegate(const T*) { return SizeOfImpl<T>::Size(m_type); } + int delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } int delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customTypeSizeOf(m_type); } private: static int customTypeSizeOf(const int type) @@ -1609,13 +1618,14 @@ public: template<typename T> quint32 delegate(const T*) { return FlagsImpl<T>::Flags(m_type); } quint32 delegate(const void*) { return 0; } + quint32 delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } quint32 delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customTypeFlags(m_type); } private: const int m_type; static quint32 customTypeFlags(const int type) { const QVector<QCustomTypeInfo> * const ct = customTypes(); - if (Q_UNLIKELY(!ct)) + if (Q_UNLIKELY(!ct || type < QMetaType::User)) return 0; QReadLocker locker(customTypesLock()); if (Q_UNLIKELY(ct->count() <= type - QMetaType::User)) @@ -1796,6 +1806,7 @@ public: template<typename T> void delegate(const T*) { TypeInfoImpl<T>(m_type, info); } void delegate(const void*) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType*) { customTypeInfo(m_type); } private: void customTypeInfo(const uint type) @@ -1816,7 +1827,7 @@ QMetaType QMetaType::typeInfo(const int type) { TypeInfo typeInfo(type); QMetaTypeSwitcher::switcher<void>(typeInfo, type, 0); - return typeInfo.info.creator || !type ? QMetaType(QMetaType::NoExtensionFlags + return typeInfo.info.creator || type == Void ? QMetaType(QMetaType::NoExtensionFlags , static_cast<const QMetaTypeInterface *>(0) // typeInfo::info is a temporary variable, we can't return address of it. , typeInfo.info.creator , typeInfo.info.deleter @@ -1827,26 +1838,23 @@ QMetaType QMetaType::typeInfo(const int type) , typeInfo.info.size , typeInfo.info.flags , type) - : QMetaType(-1); + : QMetaType(UnknownType); } QMetaType::QMetaType(const int typeId) : m_typeId(typeId) { - if (Q_UNLIKELY(typeId == -1)) { + if (Q_UNLIKELY(typeId == UnknownType)) { // Constructs invalid QMetaType instance. m_extensionFlags = 0xffffffff; Q_ASSERT(!isValid()); } else { // TODO it can be better. *this = QMetaType::typeInfo(typeId); - if (m_typeId > 0 && !m_creator) { + if (m_typeId == UnknownType) m_extensionFlags = 0xffffffff; - m_typeId = -1; - } - if (m_typeId == QMetaType::Void) { + else if (m_typeId == QMetaType::Void) m_extensionFlags = CreateEx | DestroyEx | ConstructEx | DestructEx; - } } } diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 8dc45bb2ed..fb2bce7d7b 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE // F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType) #define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\ - F(Void, 0, void) \ + F(Void, 43, void) \ F(Bool, 1, bool) \ F(Int, 2, int) \ F(UInt, 3, uint) \ @@ -192,8 +192,8 @@ public: // these are merged with QVariant QT_FOR_EACH_STATIC_TYPE(QT_DEFINE_METATYPE_ID) - FirstCoreType = Void, - LastCoreType = QModelIndex, + FirstCoreType = Bool, + LastCoreType = Void, FirstGuiType = QFont, LastGuiType = QPolygonF, FirstWidgetsType = QIcon, @@ -202,6 +202,7 @@ public: QReal = sizeof(qreal) == sizeof(double) ? Double : Float, + UnknownType = 0, User = 256 }; @@ -677,7 +678,7 @@ inline QMetaType::~QMetaType() inline bool QMetaType::isValid() const { - return m_typeId >= 0; + return m_typeId != UnknownType; } inline bool QMetaType::isRegistered() const diff --git a/src/corelib/kernel/qmetatypeswitcher_p.h b/src/corelib/kernel/qmetatypeswitcher_p.h index e9c15ea214..ffd188c972 100644 --- a/src/corelib/kernel/qmetatypeswitcher_p.h +++ b/src/corelib/kernel/qmetatypeswitcher_p.h @@ -59,7 +59,8 @@ QT_BEGIN_NAMESPACE class QMetaTypeSwitcher { public: - class NotBuiltinType; + class NotBuiltinType; // type is not a built-in type, but it may be a custom type or an unknown type + class UnknownType; // type not known to QMetaType system template<class ReturnType, class DelegateObject> static ReturnType switcher(DelegateObject &logic, int type, const void *data); }; @@ -74,7 +75,11 @@ ReturnType QMetaTypeSwitcher::switcher(DelegateObject &logic, int type, const vo switch (QMetaType::Type(type)) { QT_FOR_EACH_STATIC_TYPE(QT_METATYPE_SWICHER_CASE) + case QMetaType::UnknownType: + return logic.delegate(static_cast<UnknownType const *>(data)); default: + if (type < QMetaType::User) + return logic.delegate(static_cast<UnknownType const *>(data)); return logic.delegate(static_cast<NotBuiltinType const *>(data)); } } diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 3a4d1da592..edbb84a657 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -105,6 +105,30 @@ static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) return types; } +static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc) +{ + QScopedArrayPointer<int> types(new int [argc + 1]); + for (int i = 0; i < argc; ++i) { + const QArgumentType &type = argumentTypes[i]; + if (type.type()) + types[i] = type.type(); + else if (type.name().endsWith('*')) + types[i] = QMetaType::VoidStar; + else + types[i] = QMetaType::type(type.name()); + + if (!types[i]) { + qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" + "(Make sure '%s' is registered using qRegisterMetaType().)", + type.name().constData(), type.name().constData()); + return 0; + } + } + types[argc] = 0; + + return types.take(); +} + static QBasicMutex _q_ObjectMutexPool[131]; /** \internal @@ -2179,12 +2203,12 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM if (signal.attributes() & QMetaMethod::Compatibility) { if (!(method.attributes() & QMetaMethod::Compatibility)) qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", - sender->className(), signal.signature()); + sender->className(), signal.methodSignature().constData()); } else if ((method.attributes() & QMetaMethod::Compatibility) && method.methodType() == QMetaMethod::Signal) { qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", - sender->className(), signal.signature(), - receiver->className(), method.signature()); + sender->className(), signal.methodSignature().constData(), + receiver->className(), method.methodSignature().constData()); } } @@ -2283,20 +2307,40 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code - int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - if (signal_index < 0) { - // check for normalized signatures - tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); - signal = tmp_signal_name.constData() + 1; - - smeta = sender->metaObject(); + QByteArray signalName; + QArgumentTypeArray signalTypes; + int signal_index; + if (QMetaObjectPrivate::get(smeta)->revision >= 7) { + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + if (signal_index < 0) { + // check for normalized signatures + tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); + signal = tmp_signal_name.constData() + 1; + + signalTypes.clear(); + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + } + } else { signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - } - if (signal_index < 0) { - // re-use tmp_signal_name and signal from above + if (signal_index < 0) { + // check for normalized signatures + tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); + signal = tmp_signal_name.constData() + 1; + + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); + if (signal_index < 0) { + // re-use tmp_signal_name and signal from above - smeta = sender->metaObject(); - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + } + } } if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); @@ -2317,36 +2361,71 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign const char *method_arg = method; ++method; // skip code + QByteArray methodName; + QArgumentTypeArray methodTypes; const QMetaObject *rmeta = receiver->metaObject(); int method_index_relative = -1; - switch (membcode) { - case QSLOT_CODE: - method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); - break; - case QSIGNAL_CODE: - method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); - break; - } - - if (method_index_relative < 0) { - // check for normalized methods - tmp_method_name = QMetaObject::normalizedSignature(method); - method = tmp_method_name.constData(); - - // rmeta may have been modified above - rmeta = receiver->metaObject(); + if (QMetaObjectPrivate::get(rmeta)->revision >= 7) { + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + } + if (method_index_relative < 0) { + // check for normalized methods + tmp_method_name = QMetaObject::normalizedSignature(method); + method = tmp_method_name.constData(); + + methodTypes.clear(); + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); + // rmeta may have been modified above + rmeta = receiver->metaObject(); + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + } + } + } else { switch (membcode) { case QSLOT_CODE: method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); - if (method_index_relative < 0) - method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); break; case QSIGNAL_CODE: method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); - if (method_index_relative < 0) - method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); break; } + + if (method_index_relative < 0) { + // check for normalized methods + tmp_method_name = QMetaObject::normalizedSignature(method); + method = tmp_method_name.constData(); + + // rmeta may have been modified above + rmeta = receiver->metaObject(); + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); + if (method_index_relative < 0) + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); + if (method_index_relative < 0) + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); + break; + } + } } if (method_index_relative < 0) { @@ -2355,7 +2434,18 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign return QMetaObject::Connection(0); } - if (!QMetaObject::checkConnectArgs(signal, method)) { + bool compatibleArgs = true; + if ((QMetaObjectPrivate::get(smeta)->revision < 7) && (QMetaObjectPrivate::get(rmeta)->revision < 7)) { + compatibleArgs = QMetaObject::checkConnectArgs(signal, method); + } else { + if (signalName.isEmpty()) + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + if (methodName.isEmpty()) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); + compatibleArgs = QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(), + methodTypes.size(), methodTypes.constData()); + } + if (!compatibleArgs) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", sender->metaObject()->className(), signal, @@ -2365,8 +2455,11 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign int *types = 0; if ((type == Qt::QueuedConnection) - && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) + && ((QMetaObjectPrivate::get(smeta)->revision >= 7) + ? !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size())) + : !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))) { return QMetaObject::Connection(0); + } #ifndef QT_NO_DEBUG if (warnCompat) { @@ -2424,17 +2517,17 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho || method.methodType() == QMetaMethod::Constructor) { qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", sender ? sender->metaObject()->className() : "(null)", - signal.signature(), + signal.methodSignature().constData(), receiver ? receiver->metaObject()->className() : "(null)", - method.signature() ); + method.methodSignature().constData() ); return QMetaObject::Connection(0); } - // Reconstructing SIGNAL() macro result for signal.signature() string + // Reconstructing SIGNAL() macro result for signal.methodSignature() string QByteArray signalSignature; - signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.reserve(signal.methodSignature().size()+1); signalSignature.append((char)(QSIGNAL_CODE + '0')); - signalSignature.append(signal.signature()); + signalSignature.append(signal.methodSignature()); int signal_index; int method_index; @@ -2448,20 +2541,20 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho const QMetaObject *rmeta = receiver->metaObject(); if (signal_index == -1) { qWarning("QObject::connect: Can't find signal %s on instance of class %s", - signal.signature(), smeta->className()); + signal.methodSignature().constData(), smeta->className()); return QMetaObject::Connection(0); } if (method_index == -1) { qWarning("QObject::connect: Can't find method %s on instance of class %s", - method.signature(), rmeta->className()); + method.methodSignature().constData(), rmeta->className()); return QMetaObject::Connection(0); } - if (!QMetaObject::checkConnectArgs(signal.signature(), method.signature())) { + if (!QMetaObject::checkConnectArgs(signal.methodSignature().constData(), method.methodSignature().constData())) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", - smeta->className(), signal.signature(), - rmeta->className(), method.signature()); + smeta->className(), signal.methodSignature().constData(), + rmeta->className(), method.methodSignature().constData()); return QMetaObject::Connection(0); } @@ -2609,12 +2702,25 @@ bool QObject::disconnect(const QObject *sender, const char *signal, */ bool res = false; const QMetaObject *smeta = sender->metaObject(); + QByteArray signalName; + QArgumentTypeArray signalTypes; + if (signal && (QMetaObjectPrivate::get(smeta)->revision >= 7)) + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + QByteArray methodName; + QArgumentTypeArray methodTypes; + if (method && (QMetaObjectPrivate::get(receiver->metaObject())->revision >= 7)) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); do { int signal_index = -1; if (signal) { - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - if (signal_index < 0) - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + if (QMetaObjectPrivate::get(smeta)->revision >= 7) { + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + } else { + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); + if (signal_index < 0) + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + } if (signal_index < 0) break; signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); @@ -2629,7 +2735,13 @@ bool QObject::disconnect(const QObject *sender, const char *signal, } else { const QMetaObject *rmeta = receiver->metaObject(); do { - int method_index = rmeta->indexOfMethod(method); + int method_index; + if (QMetaObjectPrivate::get(rmeta)->revision >= 7) { + method_index = QMetaObjectPrivate::indexOfMethod( + rmeta, methodName, methodTypes.size(), methodTypes.constData()); + } else { + method_index = rmeta->indexOfMethod(method); + } if (method_index >= 0) while (method_index < rmeta->methodOffset()) rmeta = rmeta->superClass(); @@ -2693,24 +2805,24 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, if(signal.methodType() != QMetaMethod::Signal) { qWarning("QObject::%s: Attempt to %s non-signal %s::%s", "disconnect","unbind", - sender->metaObject()->className(), signal.signature()); + sender->metaObject()->className(), signal.methodSignature().constData()); return false; } } if (method.mobj) { if(method.methodType() == QMetaMethod::Constructor) { qWarning("QObject::disconect: cannot use constructor as argument %s::%s", - receiver->metaObject()->className(), method.signature()); + receiver->metaObject()->className(), method.methodSignature().constData()); return false; } } - // Reconstructing SIGNAL() macro result for signal.signature() string + // Reconstructing SIGNAL() macro result for signal.methodSignature() string QByteArray signalSignature; if (signal.mobj) { - signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.reserve(signal.methodSignature().size()+1); signalSignature.append((char)(QSIGNAL_CODE + '0')); - signalSignature.append(signal.signature()); + signalSignature.append(signal.methodSignature()); } int signal_index; @@ -2724,13 +2836,13 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, // is -1 then this signal is not a member of sender. if (signal.mobj && signal_index == -1) { qWarning("QObject::disconect: signal %s not found on class %s", - signal.signature(), sender->metaObject()->className()); + signal.methodSignature().constData(), sender->metaObject()->className()); return false; } // If this condition is true then method is not a member of receeiver. if (receiver && method.mobj && method_index == -1) { qWarning("QObject::disconect: method %s not found on class %s", - method.signature(), receiver->metaObject()->className()); + method.methodSignature().constData(), receiver->metaObject()->className()); return false; } @@ -3045,7 +3157,8 @@ void QMetaObject::connectSlotsByName(QObject *o) Q_ASSERT(mo); const QObjectList list = o->findChildren<QObject *>(QString()); for (int i = 0; i < mo->methodCount(); ++i) { - const char *slot = mo->method(i).signature(); + QByteArray slotSignature = mo->method(i).methodSignature(); + const char *slot = slotSignature.constData(); Q_ASSERT(slot); if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_') continue; @@ -3065,7 +3178,7 @@ void QMetaObject::connectSlotsByName(QObject *o) if (method.methodType() != QMetaMethod::Signal) continue; - if (!qstrncmp(method.signature(), slot + len + 4, slotlen)) { + if (!qstrncmp(method.methodSignature().constData(), slot + len + 4, slotlen)) { int signalOffset, methodOffset; computeOffsets(method.enclosingMetaObject(), &signalOffset, &methodOffset); sigIndex = k + - methodOffset + signalOffset; @@ -3311,9 +3424,17 @@ int QObjectPrivate::signalIndex(const char *signalName) const { Q_Q(const QObject); const QMetaObject *base = q->metaObject(); - int relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, false); - if (relative_index < 0) - relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, true); + int relative_index; + if (QMetaObjectPrivate::get(base)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(signalName, types); + relative_index = QMetaObjectPrivate::indexOfSignalRelative( + &base, name, types.size(), types.constData()); + } else { + relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, false); + if (relative_index < 0) + relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, true); + } if (relative_index < 0) return relative_index; relative_index = QMetaObjectPrivate::originalClone(base, relative_index); @@ -3536,7 +3657,7 @@ void QObject::dumpObjectInfo() offset = methodOffset - signalOffset; } const QMetaMethod signal = metaObject()->method(signal_index + offset); - qDebug(" signal: %s", signal.signature()); + qDebug(" signal: %s", signal.methodSignature().constData()); // receivers const QObjectPrivate::Connection *c = @@ -3552,7 +3673,7 @@ void QObject::dumpObjectInfo() qDebug(" --> %s::%s %s", receiverMetaObject->className(), c->receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver->objectName()), - method.signature()); + method.methodSignature().constData()); c = c->nextConnectionList; } } @@ -3569,7 +3690,7 @@ void QObject::dumpObjectInfo() qDebug(" <-- %s::%s %s", s->sender->metaObject()->className(), s->sender->objectName().isEmpty() ? "unnamed" : qPrintable(s->sender->objectName()), - slot.signature()); + slot.methodSignature().constData()); } } else { qDebug(" <None>"); @@ -4041,9 +4162,16 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa locker.unlock(); // reconstruct the signature to call connectNotify - const char *sig = senderMetaObject->d.stringdata + senderMetaObject->d.data[ - reinterpret_cast<const QMetaObjectPrivate*>(senderMetaObject->d.data)->methodData - + 5 * (signal_index - signalOffset)]; + QByteArray tmp_sig; + const char *sig; + if (QMetaObjectPrivate::get(senderMetaObject)->revision >= 7) { + tmp_sig = senderMetaObject->method(signal_index - signalOffset + methodOffset).methodSignature(); + sig = tmp_sig.constData(); + } else { + sig = reinterpret_cast<const char *>(senderMetaObject->d.stringdata) + + senderMetaObject->d.data[QMetaObjectPrivate::get(senderMetaObject)->methodData + + 5 * (signal_index - signalOffset)]; + } QVarLengthArray<char> signalSignature(qstrlen(sig) + 2); signalSignature.data()[0] = char(QSIGNAL_CODE + '0'); strcpy(signalSignature.data() + 1 , sig); diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 4b3829b53e..4c98bad0f9 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -50,11 +50,12 @@ QT_BEGIN_NAMESPACE class QByteArray; +struct QByteArrayData; class QString; #ifndef Q_MOC_OUTPUT_REVISION -#define Q_MOC_OUTPUT_REVISION 64 +#define Q_MOC_OUTPUT_REVISION 65 #endif // The following macros are our "extensions" to C++ @@ -326,6 +327,8 @@ struct Q_CORE_EXPORT QMetaObject QMetaProperty userProperty() const; static bool checkConnectArgs(const char *signal, const char *method); + static bool checkConnectArgs(const QMetaMethod &signal, + const QMetaMethod &method); static QByteArray normalizedSignature(const char *method); static QByteArray normalizedType(const char *type); @@ -439,7 +442,7 @@ struct Q_CORE_EXPORT QMetaObject struct { // private data const QMetaObject *superdata; - const char *stringdata; + const QByteArrayData *stringdata; const uint *data; const void *extradata; } d; @@ -480,9 +483,6 @@ struct QMetaObjectExtraData StaticMetacallFunction static_metacall; }; -inline const char *QMetaObject::className() const -{ return d.stringdata; } - inline const QMetaObject *QMetaObject::superClass() const { return d.superdata; } diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 029a261faf..39e8b4360f 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -1697,7 +1697,7 @@ void QVariant::load(QDataStream &s) QByteArray name; s >> name; typeId = QMetaType::type(name.constData()); - if (!typeId) { + if (typeId == QMetaType::UnknownType) { s.setStatus(QDataStream::ReadCorruptData); return; } diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 5da482d5cd..4d4b2d51ab 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -126,7 +126,7 @@ class Q_CORE_EXPORT QVariant { public: enum Type { - Invalid = QMetaType::Void, + Invalid = QMetaType::UnknownType, Bool = QMetaType::Bool, Int = QMetaType::Int, UInt = QMetaType::UInt, diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index a754bc4363..b28aaf3c20 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -187,7 +187,11 @@ public: return FilteredComparator<T>::compare(m_a, m_b); } - bool delegate(const void*) { return true; } + bool delegate(const void*) { Q_ASSERT(false); return true; } + bool delegate(const QMetaTypeSwitcher::UnknownType*) + { + return true; // for historical reason invalid variant == invalid variant + } bool delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return false; } protected: const QVariant::Private *m_a; @@ -281,7 +285,8 @@ public: return CallIsNull<T>::isNull(m_d); } // we need that as sizof(void) is undefined and it is needed in HasIsNullMethod - bool delegate(const void *) { return m_d->is_null; } + bool delegate(const void *) { Q_ASSERT(false); return m_d->is_null; } + bool delegate(const QMetaTypeSwitcher::UnknownType *) { return m_d->is_null; } bool delegate(const QMetaTypeSwitcher::NotBuiltinType *) { return m_d->is_null; } protected: const QVariant::Private *m_d; @@ -354,8 +359,18 @@ public: void delegate(const void*) { - // QMetaType::Void == QVariant::Invalid, creating an invalid value creates invalid QVariant - // TODO it might go away, check is needed + qWarning("Trying to create a QVariant instance of QMetaType::Void type, an invalid QVariant will be constructed instead"); + m_x->type = QMetaType::UnknownType; + m_x->is_shared = false; + m_x->is_null = !m_copy; + } + + void delegate(const QMetaTypeSwitcher::UnknownType*) + { + if (m_x->type != QMetaType::UnknownType) { + qWarning("Trying to construct an instance of an invalid type, type id: %i", m_x->type); + m_x->type = QMetaType::UnknownType; + } m_x->is_shared = false; m_x->is_null = !m_copy; } @@ -401,7 +416,8 @@ public: qWarning("Trying to destruct an instance of an invalid type, type id: %i", m_d->type); } // Ignore nonconstructible type - void delegate(const void*) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} + void delegate(const void*) { Q_ASSERT(false); } private: QVariant::Private *m_d; }; @@ -446,10 +462,11 @@ public: { qWarning("Trying to stream an instance of an invalid type, type id: %i", m_d->type); } - void delegate(const void*) + void delegate(const QMetaTypeSwitcher::UnknownType*) { m_debugStream.nospace() << "QVariant::Invalid"; } + void delegate(const void*) { Q_ASSERT(false); } private: QDebug m_debugStream; QVariant::Private *m_d; diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 7ff005f9a1..be96d895a2 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1657,7 +1657,7 @@ void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalInd #ifdef QSTATEMACHINE_DEBUG qDebug() << q_func() << ": sending signal event ( sender =" << sender - << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')'; + << ", signal =" << sender->metaObject()->method(signalIndex).methodSignature().constData() << ')'; #endif postInternalEvent(new QStateMachine::SignalEvent(sender, signalIndex, vargs)); processEvents(DirectProcessing); @@ -2211,10 +2211,29 @@ void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation) // Begin moc-generated code -- modify carefully (check "HAND EDIT" parts)! +struct qt_meta_stringdata_QSignalEventGenerator_t { + QByteArrayData data[3]; + char stringdata[32]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) { \ + Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \ + offsetof(qt_meta_stringdata_QSignalEventGenerator_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + } +static const qt_meta_stringdata_QSignalEventGenerator_t qt_meta_stringdata_QSignalEventGenerator = { + { +QT_MOC_LITERAL(0, 0, 21), +QT_MOC_LITERAL(1, 22, 7), +QT_MOC_LITERAL(2, 30, 0) + }, + "QSignalEventGenerator\0execute\0\0" +}; +#undef QT_MOC_LITERAL + static const uint qt_meta_data_QSignalEventGenerator[] = { // content: - 6, // revision + 7, // revision 0, // classname 0, 0, // classinfo 1, 14, // methods @@ -2224,14 +2243,13 @@ static const uint qt_meta_data_QSignalEventGenerator[] = { 0, // flags 0, // signalCount - // slots: signature, parameters, type, tag, flags - 23, 22, 22, 22, 0x0a, + // slots: name, argc, parameters, tag, flags + 1, 0, 19, 2, 0x0a, - 0 // eod -}; + // slots: parameters + QMetaType::Void, -static const char qt_meta_stringdata_QSignalEventGenerator[] = { - "QSignalEventGenerator\0\0execute()\0" + 0 // eod }; void QSignalEventGenerator::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) @@ -2252,7 +2270,7 @@ const QMetaObjectExtraData QSignalEventGenerator::staticMetaObjectExtraData = { }; const QMetaObject QSignalEventGenerator::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator, + { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator.data, qt_meta_data_QSignalEventGenerator, &staticMetaObjectExtraData } }; @@ -2264,7 +2282,7 @@ const QMetaObject *QSignalEventGenerator::metaObject() const void *QSignalEventGenerator::qt_metacast(const char *_clname) { if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator)) + if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator.stringdata)) return static_cast<void*>(const_cast< QSignalEventGenerator*>(this)); return QObject::qt_metacast(_clname); } diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp new file mode 100644 index 0000000000..8498d0e4d5 --- /dev/null +++ b/src/corelib/tools/qarraydata.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qarraydata.h> +#include <QtCore/private/qtools_p.h> + +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + +static const QArrayData qt_array_empty = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; +static const QArrayData qt_array_unsharable_empty = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; + +QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, + size_t capacity, AllocationOptions options) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + + // Don't allocate empty headers + if (!(options & RawData) && !capacity) + return !(options & Unsharable) + ? const_cast<QArrayData *>(&qt_array_empty) + : const_cast<QArrayData *>(&qt_array_unsharable_empty); + + size_t headerSize = sizeof(QArrayData); + + // Allocate extra (alignment - Q_ALIGNOF(QArrayData)) padding bytes so we + // can properly align the data array. This assumes malloc is able to + // provide appropriate alignment for the header -- as it should! + // Padding is skipped when allocating a header for RawData. + if (!(options & RawData)) + headerSize += (alignment - Q_ALIGNOF(QArrayData)); + + // Allocate additional space if array is growing + if (options & Grow) + capacity = qAllocMore(objectSize * capacity, headerSize) / objectSize; + + size_t allocSize = headerSize + objectSize * capacity; + + QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize)); + if (header) { + quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) + & ~(alignment - 1); + + header->ref.atomic.store(bool(!(options & Unsharable))); + header->size = 0; + header->alloc = capacity; + header->capacityReserved = bool(options & CapacityReserved); + header->offset = data - quintptr(header); + } + + return header; +} + +void QArrayData::deallocate(QArrayData *data, size_t objectSize, + size_t alignment) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + Q_UNUSED(objectSize) Q_UNUSED(alignment) + + if (data == &qt_array_unsharable_empty) + return; + + ::free(data); +} + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h new file mode 100644 index 0000000000..351a75aade --- /dev/null +++ b/src/corelib/tools/qarraydata.h @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATA_H +#define QARRAYDATA_H + +#include <QtCore/qrefcount.h> +#include <string.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +struct Q_CORE_EXPORT QArrayData +{ + QtPrivate::RefCount ref; + int size; + uint alloc : 31; + uint capacityReserved : 1; + + qptrdiff offset; // in bytes from beginning of header + + void *data() + { + Q_ASSERT(size == 0 + || offset < 0 || size_t(offset) >= sizeof(QArrayData)); + return reinterpret_cast<char *>(this) + offset; + } + + const void *data() const + { + Q_ASSERT(size == 0 + || offset < 0 || size_t(offset) >= sizeof(QArrayData)); + return reinterpret_cast<const char *>(this) + offset; + } + + // This refers to array data mutability, not "header data" represented by + // data members in QArrayData. Shared data (array and header) must still + // follow COW principles. + bool isMutable() const + { + return alloc != 0; + } + + enum AllocationOption { + CapacityReserved = 0x1, + Unsharable = 0x2, + RawData = 0x4, + Grow = 0x8, + + Default = 0 + }; + + Q_DECLARE_FLAGS(AllocationOptions, AllocationOption) + + AllocationOptions detachFlags() const + { + AllocationOptions result; + if (!ref.isSharable()) + result |= Unsharable; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + AllocationOptions cloneFlags() const + { + AllocationOptions result; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + static QArrayData *allocate(size_t objectSize, size_t alignment, + size_t capacity, AllocationOptions options = Default) + Q_REQUIRED_RESULT; + static void deallocate(QArrayData *data, size_t objectSize, + size_t alignment); + + static const QArrayData shared_null; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::AllocationOptions) + +template <class T> +struct QTypedArrayData + : QArrayData +{ + typedef T *iterator; + typedef const T *const_iterator; + + T *data() { return static_cast<T *>(QArrayData::data()); } + const T *data() const { return static_cast<const T *>(QArrayData::data()); } + + T *begin() { return data(); } + T *end() { return data() + size; } + const T *begin() const { return data(); } + const T *end() const { return data() + size; } + + class AlignmentDummy { QArrayData header; T data; }; + + static QTypedArrayData *allocate(size_t capacity, + AllocationOptions options = Default) Q_REQUIRED_RESULT + { + return static_cast<QTypedArrayData *>(QArrayData::allocate(sizeof(T), + Q_ALIGNOF(AlignmentDummy), capacity, options)); + } + + static void deallocate(QArrayData *data) + { + QArrayData::deallocate(data, sizeof(T), Q_ALIGNOF(AlignmentDummy)); + } + + static QTypedArrayData *fromRawData(const T *data, size_t n, + AllocationOptions options = Default) + { + QTypedArrayData *result = allocate(0, options | RawData); + if (result) { + Q_ASSERT(!result->ref.isShared()); // No shared empty, please! + + result->offset = reinterpret_cast<const char *>(data) + - reinterpret_cast<const char *>(result); + result->size = n; + } + return result; + } + + static QTypedArrayData *sharedNull() + { + return static_cast<QTypedArrayData *>( + const_cast<QArrayData *>(&QArrayData::shared_null)); + } +}; + +template <class T, size_t N> +struct QStaticArrayData +{ + QArrayData header; + T data[N]; +}; + +// Support for returning QArrayDataPointer<T> from functions +template <class T> +struct QArrayDataPointerRef +{ + QTypedArrayData<T> *ptr; +}; + +#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(type, size) { \ + Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, \ + (sizeof(QArrayData) + (Q_ALIGNOF(type) - 1)) \ + & ~(Q_ALIGNOF(type) - 1) } \ + /**/ + +//////////////////////////////////////////////////////////////////////////////// +// Q_ARRAY_LITERAL + +// The idea here is to place a (read-only) copy of header and array data in an +// mmappable portion of the executable (typically, .rodata section). This is +// accomplished by hiding a static const instance of QStaticArrayData, which is +// POD. + +#if defined(Q_COMPILER_VARIADIC_MACROS) +#if defined(Q_COMPILER_LAMBDA) +// Hide array inside a lambda +#define Q_ARRAY_LITERAL(Type, ...) \ + ([]() -> QArrayDataPointerRef<Type> { \ + /* MSVC 2010 Doesn't support static variables in a lambda, but */ \ + /* happily accepts them in a static function of a lambda-local */ \ + /* struct :-) */ \ + struct StaticWrapper { \ + static QArrayDataPointerRef<Type> get() \ + { \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + return ref; \ + } \ + }; \ + return StaticWrapper::get(); \ + }()) \ + /**/ +#elif defined(Q_CC_GNU) +// Hide array within GCC's __extension__ {( )} block +#define Q_ARRAY_LITERAL(Type, ...) \ + __extension__ ({ \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + ref; \ + }) \ + /**/ +#endif +#endif // defined(Q_COMPILER_VARIADIC_MACROS) + +#if defined(Q_ARRAY_LITERAL) +#define Q_ARRAY_LITERAL_IMPL(Type, ...) \ + union { Type type_must_be_POD; } dummy; Q_UNUSED(dummy) \ + \ + /* Portable compile-time array size computation */ \ + Type data[] = { __VA_ARGS__ }; Q_UNUSED(data) \ + enum { Size = sizeof(data) / sizeof(data[0]) }; \ + \ + static const QStaticArrayData<Type, Size> literal = { \ + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(Type, Size), { __VA_ARGS__ } }; \ + \ + QArrayDataPointerRef<Type> ref = \ + { static_cast<QTypedArrayData<Type> *>( \ + const_cast<QArrayData *>(&literal.header)) }; \ + /**/ +#else +// As a fallback, memory is allocated and data copied to the heap. + +// The fallback macro does NOT use variadic macros and does NOT support +// variable number of arguments. It is suitable for char arrays. + +namespace QtPrivate { + template <class T, size_t N> + inline QArrayDataPointerRef<T> qMakeArrayLiteral(const T (&array)[N]) + { + union { T type_must_be_POD; } dummy; Q_UNUSED(dummy) + + QArrayDataPointerRef<T> result = { QTypedArrayData<T>::allocate(N) }; + Q_CHECK_PTR(result.ptr); + + ::memcpy(result.ptr->data(), array, N * sizeof(T)); + result.ptr->size = N; + + return result; + } +} + +#define Q_ARRAY_LITERAL(Type, Array) \ + QT_PREPEND_NAMESPACE(QtPrivate::qMakeArrayLiteral)<Type>( Array ) +#endif // !defined(Q_ARRAY_LITERAL) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h new file mode 100644 index 0000000000..1b8ed3372d --- /dev/null +++ b/src/corelib/tools/qarraydataops.h @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATAOPS_H +#define QARRAYDATAOPS_H + +#include <QtCore/qarraydata.h> + +#include <new> +#include <string.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { + +template <class T> +struct QPodArrayOps + : QTypedArrayData<T> +{ + void copyAppend(const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(b < e); + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memcpy(this->end(), b, (e - b) * sizeof(T)); + this->size += e - b; + } + + void copyAppend(size_t n, const T &t) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(n <= this->alloc - uint(this->size)); + + T *iter = this->end(); + const T *const end = iter + n; + for (; iter != end; ++iter) + ::memcpy(iter, &t, sizeof(T)); + this->size += n; + } + + void destroyAll() // Call from destructors, ONLY! + { + Q_ASSERT(this->ref.atomic.load() == 0); + + // As this is to be called only from destructor, it doesn't need to be + // exception safe; size not updated. + } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memmove(where + (e - b), where, (this->end() - where) * sizeof(T)); + ::memcpy(where, b, (e - b) * sizeof(T)); + this->size += (e - b); + } +}; + +template <class T> +struct QGenericArrayOps + : QTypedArrayData<T> +{ + void copyAppend(const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(b < e); + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + T *iter = this->end(); + for (; b != e; ++iter, ++b) { + new (iter) T(*b); + ++this->size; + } + } + + void copyAppend(size_t n, const T &t) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(n <= this->alloc - uint(this->size)); + + T *iter = this->end(); + const T *const end = iter + n; + for (; iter != end; ++iter) { + new (iter) T(t); + ++this->size; + } + } + + void destroyAll() // Call from destructors, ONLY + { + // As this is to be called only from destructor, it doesn't need to be + // exception safe; size not updated. + + Q_ASSERT(this->ref.atomic.load() == 0); + + const T *const b = this->begin(); + const T *i = this->end(); + + while (i != b) + (--i)->~T(); + } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Array may be truncated at where in case of exceptions + + T *const end = this->end(); + const T *readIter = end; + T *writeIter = end + (e - b); + + const T *const step1End = where + qMax(e - b, end - where); + + struct Destructor + { + Destructor(T *&it) + : iter(&it) + , end(it) + { + } + + void commit() + { + iter = &end; + } + + ~Destructor() + { + for (; *iter != end; --*iter) + (*iter)->~T(); + } + + T **iter; + T *end; + } destroyer(writeIter); + + // Construct new elements in array + do { + --readIter, --writeIter; + new (writeIter) T(*readIter); + } while (writeIter != step1End); + + while (writeIter != end) { + --e, --writeIter; + new (writeIter) T(*e); + } + + destroyer.commit(); + this->size += destroyer.end - end; + + // Copy assign over existing elements + while (readIter != where) { + --readIter, --writeIter; + *writeIter = *readIter; + } + + while (writeIter != where) { + --e, --writeIter; + *writeIter = *e; + } + } +}; + +template <class T> +struct QMovableArrayOps + : QGenericArrayOps<T> +{ + // using QGenericArrayOps<T>::copyAppend; + // using QGenericArrayOps<T>::destroyAll; + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Provides strong exception safety guarantee, + // provided T::~T() nothrow + + struct ReversibleDisplace + { + ReversibleDisplace(T *start, T *finish, size_t diff) + : begin(start) + , end(finish) + , displace(diff) + { + ::memmove(begin + displace, begin, (end - begin) * sizeof(T)); + } + + void commit() { displace = 0; } + + ~ReversibleDisplace() + { + if (displace) + ::memmove(begin, begin + displace, (end - begin) * sizeof(T)); + } + + T *const begin; + T *const end; + size_t displace; + + } displace(where, this->end(), size_t(e - b)); + + struct CopyConstructor + { + CopyConstructor(T *w) : where(w) {} + + void copy(const T *src, const T *const srcEnd) + { + n = 0; + for (; src != srcEnd; ++src) { + new (where + n) T(*src); + ++n; + } + n = 0; + } + + ~CopyConstructor() + { + while (n) + where[--n].~T(); + } + + T *const where; + size_t n; + } copier(where); + + copier.copy(b, e); + displace.commit(); + this->size += (e - b); + } +}; + +template <class T, class = void> +struct QArrayOpsSelector +{ + typedef QGenericArrayOps<T> Type; +}; + +template <class T> +struct QArrayOpsSelector<T, + typename QEnableIf< + !QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic + >::Type> +{ + typedef QPodArrayOps<T> Type; +}; + +template <class T> +struct QArrayOpsSelector<T, + typename QEnableIf< + QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic + >::Type> +{ + typedef QMovableArrayOps<T> Type; +}; + +} // namespace QtPrivate + +template <class T> +struct QArrayDataOps + : QtPrivate::QArrayOpsSelector<T>::Type +{ +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h new file mode 100644 index 0000000000..f5ad53aa54 --- /dev/null +++ b/src/corelib/tools/qarraydatapointer.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATAPOINTER_H +#define QARRAYDATAPOINTER_H + +#include <QtCore/qarraydataops.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +template <class T> +struct QArrayDataPointer +{ +private: + typedef QTypedArrayData<T> Data; + typedef QArrayDataOps<T> DataOps; + +public: + QArrayDataPointer() + : d(Data::sharedNull()) + { + } + + QArrayDataPointer(const QArrayDataPointer &other) + : d(other.d->ref.ref() + ? other.d + : other.clone(other.d->cloneFlags())) + { + } + + explicit QArrayDataPointer(QTypedArrayData<T> *ptr) + : d(ptr) + { + Q_CHECK_PTR(ptr); + } + + QArrayDataPointer(QArrayDataPointerRef<T> ref) + : d(ref.ptr) + { + } + + QArrayDataPointer &operator=(const QArrayDataPointer &other) + { + QArrayDataPointer tmp(other); + this->swap(tmp); + return *this; + } + +#ifdef Q_COMPILER_RVALUE_REFS + QArrayDataPointer(QArrayDataPointer &&other) + : d(other.d) + { + other.d = Data::sharedNull(); + } + + QArrayDataPointer &operator=(QArrayDataPointer &&other) + { + this->swap(other); + return *this; + } +#endif + + DataOps &operator*() const + { + Q_ASSERT(d); + return *static_cast<DataOps *>(d); + } + + DataOps *operator->() const + { + Q_ASSERT(d); + return static_cast<DataOps *>(d); + } + + ~QArrayDataPointer() + { + if (!d->ref.deref()) { + (*this)->destroyAll(); + Data::deallocate(d); + } + } + + bool isNull() const + { + return d == Data::sharedNull(); + } + + Data *data() const + { + return d; + } + + void setSharable(bool sharable) + { + // Can't call setSharable on static read-only data, like shared_null + // and the internal shared-empties. + if (d->alloc == 0 && d->size == 0) { + d = Data::allocate(0, sharable + ? QArrayData::Default + : QArrayData::Unsharable); + return; + } + + detach(); + d->ref.setSharable(sharable); + } + + void swap(QArrayDataPointer &other) + { + qSwap(d, other.d); + } + + void clear() + { + QArrayDataPointer tmp(d); + d = Data::sharedNull(); + } + + bool detach() + { + if (!d->isMutable() || d->ref.isShared()) { + Data *copy = clone(d->detachFlags()); + QArrayDataPointer old(d); + d = copy; + return true; + } + + return false; + } + +private: + Data *clone(QArrayData::AllocationOptions options) const Q_REQUIRED_RESULT + { + QArrayDataPointer copy(Data::allocate(d->alloc ? d->alloc : d->size, + options)); + if (d->size) + copy->copyAppend(d->begin(), d->end()); + + Data *result = copy.d; + copy.d = Data::sharedNull(); + return result; + } + + Data *d; +}; + +template <class T> +inline bool operator==(const QArrayDataPointer<T> &lhs, const QArrayDataPointer<T> &rhs) +{ + return lhs.data() == rhs.data(); +} + +template <class T> +inline bool operator!=(const QArrayDataPointer<T> &lhs, const QArrayDataPointer<T> &rhs) +{ + return lhs.data() != rhs.data(); +} + +template <class T> +inline void qSwap(QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2) +{ + p1.swap(p2); +} + +QT_END_NAMESPACE + +namespace std +{ + template <class T> + inline void swap( + QT_PREPEND_NAMESPACE(QArrayDataPointer)<T> &p1, + QT_PREPEND_NAMESPACE(QArrayDataPointer)<T> &p2) + { + p1.swap(p2); + } +} + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index b6719c9536..ac936b1d0a 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -57,7 +57,7 @@ #include <string.h> #include <stdlib.h> -#define IS_RAW_DATA(d) ((d)->offset != 0) +#define IS_RAW_DATA(d) ((d)->offset != sizeof(QByteArrayData)) QT_BEGIN_NAMESPACE @@ -69,24 +69,25 @@ int qFindByteArray( int qAllocMore(int alloc, int extra) { - if (alloc == 0 && extra == 0) - return 0; - const int page = 1 << 12; - int nalloc; - alloc += extra; - if (alloc < 1<<6) { - nalloc = (1<<3) + ((alloc >>3) << 3); - } else { - // don't do anything if the loop will overflow signed int. - if (alloc >= INT_MAX/2) - return INT_MAX; - nalloc = (alloc < page) ? 1 << 3 : page; - while (nalloc < alloc) { - if (nalloc <= 0) - return INT_MAX; - nalloc *= 2; - } - } + Q_ASSERT(alloc >= 0 && extra >= 0); + Q_ASSERT(alloc < (1 << 30) - extra); + + unsigned nalloc = alloc + extra; + + // Round up to next power of 2 + + // Assuming container is growing, always overshoot + //--nalloc; + + nalloc |= nalloc >> 1; + nalloc |= nalloc >> 2; + nalloc |= nalloc >> 4; + nalloc |= nalloc >> 8; + nalloc |= nalloc >> 16; + ++nalloc; + + Q_ASSERT(nalloc > unsigned(alloc + extra)); + return nalloc - extra; } @@ -554,7 +555,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) } d.take(); // realloc was successful d.reset(p); - d->offset = 0; + d->offset = sizeof(QByteArrayData); int res = ::uncompress((uchar*)d->data(), &len, (uchar*)data+4, nbytes-4); @@ -576,11 +577,11 @@ QByteArray qUncompress(const uchar* data, int nbytes) d.take(); // realloc was successful d.reset(p); } - d->ref = 1; + d->ref.initializeOwned(); d->size = len; d->alloc = len; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->data()[len] = 0; return QByteArray(d.take(), 0, 0); @@ -614,10 +615,10 @@ static inline char qToLower(char c) return c; } -const QConstByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), - 0, 0, 0, { 0 } }, { 0 } }; -const QConstByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), - 0, 0, 0, { 0 } }, { 0 } }; +const QStaticByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, + 0, 0, 0, sizeof(QByteArrayData) }, { 0 } }; +const QStaticByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, + 0, 0, 0, sizeof(QByteArrayData) }, { 0 } }; /*! \class QByteArray @@ -904,7 +905,7 @@ QByteArray &QByteArray::operator=(const char *str) x = const_cast<Data *>(&shared_empty.ba); } else { int len = qstrlen(str); - if (d->ref != 1 || len > int(d->alloc) || (len < d->size && len < int(d->alloc) >> 1)) + if (d->ref.isShared() || len > int(d->alloc) || (len < d->size && len < int(d->alloc) >> 1)) realloc(len); x = d; memcpy(x->data(), str, len + 1); // include null terminator @@ -1289,38 +1290,16 @@ void QByteArray::chop(int n) \sa isEmpty() */ -/*! \fn QByteArray::QByteArray(const char *str) - - Constructs a byte array initialized with the string \a str. - - QByteArray makes a deep copy of the string data. -*/ - -QByteArray::QByteArray(const char *str) -{ - if (!str) { - d = const_cast<Data *>(&shared_null.ba); - } else if (!*str) { - d = const_cast<Data *>(&shared_empty.ba); - } else { - int len = qstrlen(str); - d = static_cast<Data *>(malloc(sizeof(Data) + len + 1)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = len; - d->alloc = len; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), str, len+1); // include null terminator - } -} - /*! Constructs a byte array containing the first \a size bytes of array \a data. If \a data is 0, a null byte array is constructed. + If \a size is negative, \a data is assumed to point to a nul-terminated + string and its length is determined dynamically. The terminating + nul-character is not considered part of the byte array. + QByteArray makes a deep copy of the string data. \sa fromRawData() @@ -1330,18 +1309,22 @@ QByteArray::QByteArray(const char *data, int size) { if (!data) { d = const_cast<Data *>(&shared_null.ba); - } else if (size <= 0) { - d = const_cast<Data *>(&shared_empty.ba); } else { - d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), data, size); - d->data()[size] = '\0'; + if (size < 0) + size = strlen(data); + if (!size) { + d = const_cast<Data *>(&shared_empty.ba); + } else { + d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); + Q_CHECK_PTR(d); + d->ref.initializeOwned(); + d->size = size; + d->alloc = size; + d->capacityReserved = false; + d->offset = sizeof(QByteArrayData); + memcpy(d->data(), data, size); + d->data()[size] = '\0'; + } } } @@ -1359,11 +1342,11 @@ QByteArray::QByteArray(int size, char ch) } else { d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); memset(d->data(), ch, size); d->data()[size] = '\0'; } @@ -1379,11 +1362,11 @@ QByteArray::QByteArray(int size, Qt::Initialization) { d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->data()[size] = '\0'; } @@ -1405,7 +1388,7 @@ void QByteArray::resize(int size) if (size < 0) size = 0; - if (d->offset && d->ref == 1 && size < d->size) { + if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1426,15 +1409,15 @@ void QByteArray::resize(int size) // Data *x = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = size; x->capacityReserved = false; - x->offset = 0; + x->offset = sizeof(QByteArrayData); x->data()[size] = '\0'; d = x; } else { - if (d->ref != 1 || size > int(d->alloc) + if (d->ref.isShared() || size > int(d->alloc) || (!d->capacityReserved && size < d->size && size < int(d->alloc) >> 1)) realloc(qAllocMore(size, sizeof(Data))); if (int(d->alloc) >= size) { @@ -1465,14 +1448,14 @@ QByteArray &QByteArray::fill(char ch, int size) void QByteArray::realloc(int alloc) { - if (d->ref != 1 || d->offset) { + if (d->ref.isShared() || IS_RAW_DATA(d)) { Data *x = static_cast<Data *>(malloc(sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = alloc; x->capacityReserved = d->capacityReserved; - x->offset = 0; + x->offset = sizeof(QByteArrayData); ::memcpy(x->data(), d->data(), x->size); x->data()[x->size] = '\0'; if (!d->ref.deref()) @@ -1482,7 +1465,7 @@ void QByteArray::realloc(int alloc) Data *x = static_cast<Data *>(::realloc(d, sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); x->alloc = alloc; - x->offset = 0; + x->offset = sizeof(QByteArrayData); d = x; } } @@ -1504,7 +1487,7 @@ void QByteArray::expand(int i) QByteArray QByteArray::nulTerminated() const { // is this fromRawData? - if (!d->offset) + if (!IS_RAW_DATA(d)) return *this; // no, then we're sure we're zero terminated QByteArray copy(*this); @@ -1566,7 +1549,7 @@ QByteArray &QByteArray::prepend(const char *str) QByteArray &QByteArray::prepend(const char *str, int len) { if (str) { - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memmove(d->data()+len, d->data(), d->size); memcpy(d->data(), str, len); @@ -1584,7 +1567,7 @@ QByteArray &QByteArray::prepend(const char *str, int len) QByteArray &QByteArray::prepend(char ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(qAllocMore(d->size + 1, sizeof(Data))); memmove(d->data()+1, d->data(), d->size); d->data()[0] = ch; @@ -1622,7 +1605,7 @@ QByteArray &QByteArray::append(const QByteArray &ba) if ((d == &shared_null.ba || d == &shared_empty.ba) && !IS_RAW_DATA(ba.d)) { *this = ba; } else if (ba.d != &shared_null.ba) { - if (d->ref != 1 || d->size + ba.d->size > int(d->alloc)) + if (d->ref.isShared() || d->size + ba.d->size > int(d->alloc)) realloc(qAllocMore(d->size + ba.d->size, sizeof(Data))); memcpy(d->data() + d->size, ba.d->data(), ba.d->size); d->size += ba.d->size; @@ -1656,7 +1639,7 @@ QByteArray& QByteArray::append(const char *str) { if (str) { int len = qstrlen(str); - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memcpy(d->data() + d->size, str, len + 1); // include null terminator d->size += len; @@ -1681,7 +1664,7 @@ QByteArray &QByteArray::append(const char *str, int len) if (len < 0) len = qstrlen(str); if (str && len) { - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memcpy(d->data() + d->size, str, len); // include null terminator d->size += len; @@ -1698,7 +1681,7 @@ QByteArray &QByteArray::append(const char *str, int len) QByteArray& QByteArray::append(char ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(qAllocMore(d->size + 1, sizeof(Data))); d->data()[d->size++] = ch; d->data()[d->size] = '\0'; @@ -3889,11 +3872,11 @@ QByteArray QByteArray::fromRawData(const char *data, int size) } else { x = static_cast<Data *>(malloc(sizeof(Data) + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = 0; x->capacityReserved = false; - x->offset = data - (x->d + sizeof(qptrdiff)); + x->offset = data - reinterpret_cast<char *>(x); } return QByteArray(x, 0, 0); } @@ -3914,14 +3897,14 @@ QByteArray QByteArray::fromRawData(const char *data, int size) */ QByteArray &QByteArray::setRawData(const char *data, uint size) { - if (d->ref != 1 || d->alloc) { + if (d->ref.isShared() || d->alloc) { *this = fromRawData(data, size); } else { if (data) { d->size = size; - d->offset = data - (d->d + sizeof(qptrdiff)); + d->offset = data - reinterpret_cast<char *>(d); } else { - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->size = 0; *d->data() = 0; } diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 09c43988fd..e65a9201c2 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -124,32 +124,31 @@ struct QByteArrayData int size; uint alloc : 31; uint capacityReserved : 1; - union { - qptrdiff offset; // will always work as we add/subtract from a ushort ptr - char d[sizeof(qptrdiff)]; - }; - inline char *data() { return d + sizeof(qptrdiff) + offset; } - inline const char *data() const { return d + sizeof(qptrdiff) + offset; } + + qptrdiff offset; + + inline char *data() { return reinterpret_cast<char *>(this) + offset; } + inline const char *data() const { return reinterpret_cast<const char *>(this) + offset; } }; -template<int N> struct QConstByteArrayData +template<int N> struct QStaticByteArrayData { - const QByteArrayData ba; - const char data[N + 1]; + QByteArrayData ba; + char data[N + 1]; }; -template<int N> struct QConstByteArrayDataPtr +template<int N> struct QStaticByteArrayDataPtr { - const QConstByteArrayData<N> *ptr; + const QStaticByteArrayData<N> *ptr; }; #if defined(Q_COMPILER_LAMBDA) -# define QByteArrayLiteral(str) ([]() -> QConstByteArrayDataPtr<sizeof(str) - 1> { \ +# define QByteArrayLiteral(str) ([]() -> QStaticByteArrayDataPtr<sizeof(str) - 1> { \ enum { Size = sizeof(str) - 1 }; \ - static const QConstByteArrayData<Size> qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, str }; \ - QConstByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ + static const QStaticByteArrayData<Size> qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QByteArrayData) }, str }; \ + QStaticByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ return holder; }()) #elif defined(Q_CC_GNU) @@ -160,9 +159,9 @@ template<int N> struct QConstByteArrayDataPtr # define QByteArrayLiteral(str) \ __extension__ ({ \ enum { Size = sizeof(str) - 1 }; \ - static const QConstByteArrayData<Size> qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, str }; \ - QConstByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ + static const QStaticByteArrayData<Size> qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QByteArrayData) }, str }; \ + QStaticByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ holder; }) #endif @@ -180,8 +179,7 @@ private: public: inline QByteArray(); - QByteArray(const char *); - QByteArray(const char *, int size); + QByteArray(const char *, int size = -1); QByteArray(int size, char c); QByteArray(int size, Qt::Initialization); inline QByteArray(const QByteArray &); @@ -379,16 +377,16 @@ public: bool isNull() const; template <int n> - inline QByteArray(const QConstByteArrayData<n> &dd) + inline QByteArray(const QStaticByteArrayData<n> &dd) : d(const_cast<QByteArrayData *>(&dd.ba)) {} template <int N> - Q_DECL_CONSTEXPR inline QByteArray(QConstByteArrayDataPtr<N> dd) + Q_DECL_CONSTEXPR inline QByteArray(QStaticByteArrayDataPtr<N> dd) : d(const_cast<QByteArrayData *>(&dd.ptr->ba)) {} private: operator QNoImplicitBoolCast() const; - static const QConstByteArrayData<1> shared_null; - static const QConstByteArrayData<1> shared_empty; + static const QStaticByteArrayData<1> shared_null; + static const QStaticByteArrayData<1> shared_empty; Data *d; QByteArray(Data *dd, int /*dummy*/, int /*dummy*/) : d(dd) {} void realloc(int alloc); @@ -424,9 +422,9 @@ inline const char *QByteArray::data() const inline const char *QByteArray::constData() const { return d->data(); } inline void QByteArray::detach() -{ if (d->ref != 1 || d->offset) realloc(d->size); } +{ if (d->ref.isShared() || (d->offset != sizeof(QByteArrayData))) realloc(d->size); } inline bool QByteArray::isDetached() const -{ return d->ref == 1; } +{ return !d->ref.isShared(); } inline QByteArray::QByteArray(const QByteArray &a) : d(a.d) { d->ref.ref(); } @@ -435,7 +433,7 @@ inline int QByteArray::capacity() const inline void QByteArray::reserve(int asize) { - if (d->ref != 1 || asize > int(d->alloc)) + if (d->ref.isShared() || asize > int(d->alloc)) realloc(asize); if (!d->capacityReserved) { @@ -446,11 +444,12 @@ inline void QByteArray::reserve(int asize) inline void QByteArray::squeeze() { - if (d->ref > 1 || d->size < int(d->alloc)) + if (d->ref.isShared() || d->size < int(d->alloc)) realloc(d->size); if (d->capacityReserved) { - // cannot set unconditionally, since d could be the shared_null/shared_empty (which is const) + // cannot set unconditionally, since d could be shared_null or + // otherwise static. d->capacityReserved = false; } } diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index d5703e8b2a..897de77f2e 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -166,7 +166,7 @@ static int countBits(int hint) const int MinNumBits = 4; const QHashData QHashData::shared_null = { - 0, 0, Q_REFCOUNT_INITIALIZER(-1), 0, 0, MinNumBits, 0, 0, true, false, 0 + 0, 0, Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, MinNumBits, 0, 0, true, false, 0 }; void *QHashData::allocateNode(int nodeAlign) @@ -196,7 +196,7 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), d = new QHashData; d->fakeNext = 0; d->buckets = 0; - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->nodeSize = nodeSize; d->userNumBits = userNumBits; diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 9e4007c26e..91d015c394 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -288,8 +288,8 @@ public: void reserve(int size); inline void squeeze() { reserve(1); } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline void detach() { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QHashData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QHash<Key, T> &other) const { return d == other.d; } diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index b31ef3e5e9..6e66f804c0 100644 --- a/src/corelib/tools/qlinkedlist.cpp +++ b/src/corelib/tools/qlinkedlist.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE const QLinkedListData QLinkedListData::shared_null = { const_cast<QLinkedListData *>(&QLinkedListData::shared_null), const_cast<QLinkedListData *>(&QLinkedListData::shared_null), - Q_REFCOUNT_INITIALIZER(-1), 0, true + Q_REFCOUNT_INITIALIZE_STATIC, 0, true }; /*! \class QLinkedList diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 2e6d05ac35..28f190c7fa 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -94,8 +94,8 @@ public: inline int size() const { return d->size; } inline void detach() - { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QLinkedListData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QLinkedList<T> &other) const { return d == other.d; } @@ -241,8 +241,6 @@ private: template <typename T> inline QLinkedList<T>::~QLinkedList() { - if (!d) - return; if (!d->ref.deref()) free(d); } @@ -252,7 +250,7 @@ void QLinkedList<T>::detach_helper() { union { QLinkedListData *d; Node *e; } x; x.d = new QLinkedListData; - x.d->ref = 1; + x.d->ref.initializeOwned(); x.d->size = d->size; x.d->sharable = true; Node *original = e->n; @@ -265,6 +263,7 @@ void QLinkedList<T>::detach_helper() copy = copy->n; } QT_CATCH(...) { copy->n = x.e; + Q_ASSERT(!x.d->ref.deref()); // Don't trigger assert in free free(x.d); QT_RETHROW; } @@ -281,14 +280,13 @@ void QLinkedList<T>::free(QLinkedListData *x) { Node *y = reinterpret_cast<Node*>(x); Node *i = y->n; - if (x->ref == 0) { - while(i != y) { - Node *n = i; - i = i->n; - delete n; - } - delete x; + Q_ASSERT(x->ref.atomic.load() == 0); + while (i != y) { + Node *n = i; + i = i->n; + delete n; } + delete x; } template <typename T> diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 263045a25d..9ee4c0a797 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE the number of elements in the list. */ -const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, true, { 0 } }; +const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }; static int grow(int size) { @@ -87,8 +87,7 @@ QListData::Data *QListData::detach_grow(int *idx, int num) Data* t = static_cast<Data *>(::malloc(DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(t); - t->ref = 1; - t->sharable = true; + t->ref.initializeOwned(); t->alloc = alloc; // The space reservation algorithm's optimization is biased towards appending: // Something which looks like an append will put the data at the beginning, @@ -129,8 +128,7 @@ QListData::Data *QListData::detach(int alloc) Data* t = static_cast<Data *>(::malloc(DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(t); - t->ref = 1; - t->sharable = true; + t->ref.initializeOwned(); t->alloc = alloc; if (!alloc) { t->begin = 0; @@ -146,7 +144,7 @@ QListData::Data *QListData::detach(int alloc) void QListData::realloc(int alloc) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); Data *x = static_cast<Data *>(::realloc(d, DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(x); @@ -159,7 +157,7 @@ void QListData::realloc(int alloc) // ensures that enough space is available to append n elements void **QListData::append(int n) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); int e = d->end; if (e + n > d->alloc) { int b = d->begin; @@ -190,7 +188,7 @@ void **QListData::append(const QListData& l) void **QListData::prepend() { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (d->begin == 0) { if (d->end >= d->alloc / 3) realloc(grow(d->alloc + 1)); @@ -208,7 +206,7 @@ void **QListData::prepend() void **QListData::insert(int i) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (i <= 0) return prepend(); int size = d->end - d->begin; @@ -247,7 +245,7 @@ void **QListData::insert(int i) void QListData::remove(int i) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); i += d->begin; if (i - d->begin < d->end - i) { if (int offset = i - d->begin) @@ -262,7 +260,7 @@ void QListData::remove(int i) void QListData::remove(int i, int n) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); i += d->begin; int middle = i + n/2; if (middle - d->begin < d->end - middle) { @@ -278,7 +276,7 @@ void QListData::remove(int i, int n) void QListData::move(int from, int to) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (from == to) return; @@ -318,7 +316,7 @@ void QListData::move(int from, int to) void **QListData::erase(void **xi) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); int i = xi - (d->array + d->begin); remove(i); return d->array + d->begin + i; diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index bf6933732c..bc3350f42b 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -71,7 +71,6 @@ struct Q_CORE_EXPORT QListData { struct Data { QtPrivate::RefCount ref; int alloc, begin, end; - uint sharable : 1; void *array[1]; }; enum { DataHeaderSize = sizeof(Data) - sizeof(void *) }; @@ -114,7 +113,7 @@ class QList public: inline QList() : d(const_cast<QListData::Data *>(&QListData::shared_null)) { } - inline QList(const QList<T> &l) : d(l.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } + QList(const QList<T> &l); ~QList(); QList<T> &operator=(const QList<T> &l); #ifdef Q_COMPILER_RVALUE_REFS @@ -132,17 +131,25 @@ public: inline int size() const { return p.size(); } - inline void detach() { if (d->ref != 1) detach_helper(); } + inline void detach() { if (d->ref.isShared()) detach_helper(); } inline void detachShared() { // The "this->" qualification is needed for GCCE. - if (d->ref != 1 && this->d != &QListData::shared_null) + if (d->ref.isShared() && this->d != &QListData::shared_null) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } - inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QListData::shared_null) d->sharable = sharable; } + inline bool isDetached() const { return !d->ref.isShared(); } + inline void setSharable(bool sharable) + { + if (sharable == d->ref.isSharable()) + return; + if (!sharable) + detach(); + if (d != &QListData::shared_null) + d->ref.setSharable(sharable); + } inline bool isSharedWith(const QList<T> &other) const { return d == other.d; } inline bool isEmpty() const { return p.isEmpty(); } @@ -419,13 +426,8 @@ template <typename T> Q_INLINE_TEMPLATE QList<T> &QList<T>::operator=(const QList<T> &l) { if (d != l.d) { - QListData::Data *o = l.d; - o->ref.ref(); - if (!d->ref.deref()) - dealloc(d); - d = o; - if (!d->sharable) - detach_helper(); + QList<T> tmp(l); + tmp.swap(*this); } return *this; } @@ -478,7 +480,7 @@ template <typename T> Q_OUTOFLINE_TEMPLATE void QList<T>::reserve(int alloc) { if (d->alloc < alloc) { - if (d->ref != 1) + if (d->ref.isShared()) detach_helper(alloc); else p.realloc(alloc); @@ -488,7 +490,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::reserve(int alloc) template <typename T> Q_OUTOFLINE_TEMPLATE void QList<T>::append(const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(INT_MAX, 1); QT_TRY { node_construct(n, t); @@ -522,7 +524,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::append(const T &t) template <typename T> inline void QList<T>::prepend(const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(0, 1); QT_TRY { node_construct(n, t); @@ -556,7 +558,7 @@ inline void QList<T>::prepend(const T &t) template <typename T> inline void QList<T>::insert(int i, const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(i, 1); QT_TRY { node_construct(n, t); @@ -708,6 +710,28 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::detach_helper() } template <typename T> +Q_OUTOFLINE_TEMPLATE QList<T>::QList(const QList<T> &l) + : d(l.d) +{ + if (!d->ref.ref()) { + p.detach(d->alloc); + + struct Cleanup + { + Cleanup(QListData::Data *d) : d_(d) {} + ~Cleanup() { if (d_) qFree(d_); } + + QListData::Data *d_; + } tryCatch(d); + + node_copy(reinterpret_cast<Node *>(p.begin()), + reinterpret_cast<Node *>(p.end()), + reinterpret_cast<Node *>(l.p.begin())); + tryCatch.d_ = 0; + } +} + +template <typename T> Q_OUTOFLINE_TEMPLATE QList<T>::~QList() { if (!d->ref.deref()) @@ -802,7 +826,7 @@ Q_OUTOFLINE_TEMPLATE QList<T> &QList<T>::operator+=(const QList<T> &l) if (isEmpty()) { *this = l; } else { - Node *n = (d->ref != 1) + Node *n = (d->ref.isShared()) ? detach_helper_grow(INT_MAX, l.size()) : reinterpret_cast<Node *>(p.append(l.p)); QT_TRY { diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 103d074941..c922d7aab0 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE const QMapData QMapData::shared_null = { const_cast<QMapData *>(&shared_null), { const_cast<QMapData *>(&shared_null), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, false, true, false, 0 + Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, false, true, false, 0 }; QMapData *QMapData::createData(int alignment) @@ -63,7 +63,7 @@ QMapData *QMapData::createData(int alignment) Node *e = reinterpret_cast<Node *>(d); e->backward = e; e->forward[0] = e; - d->ref = 1; + d->ref.initializeOwned(); d->topLevel = 0; d->size = 0; d->randomBits = 0; diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 515b60bea6..dc358a8106 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -179,7 +179,7 @@ public: inline QMap() : d(const_cast<QMapData *>(&QMapData::shared_null)) { } inline QMap(const QMap<Key, T> &other) : d(other.d) { d->ref.ref(); if (!d->sharable) detach(); } - inline ~QMap() { if (!d) return; if (!d->ref.deref()) freeData(d); } + inline ~QMap() { if (!d->ref.deref()) freeData(d); } QMap<Key, T> &operator=(const QMap<Key, T> &other); #ifdef Q_COMPILER_RVALUE_REFS @@ -199,8 +199,8 @@ public: inline bool isEmpty() const { return d->size == 0; } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline void detach() { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QMapData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QMap<Key, T> &other) const { return d == other.d; } inline void setInsertInOrder(bool ordered) { if (ordered) detach(); if (d != &QMapData::shared_null) d->insertInOrder = ordered; } diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index bc673214fa..16b1d339b3 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -55,37 +55,61 @@ namespace QtPrivate class RefCount { public: - inline void ref() { - if (atomic.load() > 0) + inline bool ref() { + int count = atomic.load(); + if (count == 0) // !isSharable + return false; + if (count != -1) // !isStatic atomic.ref(); + return true; } inline bool deref() { - if (atomic.load() <= 0) + int count = atomic.load(); + if (count == 0) // !isSharable + return false; + if (count == -1) // isStatic return true; return atomic.deref(); } - inline bool operator==(int value) const - { return atomic.load() == value; } - inline bool operator!=(int value) const - { return atomic.load() != value; } - inline bool operator!() const - { return !atomic.load(); } - inline operator int() const - { return atomic.load(); } - inline RefCount &operator=(int value) - { atomic.store(value); return *this; } - inline RefCount &operator=(const RefCount &other) - { atomic.store(other.atomic.load()); return *this; } + bool setSharable(bool sharable) + { + Q_ASSERT(!isShared()); + if (sharable) + return atomic.testAndSetRelaxed(0, 1); + else + return atomic.testAndSetRelaxed(1, 0); + } + + bool isStatic() const + { + // Persistent object, never deleted + return atomic.load() == -1; + } + + bool isSharable() const + { + // Sharable === Shared ownership. + return atomic.load() != 0; + } + + bool isShared() const + { + int count = atomic.load(); + return (count != 1) && (count != 0); + } + + void initializeOwned() { atomic.store(1); } + void initializeUnsharable() { atomic.store(0); } QBasicAtomicInt atomic; }; -#define Q_REFCOUNT_INITIALIZER(a) { Q_BASIC_ATOMIC_INITIALIZER(a) } - } +#define Q_REFCOUNT_INITIALIZE_STATIC { Q_BASIC_ATOMIC_INITIALIZER(-1) } + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 083abcbaad..58eb711168 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -94,6 +94,8 @@ #define ULLONG_MAX quint64_C(18446744073709551615) #endif +#define IS_RAW_DATA(d) ((d)->offset != sizeof(QStringData)) + QT_BEGIN_NAMESPACE #ifdef QT_USE_ICU @@ -793,8 +795,8 @@ const QString::Null QString::null = { }; \sa split() */ -const QConstStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; -const QConstStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; +const QStaticStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, sizeof(QStringData) }, { 0 } }; +const QStaticStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, sizeof(QStringData) }, { 0 } }; int QString::grow(int size) { @@ -1015,63 +1017,44 @@ int QString::toUcs4_helper(const ushort *uc, int length, uint *out) Constructs a string initialized with the first \a size characters of the QChar array \a unicode. + If \a unicode is 0, a null string is constructed. + + If \a size is negative, \a unicode is assumed to point to a nul-terminated + array and its length is determined dynamically. The terminating + nul-character is not considered part of the string. + QString makes a deep copy of the string data. The unicode data is copied as is and the Byte Order Mark is preserved if present. + + \sa fromRawData() */ QString::QString(const QChar *unicode, int size) { if (!unicode) { d = const_cast<Data *>(&shared_null.str); - } else if (size <= 0) { - d = const_cast<Data *>(&shared_empty.str); } else { - d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = (uint) size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), unicode, size * sizeof(QChar)); - d->data()[size] = '\0'; + if (size < 0) { + size = 0; + while (unicode[size] != 0) + ++size; + } + if (!size) { + d = const_cast<Data *>(&shared_empty.str); + } else { + d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); + Q_CHECK_PTR(d); + d->ref.initializeOwned(); + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = sizeof(QStringData); + memcpy(d->data(), unicode, size * sizeof(QChar)); + d->data()[size] = '\0'; + } } } /*! - \since 4.7 - - Constructs a string initialized with the characters of the QChar array - \a unicode, which must be terminated with a 0. - - QString makes a deep copy of the string data. The unicode data is copied as - is and the Byte Order Mark is preserved if present. -*/ -QString::QString(const QChar *unicode) -{ - if (!unicode) { - d = const_cast<Data *>(&shared_null.str); - } else { - int size = 0; - while (unicode[size] != 0) - ++size; - if (!size) { - d = const_cast<Data *>(&shared_empty.str); - } else { - d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = (uint) size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), unicode, size * sizeof(QChar)); - d->data()[size] = '\0'; - } - } -} - - -/*! Constructs a string of the given \a size with every character set to \a ch. @@ -1084,11 +1067,11 @@ QString::QString(int size, QChar ch) } else { d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; ushort *i = d->data() + size; ushort *b = d->data(); @@ -1108,11 +1091,11 @@ QString::QString(int size, Qt::Initialization) { d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; } @@ -1130,11 +1113,11 @@ QString::QString(QChar ch) { d = (Data *) ::malloc(sizeof(Data) + 2*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = 1; d->alloc = 1; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[0] = ch.unicode(); d->data()[1] = '\0'; } @@ -1232,7 +1215,7 @@ void QString::resize(int size) if (size < 0) size = 0; - if (d->offset && d->ref == 1 && size < d->size) { + if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1243,7 +1226,7 @@ void QString::resize(int size) QString::free(d); d = x; } else { - if (d->ref != 1 || size > int(d->alloc) || + if (d->ref.isShared() || size > int(d->alloc) || (!d->capacityReserved && size < d->size && size < int(d->alloc) >> 1)) realloc(grow(size)); if (int(d->alloc) >= size) { @@ -1306,14 +1289,14 @@ void QString::resize(int size) // ### Qt 5: rename reallocData() to avoid confusion. 197625 void QString::realloc(int alloc) { - if (d->ref != 1 || d->offset) { + if (d->ref.isShared() || IS_RAW_DATA(d)) { Data *x = static_cast<Data *>(::malloc(sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = (uint) alloc; x->capacityReserved = d->capacityReserved; - x->offset =0; + x->offset = sizeof(QStringData); ::memcpy(x->data(), d->data(), x->size * sizeof(QChar)); x->data()[x->size] = 0; if (!d->ref.deref()) @@ -1324,7 +1307,7 @@ void QString::realloc(int alloc) Q_CHECK_PTR(p); d = p; d->alloc = alloc; - d->offset = 0; + d->offset = sizeof(QStringData); } } @@ -1536,7 +1519,7 @@ QString &QString::append(const QString &str) if (d == &shared_null.str) { operator=(str); } else { - if (d->ref != 1 || d->size + str.d->size > int(d->alloc)) + if (d->ref.isShared() || d->size + str.d->size > int(d->alloc)) realloc(grow(d->size + str.d->size)); memcpy(d->data() + d->size, str.d->data(), str.d->size * sizeof(QChar)); d->size += str.d->size; @@ -1556,7 +1539,7 @@ QString &QString::append(const QLatin1String &str) const uchar *s = (const uchar *)str.latin1(); if (s) { int len = str.size(); - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(grow(d->size + len)); ushort *i = d->data() + d->size; while ((*i++ = *s++)) @@ -1599,7 +1582,7 @@ QString &QString::append(const QLatin1String &str) */ QString &QString::append(QChar ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(grow(d->size + 1)); d->data()[d->size++] = ch.unicode(); d->data()[d->size] = '\0'; @@ -3390,15 +3373,11 @@ QString QString::right(int n) const QString QString::mid(int position, int n) const { - if (d == &shared_null.str || position > d->size) + if (position > d->size) return QString(); - if (n < 0) - n = d->size - position; - if (position < 0) { - n += position; + if (position < 0) position = 0; - } - if (n + position > d->size) + if (n < 0 || n > d->size - position) n = d->size - position; if (position == 0 && n == d->size) return *this; @@ -3753,11 +3732,11 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) size = qstrlen(str); d = static_cast<Data *>(::malloc(sizeof(Data) + (size+1) * sizeof(QChar))); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; ushort *dst = d->data(); /* SIMD: @@ -4765,7 +4744,7 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1, const ushort *QString::utf16() const { - if (d->offset) + if (IS_RAW_DATA(d)) const_cast<QString*>(this)->realloc(); // ensure '\\0'-termination for ::fromRawData strings return d->data(); } @@ -7067,11 +7046,11 @@ QString QString::fromRawData(const QChar *unicode, int size) } else { x = static_cast<Data *>(::malloc(sizeof(Data) + sizeof(ushort))); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = 0; x->capacityReserved = false; - x->offset = (const ushort *)unicode - (x->d + sizeof(qptrdiff)/sizeof(ushort)); + x->offset = reinterpret_cast<const char *>(unicode) - reinterpret_cast<char *>(x); } return QString(x, 0); } @@ -7092,14 +7071,14 @@ QString QString::fromRawData(const QChar *unicode, int size) */ QString &QString::setRawData(const QChar *unicode, int size) { - if (d->ref != 1 || d->alloc) { + if (d->ref.isShared() || d->alloc) { *this = fromRawData(unicode, size); } else { if (unicode) { d->size = size; - d->offset = (const ushort *)unicode - (d->d + sizeof(qptrdiff)/sizeof(ushort)); + d->offset = reinterpret_cast<const char *>(unicode) - reinterpret_cast<char *>(d); } else { - d->offset = 0; + d->offset = sizeof(QStringData); d->size = 0; } } @@ -8059,15 +8038,11 @@ QStringRef QString::rightRef(int n) const QStringRef QString::midRef(int position, int n) const { - if (d == &shared_null.str || position > d->size) + if (position > d->size) return QStringRef(); - if (n < 0) - n = d->size - position; - if (position < 0) { - n += position; + if (position < 0) position = 0; - } - if (n + position > d->size) + if (n < 0 || n > d->size - position) n = d->size - position; return QStringRef(this, position, n); } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 4d02fbe66d..26959d81f3 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -75,25 +75,24 @@ struct QStringData { int size; uint alloc : 31; uint capacityReserved : 1; - union { - qptrdiff offset; // will always work as we add/subtract from a ushort ptr - ushort d[sizeof(qptrdiff)/sizeof(ushort)]; - }; - inline ushort *data() { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } - inline const ushort *data() const { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } + + qptrdiff offset; + + inline ushort *data() { return reinterpret_cast<ushort *>(reinterpret_cast<char *>(this) + offset); } + inline const ushort *data() const { return reinterpret_cast<const ushort *>(reinterpret_cast<const char *>(this) + offset); } }; -template<int N> struct QConstStringData; -template<int N> struct QConstStringDataPtr +template<int N> struct QStaticStringData; +template<int N> struct QStaticStringDataPtr { - const QConstStringData<N> *ptr; + const QStaticStringData<N> *ptr; }; #if defined(Q_COMPILER_UNICODE_STRINGS) -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const char16_t data[N + 1]; + QStringData str; + char16_t data[N + 1]; }; #define QT_UNICODE_LITERAL_II(str) u"" str @@ -102,10 +101,10 @@ template<int N> struct QConstStringData || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) \ || (!defined(__SIZEOF_WCHAR_T__) && defined(WCHAR_MAX) && (WCHAR_MAX - 0 < 65536)) // wchar_t is 2 bytes -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const wchar_t data[N + 1]; + QStringData str; + wchar_t data[N + 1]; }; #if defined(Q_CC_MSVC) @@ -115,21 +114,21 @@ template<int N> struct QConstStringData #endif #else -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const ushort data[N + 1]; + QStringData str; + ushort data[N + 1]; }; #endif #if defined(QT_UNICODE_LITERAL_II) # define QT_UNICODE_LITERAL(str) QT_UNICODE_LITERAL_II(str) # if defined(Q_COMPILER_LAMBDA) -# define QStringLiteral(str) ([]() -> QConstStringDataPtr<sizeof(QT_UNICODE_LITERAL(str))/2 - 1> { \ +# define QStringLiteral(str) ([]() -> QStaticStringDataPtr<sizeof(QT_UNICODE_LITERAL(str))/2 - 1> { \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ - static const QConstStringData<Size> qstring_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ - QConstStringDataPtr<Size> holder = { &qstring_literal }; \ + static const QStaticStringData<Size> qstring_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QStringData) }, QT_UNICODE_LITERAL(str) }; \ + QStaticStringDataPtr<Size> holder = { &qstring_literal }; \ return holder; }()) # elif defined(Q_CC_GNU) @@ -140,9 +139,9 @@ template<int N> struct QConstStringData # define QStringLiteral(str) \ __extension__ ({ \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ - static const QConstStringData<Size> qstring_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ - QConstStringDataPtr<Size> holder = { &qstring_literal }; \ + static const QStaticStringData<Size> qstring_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QStringData) }, QT_UNICODE_LITERAL(str) }; \ + QStaticStringDataPtr<Size> holder = { &qstring_literal }; \ holder; }) # endif #endif @@ -160,8 +159,7 @@ public: typedef QStringData Data; inline QString(); - QString(const QChar *unicode, int size); // Qt5: don't cap size < 0 - explicit QString(const QChar *unicode); // Qt5: merge with the above + explicit QString(const QChar *unicode, int size = -1); QString(QChar c); QString(int size, QChar c); inline QString(const QLatin1String &latin1); @@ -340,7 +338,7 @@ public: inline QString &prepend(const QLatin1String &s) { return insert(0, s); } inline QString &operator+=(QChar c) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(grow(d->size + 1)); d->data()[d->size++] = c.unicode(); d->data()[d->size] = '\0'; @@ -592,9 +590,9 @@ public: QString(int size, Qt::Initialization); template <int n> - inline QString(const QConstStringData<n> &dd) : d(const_cast<QStringData *>(&dd.str)) {} + inline QString(const QStaticStringData<n> &dd) : d(const_cast<QStringData *>(&dd.str)) {} template <int N> - Q_DECL_CONSTEXPR inline QString(QConstStringDataPtr<N> dd) : d(const_cast<QStringData *>(&dd.ptr->str)) {} + Q_DECL_CONSTEXPR inline QString(QStaticStringDataPtr<N> dd) : d(const_cast<QStringData *>(&dd.ptr->str)) {} private: #if defined(QT_NO_CAST_FROM_ASCII) && !defined(Q_NO_DECLARED_NOT_DEFINED) @@ -606,8 +604,8 @@ private: QString &operator=(const QByteArray &a); #endif - static const QConstStringData<1> shared_null; - static const QConstStringData<1> shared_empty; + static const QStaticStringData<1> shared_null; + static const QStaticStringData<1> shared_empty; Data *d; inline QString(Data *dd, int /*dummy*/) : d(dd) {} @@ -709,9 +707,9 @@ inline QChar *QString::data() inline const QChar *QString::constData() const { return reinterpret_cast<const QChar*>(d->data()); } inline void QString::detach() -{ if (d->ref != 1 || d->offset) realloc(); } +{ if (d->ref.isShared() || (d->offset != sizeof(QStringData))) realloc(); } inline bool QString::isDetached() const -{ return d->ref == 1; } +{ return !d->ref.isShared(); } inline QString &QString::operator=(const QLatin1String &s) { *this = fromLatin1(s.latin1(), s.size()); @@ -874,7 +872,7 @@ inline QString::~QString() { if (!d->ref.deref()) free(d); } inline void QString::reserve(int asize) { - if (d->ref != 1 || asize > int(d->alloc)) + if (d->ref.isShared() || asize > int(d->alloc)) realloc(asize); if (!d->capacityReserved) { @@ -885,11 +883,12 @@ inline void QString::reserve(int asize) inline void QString::squeeze() { - if (d->ref > 1 || d->size < int(d->alloc)) + if (d->ref.isShared() || d->size < int(d->alloc)) realloc(); if (d->capacityReserved) { - // cannot set unconditionally, since d could be the shared_null/shared_empty (which is const) + // cannot set unconditionally, since d could be shared_null or + // otherwise static. d->capacityReserved = false; } } diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index e34425be7e..0eb8aa8903 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -219,9 +219,9 @@ template <> struct QConcatenable<QString> : private QAbstractConcatenable } }; -template <int N> struct QConcatenable<QConstStringDataPtr<N> > : private QAbstractConcatenable +template <int N> struct QConcatenable<QStaticStringDataPtr<N> > : private QAbstractConcatenable { - typedef QConstStringDataPtr<N> type; + typedef QStaticStringDataPtr<N> type; typedef QString ConvertTo; enum { ExactSize = true }; static int size(const type &) { return N; } @@ -324,9 +324,9 @@ template <> struct QConcatenable<QByteArray> : private QAbstractConcatenable } }; -template <int N> struct QConcatenable<QConstByteArrayDataPtr<N> > : private QAbstractConcatenable +template <int N> struct QConcatenable<QStaticByteArrayDataPtr<N> > : private QAbstractConcatenable { - typedef QConstByteArrayDataPtr<N> type; + typedef QStaticByteArrayDataPtr<N> type; typedef QByteArray ConvertTo; enum { ExactSize = false }; static int size(const type &) { return N; } @@ -338,9 +338,8 @@ template <int N> struct QConcatenable<QConstByteArrayDataPtr<N> > : private QAbs #endif static inline void appendTo(const type &ba, char *&out) { - const char *a = ba.ptr->data; - while (*a) - *out++ = *a++; + ::memcpy(out, ba.ptr->data, N); + out += N; } }; diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 75c219bbc9..1a746dc061 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -54,15 +54,7 @@ static inline int alignmentThreshold() return 2 * sizeof(void*); } -const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, true, false, 0 }; - -QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) -{ - QVectorData* p = (QVectorData *)::malloc(sizeofTypedData + (size - 1) * sizeofT); - Q_CHECK_PTR(p); - ::memcpy(p, init, sizeofTypedData + (qMin(size, init->alloc) - 1) * sizeofT); - return p; -} +const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, 0 }; QVectorData *QVectorData::allocate(int size, int alignment) { @@ -84,11 +76,9 @@ void QVectorData::free(QVectorData *x, int alignment) ::free(x); } -int QVectorData::grow(int sizeofTypedData, int size, int sizeofT, bool excessive) +int QVectorData::grow(int sizeOfHeader, int size, int sizeOfT) { - if (excessive) - return size + size / 2; - return qAllocMore(size * sizeofT, sizeofTypedData - sizeofT) / sizeofT; + return qAllocMore(size * sizeOfT, sizeOfHeader) / sizeOfT; } /*! diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 4230e55ff5..c119ef43ae 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -65,37 +65,28 @@ QT_BEGIN_NAMESPACE struct Q_CORE_EXPORT QVectorData { QtPrivate::RefCount ref; - int alloc; int size; -#if defined(Q_PROCESSOR_SPARC) && defined(Q_CC_GNU) && defined(__LP64__) && defined(QT_BOOTSTRAPPED) - // workaround for bug in gcc 3.4.2 - uint sharable; - uint capacity; - uint reserved; -#else - uint sharable : 1; - uint capacity : 1; - uint reserved : 30; -#endif + uint alloc : 31; + uint capacityReserved : 1; + + qptrdiff offset; + + void* data() { return reinterpret_cast<char *>(this) + this->offset; } static const QVectorData shared_null; - // ### Qt 5: rename to 'allocate()'. The current name causes problems for - // some debugges when the QVector is member of a class within an unnamed namespace. - // ### Qt 5: can be removed completely. (Ralf) - static QVectorData *malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init); static QVectorData *allocate(int size, int alignment); static QVectorData *reallocate(QVectorData *old, int newsize, int oldsize, int alignment); static void free(QVectorData *data, int alignment); - static int grow(int sizeofTypedData, int size, int sizeofT, bool excessive); + static int grow(int sizeOfHeader, int size, int sizeOfT); }; template <typename T> -struct QVectorTypedData : private QVectorData -{ // private inheritance as we must not access QVectorData member thought QVectorTypedData - // as this would break strict aliasing rules. (in the case of shared_null) - T array[1]; +struct QVectorTypedData : QVectorData +{ + T* begin() { return reinterpret_cast<T *>(this->data()); } + T* end() { return begin() + this->size; } - static inline void free(QVectorTypedData<T> *x, int alignment) { QVectorData::free(static_cast<QVectorData *>(x), alignment); } + static QVectorTypedData *sharedNull() { return static_cast<QVectorTypedData *>(const_cast<QVectorData *>(&QVectorData::shared_null)); } }; class QRegion; @@ -104,27 +95,30 @@ template <typename T> class QVector { typedef QVectorTypedData<T> Data; - union { - QVectorData *d; -#if defined(Q_CC_SUN) && (__SUNPRO_CC <= 0x550) - QVectorTypedData<T> *p; -#else - Data *p; -#endif - }; + Data *d; public: - // ### Qt 5: Consider making QVector non-shared to get at least one - // "really fast" container. See tests/benchmarks/corelib/tools/qvector/ - inline QVector() : d(const_cast<QVectorData *>(&QVectorData::shared_null)) { } + inline QVector() : d(Data::sharedNull()) { } explicit QVector(int size); QVector(int size, const T &t); - inline QVector(const QVector<T> &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } - inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(p); } + inline QVector(const QVector<T> &v) + { + if (v.d->ref.ref()) { + d = v.d; + } else { + d = Data::sharedNull(); + realloc(0, int(v.d->alloc)); + qCopy(v.d->begin(), v.d->end(), d->begin()); + d->size = v.d->size; + d->capacityReserved = v.d->capacityReserved; + } + } + + inline ~QVector() { if (!d->ref.deref()) free(d); } QVector<T> &operator=(const QVector<T> &v); #ifdef Q_COMPILER_RVALUE_REFS inline QVector<T> operator=(QVector<T> &&other) - { qSwap(p, other.p); return *this; } + { qSwap(d, other.d); return *this; } #endif inline void swap(QVector<T> &other) { qSwap(d, other.d); } #ifdef Q_COMPILER_INITIALIZER_LISTS @@ -139,18 +133,27 @@ public: void resize(int size); - inline int capacity() const { return d->alloc; } + inline int capacity() const { return int(d->alloc); } void reserve(int size); - inline void squeeze() { realloc(d->size, d->size); d->capacity = 0; } + inline void squeeze() { realloc(d->size, d->size); d->capacityReserved = 0; } + + inline void detach() { if (!isDetached()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } + inline void setSharable(bool sharable) + { + if (sharable == d->ref.isSharable()) + return; + if (!sharable) + detach(); + if (d != Data::sharedNull()) + d->ref.setSharable(sharable); + } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } - inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QVectorData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QVector<T> &other) const { return d == other.d; } - inline T *data() { detach(); return p->array; } - inline const T *data() const { return p->array; } - inline const T *constData() const { return p->array; } + inline T *data() { detach(); return d->begin(); } + inline const T *data() const { return d->begin(); } + inline const T *constData() const { return d->begin(); } void clear(); const T &at(int i) const; @@ -243,12 +246,12 @@ public: typedef T* iterator; typedef const T* const_iterator; #endif - inline iterator begin() { detach(); return p->array; } - inline const_iterator begin() const { return p->array; } - inline const_iterator constBegin() const { return p->array; } - inline iterator end() { detach(); return p->array + d->size; } - inline const_iterator end() const { return p->array + d->size; } - inline const_iterator constEnd() const { return p->array + d->size; } + inline iterator begin() { detach(); return d->begin(); } + inline const_iterator begin() const { return d->begin(); } + inline const_iterator constBegin() const { return d->begin(); } + inline iterator end() { detach(); return d->end(); } + inline const_iterator end() const { return d->end(); } + inline const_iterator constEnd() const { return d->end(); } iterator insert(iterator before, int n, const T &x); inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } iterator erase(iterator begin, iterator end); @@ -313,46 +316,49 @@ private: friend class QRegion; // Optimization for QRegion::rects() void detach_helper(); - QVectorData *malloc(int alloc); + Data *malloc(int alloc); void realloc(int size, int alloc); void free(Data *d); - int sizeOfTypedData() { - // this is more or less the same as sizeof(Data), except that it doesn't - // count the padding at the end - return reinterpret_cast<const char *>(&(reinterpret_cast<const Data *>(this))->array[1]) - reinterpret_cast<const char *>(this); + + class AlignmentDummy { QVectorData header; T array[1]; }; + + static Q_DECL_CONSTEXPR int offsetOfTypedData() + { + // (non-POD)-safe offsetof(AlignmentDummy, array) + return (sizeof(QVectorData) + (alignOfTypedData() - 1)) & ~(alignOfTypedData() - 1); } - inline int alignOfTypedData() const + static Q_DECL_CONSTEXPR int alignOfTypedData() { #ifdef Q_ALIGNOF - return qMax<int>(sizeof(void*), Q_ALIGNOF(Data)); + return Q_ALIGNOF(AlignmentDummy); #else - return 0; + return sizeof(void *); #endif } }; template <typename T> void QVector<T>::detach_helper() -{ realloc(d->size, d->alloc); } +{ realloc(d->size, int(d->alloc)); } template <typename T> void QVector<T>::reserve(int asize) -{ if (asize > d->alloc) realloc(d->size, asize); if (d->ref == 1) d->capacity = 1; } +{ if (asize > int(d->alloc)) realloc(d->size, asize); if (isDetached()) d->capacityReserved = 1; } template <typename T> void QVector<T>::resize(int asize) -{ realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ? - QVectorData::grow(sizeOfTypedData(), asize, sizeof(T), QTypeInfo<T>::isStatic) - : d->alloc); } +{ realloc(asize, (asize > int(d->alloc) || (!d->capacityReserved && asize < d->size && asize < int(d->alloc >> 1))) ? + QVectorData::grow(offsetOfTypedData(), asize, sizeof(T)) + : int(d->alloc)); } template <typename T> inline void QVector<T>::clear() { *this = QVector<T>(); } template <typename T> inline const T &QVector<T>::at(int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::at", "index out of range"); - return p->array[i]; } + return d->begin()[i]; } template <typename T> inline const T &QVector<T>::operator[](int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); - return p->array[i]; } + return d->begin()[i]; } template <typename T> inline T &QVector<T>::operator[](int i) { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); @@ -388,39 +394,37 @@ inline void QVector<T>::replace(int i, const T &t) template <typename T> QVector<T> &QVector<T>::operator=(const QVector<T> &v) { - QVectorData *o = v.d; - o->ref.ref(); - if (!d->ref.deref()) - free(p); - d = o; - if (!d->sharable) - detach_helper(); + if (v.d != d) { + QVector<T> tmp(v); + tmp.swap(*this); + } return *this; } template <typename T> -inline QVectorData *QVector<T>::malloc(int aalloc) +inline typename QVector<T>::Data *QVector<T>::malloc(int aalloc) { - QVectorData *vectordata = QVectorData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *vectordata = QVectorData::allocate(offsetOfTypedData() + aalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(vectordata); - return vectordata; + return static_cast<Data *>(vectordata); } template <typename T> QVector<T>::QVector(int asize) { d = malloc(asize); - d->ref = 1; - d->alloc = d->size = asize; - d->sharable = true; - d->capacity = false; + d->ref.initializeOwned(); + d->size = asize; + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); if (QTypeInfo<T>::isComplex) { - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) new (--i) T; } else { - qMemSet(p->array, 0, asize * sizeof(T)); + qMemSet(d->begin(), 0, asize * sizeof(T)); } } @@ -428,12 +432,13 @@ template <typename T> QVector<T>::QVector(int asize, const T &t) { d = malloc(asize); - d->ref = 1; - d->alloc = d->size = asize; - d->sharable = true; - d->capacity = false; - T* i = p->array + d->size; - while (i != p->array) + d->ref.initializeOwned(); + d->size = asize; + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); + T* i = d->end(); + while (i != d->begin()) new (--i) T(t); } @@ -442,14 +447,22 @@ template <typename T> QVector<T>::QVector(std::initializer_list<T> args) { d = malloc(int(args.size())); - d->ref = 1; - d->alloc = d->size = int(args.size()); - d->sharable = true; - d->capacity = false; - T* i = p->array + d->size; - auto it = args.end(); - while (i != p->array) - new (--i) T(*(--it)); + d->ref.initializeOwned(); + d->size = int(args.size()); + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); + if (QTypeInfo<T>::isComplex) { + T* b = d->begin(); + T* i = d->end(); + const T* s = args.end(); + while (i != b) + new(--i) T(*--s); + } else { + // std::initializer_list<T>::iterator is guaranteed to be + // const T* ([support.initlist]/1), so can be memcpy'ed away from: + ::memcpy(d->begin(), args.begin(), args.size() * sizeof(T)); + } } #endif @@ -457,14 +470,12 @@ template <typename T> void QVector<T>::free(Data *x) { if (QTypeInfo<T>::isComplex) { - T* b = x->array; - union { QVectorData *d; Data *p; } u; - u.p = x; - T* i = b + u.d->size; + T* b = x->begin(); + T* i = b + x->size; while (i-- != b) i->~T(); } - x->free(x, alignOfTypedData()); + Data::free(x, alignOfTypedData()); } template <typename T> @@ -473,84 +484,82 @@ void QVector<T>::realloc(int asize, int aalloc) Q_ASSERT(asize <= aalloc); T *pOld; T *pNew; - union { QVectorData *d; Data *p; } x; - x.d = d; + Data *x = d; - if (QTypeInfo<T>::isComplex && asize < d->size && d->ref == 1 ) { + if (QTypeInfo<T>::isComplex && asize < d->size && isDetached()) { // call the destructor on all objects that need to be // destroyed when shrinking - pOld = p->array + d->size; - pNew = p->array + asize; + pOld = d->begin() + d->size; + pNew = d->begin() + asize; while (asize < d->size) { (--pOld)->~T(); d->size--; } } - if (aalloc != d->alloc || d->ref != 1) { + if (aalloc != int(d->alloc) || !isDetached()) { // (re)allocate memory if (QTypeInfo<T>::isStatic) { - x.d = malloc(aalloc); - Q_CHECK_PTR(x.p); - x.d->size = 0; - } else if (d->ref != 1) { - x.d = malloc(aalloc); - Q_CHECK_PTR(x.p); + x = malloc(aalloc); + Q_CHECK_PTR(x); + x->size = 0; + } else if (!isDetached()) { + x = malloc(aalloc); + Q_CHECK_PTR(x); if (QTypeInfo<T>::isComplex) { - x.d->size = 0; + x->size = 0; } else { - ::memcpy(x.p, p, sizeOfTypedData() + (qMin(aalloc, d->alloc) - 1) * sizeof(T)); - x.d->size = d->size; + ::memcpy(x, d, offsetOfTypedData() + qMin(uint(aalloc), d->alloc) * sizeof(T)); + x->size = d->size; } } else { QT_TRY { - QVectorData *mem = QVectorData::reallocate(d, sizeOfTypedData() + (aalloc - 1) * sizeof(T), - sizeOfTypedData() + (d->alloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *mem = QVectorData::reallocate(d, offsetOfTypedData() + aalloc * sizeof(T), + offsetOfTypedData() + d->alloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(mem); - x.d = d = mem; - x.d->size = d->size; + x = d = static_cast<Data *>(mem); + x->size = d->size; } QT_CATCH (const std::bad_alloc &) { - if (aalloc > d->alloc) // ignore the error in case we are just shrinking. + if (aalloc > int(d->alloc)) // ignore the error in case we are just shrinking. QT_RETHROW; } } - x.d->ref = 1; - x.d->alloc = aalloc; - x.d->sharable = true; - x.d->capacity = d->capacity; - x.d->reserved = 0; + x->ref.initializeOwned(); + x->alloc = uint(aalloc); + x->capacityReserved = d->capacityReserved; + x->offset = offsetOfTypedData(); } if (QTypeInfo<T>::isComplex) { QT_TRY { - pOld = p->array + x.d->size; - pNew = x.p->array + x.d->size; + pOld = d->begin() + x->size; + pNew = x->begin() + x->size; // copy objects from the old array into the new array const int toMove = qMin(asize, d->size); - while (x.d->size < toMove) { + while (x->size < toMove) { new (pNew++) T(*pOld++); - x.d->size++; + x->size++; } // construct all new objects when growing - while (x.d->size < asize) { + while (x->size < asize) { new (pNew++) T; - x.d->size++; + x->size++; } } QT_CATCH (...) { - free(x.p); + free(x); QT_RETHROW; } - } else if (asize > x.d->size) { + } else if (asize > x->size) { // initialize newly allocated memory to 0 - qMemSet(x.p->array + x.d->size, 0, (asize - x.d->size) * sizeof(T)); + qMemSet(x->end(), 0, (asize - x->size) * sizeof(T)); } - x.d->size = asize; + x->size = asize; - if (d != x.d) { + if (d != x) { if (!d->ref.deref()) - free(p); - d = x.d; + free(d); + d = x; } } @@ -560,31 +569,31 @@ Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i) const if (i < 0 || i >= d->size) { return T(); } - return p->array[i]; + return d->begin()[i]; } template<typename T> Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i, const T &defaultValue) const { - return ((i < 0 || i >= d->size) ? defaultValue : p->array[i]); + return ((i < 0 || i >= d->size) ? defaultValue : d->begin()[i]); } template <typename T> void QVector<T>::append(const T &t) { - if (d->ref != 1 || d->size + 1 > d->alloc) { + if (!isDetached() || d->size + 1 > int(d->alloc)) { const T copy(t); - realloc(d->size, (d->size + 1 > d->alloc) ? - QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T), QTypeInfo<T>::isStatic) - : d->alloc); + realloc(d->size, (d->size + 1 > int(d->alloc)) ? + QVectorData::grow(offsetOfTypedData(), d->size + 1, sizeof(T)) + : int(d->alloc)); if (QTypeInfo<T>::isComplex) - new (p->array + d->size) T(copy); + new (d->end()) T(copy); else - p->array[d->size] = copy; + *d->end() = copy; } else { if (QTypeInfo<T>::isComplex) - new (p->array + d->size) T(t); + new (d->end()) T(t); else - p->array[d->size] = t; + *d->end() = t; } ++d->size; } @@ -592,27 +601,26 @@ void QVector<T>::append(const T &t) template <typename T> typename QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, const T &t) { - int offset = int(before - p->array); + int offset = int(before - d->begin()); if (n != 0) { const T copy(t); - if (d->ref != 1 || d->size + n > d->alloc) - realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T), - QTypeInfo<T>::isStatic)); + if (!isDetached() || d->size + n > int(d->alloc)) + realloc(d->size, QVectorData::grow(offsetOfTypedData(), d->size + n, sizeof(T))); if (QTypeInfo<T>::isStatic) { - T *b = p->array + d->size; - T *i = p->array + d->size + n; + T *b = d->end(); + T *i = d->end() + n; while (i != b) new (--i) T; - i = p->array + d->size; + i = d->end(); T *j = i + n; - b = p->array + offset; + b = d->begin() + offset; while (i != b) *--j = *--i; i = b+n; while (i != b) *--i = copy; } else { - T *b = p->array + offset; + T *b = d->begin() + offset; T *i = b + n; memmove(i, b, (d->size - offset) * sizeof(T)); while (i != b) @@ -620,29 +628,29 @@ typename QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, c } d->size += n; } - return p->array + offset; + return d->begin() + offset; } template <typename T> typename QVector<T>::iterator QVector<T>::erase(iterator abegin, iterator aend) { - int f = int(abegin - p->array); - int l = int(aend - p->array); + int f = int(abegin - d->begin()); + int l = int(aend - d->begin()); int n = l - f; detach(); if (QTypeInfo<T>::isComplex) { - qCopy(p->array+l, p->array+d->size, p->array+f); - T *i = p->array+d->size; - T* b = p->array+d->size-n; + qCopy(d->begin()+l, d->end(), d->begin()+f); + T *i = d->end(); + T* b = d->end()-n; while (i != b) { --i; i->~T(); } } else { - memmove(p->array + f, p->array + l, (d->size-l)*sizeof(T)); + memmove(d->begin() + f, d->begin() + l, (d->size-l)*sizeof(T)); } d->size -= n; - return p->array + f; + return d->begin() + f; } template <typename T> @@ -652,9 +660,9 @@ bool QVector<T>::operator==(const QVector<T> &v) const return false; if (d == v.d) return true; - T* b = p->array; + T* b = d->begin(); T* i = b + d->size; - T* j = v.p->array + d->size; + T* j = v.d->end(); while (i != b) if (!(*--i == *--j)) return false; @@ -667,8 +675,8 @@ QVector<T> &QVector<T>::fill(const T &from, int asize) const T copy(from); resize(asize < 0 ? d->size : asize); if (d->size) { - T *i = p->array + d->size; - T *b = p->array; + T *i = d->end(); + T *b = d->begin(); while (i != b) *--i = copy; } @@ -681,9 +689,9 @@ QVector<T> &QVector<T>::operator+=(const QVector &l) int newSize = d->size + l.d->size; realloc(d->size, newSize); - T *w = p->array + newSize; - T *i = l.p->array + l.d->size; - T *b = l.p->array; + T *w = d->begin() + newSize; + T *i = l.d->end(); + T *b = l.d->begin(); while (i != b) { if (QTypeInfo<T>::isComplex) new (--w) T(*--i); @@ -700,11 +708,11 @@ int QVector<T>::indexOf(const T &t, int from) const if (from < 0) from = qMax(from + d->size, 0); if (from < d->size) { - T* n = p->array + from - 1; - T* e = p->array + d->size; + T* n = d->begin() + from - 1; + T* e = d->end(); while (++n != e) if (*n == t) - return n - p->array; + return n - d->begin(); } return -1; } @@ -717,8 +725,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const else if (from >= d->size) from = d->size-1; if (from >= 0) { - T* b = p->array; - T* n = p->array + from + 1; + T* b = d->begin(); + T* n = d->begin() + from + 1; while (n != b) { if (*--n == t) return n - b; @@ -730,8 +738,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const template <typename T> bool QVector<T>::contains(const T &t) const { - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) if (*--i == t) return true; @@ -742,8 +750,8 @@ template <typename T> int QVector<T>::count(const T &t) const { int c = 0; - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) if (*--i == t) ++c; diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 250789a969..ac347404fd 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -2,6 +2,9 @@ HEADERS += \ tools/qalgorithms.h \ + tools/qarraydata.h \ + tools/qarraydataops.h \ + tools/qarraydatapointer.h \ tools/qbitarray.h \ tools/qbytearray.h \ tools/qbytearraymatcher.h \ @@ -56,6 +59,7 @@ HEADERS += \ SOURCES += \ + tools/qarraydata.cpp \ tools/qbitarray.cpp \ tools/qbytearray.cpp \ tools/qbytearraymatcher.cpp \ |