diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 6c8d8ec559..40c87b98e8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -82,6 +82,10 @@ int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { return 0; } +const TypeStreamer* Bitstream::getTypeStreamer(int type) { + return getTypeStreamers().value(type); +} + const QMetaObject* Bitstream::getMetaObject(const QByteArray& className) { return getMetaObjects().value(className); } @@ -325,11 +329,9 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { } Bitstream& Bitstream::operator>>(QVariant& value) { - const TypeStreamer* streamer; - _typeStreamerStreamer >> streamer; - if (streamer) { - value = streamer->read(*this); - } + TypeReader reader; + _typeStreamerStreamer >> reader; + value = reader.read(*this); return *this; } @@ -406,7 +408,14 @@ Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { } Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { - _typeStreamerStreamer >> streamer; + TypeReader typeReader; + _typeStreamerStreamer >> typeReader; + streamer = typeReader.getStreamer(); + return *this; +} + +Bitstream& Bitstream::operator>>(TypeReader& reader) { + _typeStreamerStreamer >> reader; return *this; } @@ -499,8 +508,8 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { *this >> storedPropertyCount; QVector properties(storedPropertyCount); for (int i = 0; i < storedPropertyCount; i++) { - const TypeStreamer* typeStreamer; - *this >> typeStreamer; + TypeReader typeReader; + *this >> typeReader; QMetaProperty property = QMetaProperty(); if (_metadataType == FULL_METADATA) { QByteArray propertyName; @@ -509,7 +518,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { property = metaObject->property(metaObject->indexOfProperty(propertyName)); } } - properties[i] = PropertyReader(typeStreamer, property); + properties[i] = PropertyReader(typeReader, property); } // for hash metadata, check the names/types of the properties as well as the name hash against our own class if (_metadataType == HASH_METADATA) { @@ -525,7 +534,8 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { if (!typeStreamer) { continue; } - if (propertyIndex >= properties.size() || properties.at(propertyIndex).getStreamer() != typeStreamer) { + if (propertyIndex >= properties.size() || + !properties.at(propertyIndex).getReader().matchesExactly(typeStreamer)) { objectReader = ObjectReader(className, metaObject, properties); return *this; } @@ -551,16 +561,106 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { const char* typeName = QMetaType::typeName(streamer->getType()); - return *this << QByteArray::fromRawData(typeName, strlen(typeName)); + *this << QByteArray::fromRawData(typeName, strlen(typeName)); + if (_metadataType == NO_METADATA) { + return *this; + } + const QVector& metaFields = streamer->getMetaFields(); + *this << metaFields.size(); + if (metaFields.isEmpty()) { + return *this; + } + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const MetaField& metaField, metaFields) { + _typeStreamerStreamer << metaField.getStreamer(); + if (_metadataType == FULL_METADATA) { + *this << metaField.getName(); + } else { + hash.addData(metaField.getName().constData(), metaField.getName().size() + 1); + } + } + if (_metadataType == HASH_METADATA) { + QByteArray hashResult = hash.result(); + write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + } + return *this; } -Bitstream& Bitstream::operator>(const TypeStreamer*& streamer) { +Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray typeName; *this >> typeName; - streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + const TypeStreamer* streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); if (!streamer) { qWarning() << "Unknown type name: " << typeName << "\n"; } + if (_metadataType == NO_METADATA) { + reader = TypeReader(typeName, streamer); + return *this; + } + int fieldCount; + *this >> fieldCount; + QVector fields(fieldCount); + for (int i = 0; i < fieldCount; i++) { + TypeReader typeReader; + *this >> typeReader; + int index = -1; + if (_metadataType == FULL_METADATA) { + QByteArray fieldName; + *this >> fieldName; + if (streamer) { + index = streamer->getFieldIndex(fieldName); + } + } + fields[i] = FieldReader(typeReader, index); + } + // for hash metadata, check the names/types of the fields as well as the name hash against our own class + if (_metadataType == HASH_METADATA) { + QCryptographicHash hash(QCryptographicHash::Md5); + if (streamer) { + const QVector& localFields = streamer->getMetaFields(); + if (fieldCount != localFields.size()) { + reader = TypeReader(typeName, streamer, false, fields); + return *this; + } + if (fieldCount == 0) { + reader = TypeReader(typeName, streamer); + return *this; + } + for (int i = 0; i < fieldCount; i++) { + const MetaField& localField = localFields.at(i); + if (!fields.at(i).getReader().matchesExactly(localField.getStreamer())) { + reader = TypeReader(typeName, streamer, false, fields); + return *this; + } + hash.addData(localField.getName().constData(), localField.getName().size() + 1); + } + } + QByteArray localHashResult = hash.result(); + QByteArray remoteHashResult(localHashResult.size(), 0); + read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); + if (streamer && localHashResult == remoteHashResult) { + // since everything is the same, we can use the default streamer + reader = TypeReader(typeName, streamer); + return *this; + } + } else if (streamer) { + // if all fields are the same type and in the right order, we can use the (more efficient) default streamer + const QVector& localFields = streamer->getMetaFields(); + if (fieldCount != localFields.size()) { + reader = TypeReader(typeName, streamer, false, fields); + return *this; + } + for (int i = 0; i < fieldCount; i++) { + const FieldReader& fieldReader = fields.at(i); + if (!fieldReader.getReader().matchesExactly(localFields.at(i).getStreamer()) || fieldReader.getIndex() != i) { + reader = TypeReader(typeName, streamer, false, fields); + return *this; + } + } + reader = TypeReader(typeName, streamer); + return *this; + } + reader = TypeReader(typeName, streamer, false, fields); return *this; } @@ -650,12 +750,51 @@ QVector Bitstream::getPropertyReaders(const QMetaObject* metaObj } const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType()); if (typeStreamer) { - propertyReaders.append(PropertyReader(typeStreamer, property)); + propertyReaders.append(PropertyReader(TypeReader(QByteArray(), typeStreamer), property)); } } return propertyReaders; } +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, + bool exactMatch, const QVector& fields) : + _typeName(typeName), + _streamer(streamer), + _exactMatch(exactMatch), + _fields(fields) { +} + +QVariant TypeReader::read(Bitstream& in) const { + if (_exactMatch) { + return _streamer->read(in); + } + QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant(); + foreach (const FieldReader& field, _fields) { + field.read(in, _streamer, object); + } + return object; +} + +bool TypeReader::matchesExactly(const TypeStreamer* streamer) const { + return _exactMatch && _streamer == streamer; +} + +uint qHash(const TypeReader& typeReader, uint seed) { + return qHash(typeReader.getTypeName(), seed); +} + +FieldReader::FieldReader(const TypeReader& reader, int index) : + _reader(reader), + _index(index) { +} + +void FieldReader::read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const { + QVariant value = _reader.read(in); + if (_index != -1 && streamer) { + streamer->setField(_index, object, value); + } +} + ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, const QVector& properties) : _className(className), @@ -677,14 +816,35 @@ uint qHash(const ObjectReader& objectReader, uint seed) { return qHash(objectReader.getClassName(), seed); } -PropertyReader::PropertyReader(const TypeStreamer* streamer, const QMetaProperty& property) : - _streamer(streamer), +PropertyReader::PropertyReader(const TypeReader& reader, const QMetaProperty& property) : + _reader(reader), _property(property) { } void PropertyReader::read(Bitstream& in, QObject* object) const { - QVariant value = _streamer->read(in); + QVariant value = _reader.read(in); if (_property.isValid() && object) { _property.write(object, value); } } + +MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : + _name(name), + _streamer(streamer) { +} + +TypeStreamer::~TypeStreamer() { +} + +const QVector& TypeStreamer::getMetaFields() const { + static QVector emptyMetaFields; + return emptyMetaFields; +} + +int TypeStreamer::getFieldIndex(const QByteArray& name) const { + return -1; +} + +void TypeStreamer::setField(int index, QVariant& object, const QVariant& value) const { + // nothing by default +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index f52db029b6..78bc125841 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -30,9 +31,11 @@ class QUrl; class Attribute; class AttributeValue; class Bitstream; +class FieldReader; class ObjectReader; class OwnedAttributeValue; class PropertyReader; +class TypeReader; class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; @@ -191,7 +194,7 @@ public: class ReadMappings { public: QHash metaObjectValues; - QHash typeStreamerValues; + QHash typeStreamerValues; QHash attributeValues; QHash scriptStringValues; QHash sharedObjectValues; @@ -205,6 +208,9 @@ public: /// \return zero; the function only returns a value so that it can be used in static initialization static int registerTypeStreamer(int type, TypeStreamer* streamer); + /// Returns the streamer registered for the supplied type, if any. + static const TypeStreamer* getTypeStreamer(int type); + /// Returns the meta-object registered under the supplied class name, if any. static const QMetaObject* getMetaObject(const QByteArray& className); @@ -307,6 +313,7 @@ public: Bitstream& operator<<(const TypeStreamer* streamer); Bitstream& operator>>(const TypeStreamer*& streamer); + Bitstream& operator>>(TypeReader& reader); Bitstream& operator<<(const AttributePointer& attribute); Bitstream& operator>>(AttributePointer& attribute); @@ -321,7 +328,7 @@ public: Bitstream& operator>(ObjectReader& objectReader); Bitstream& operator<(const TypeStreamer* streamer); - Bitstream& operator>(const TypeStreamer*& streamer); + Bitstream& operator>(TypeReader& reader); Bitstream& operator<(const AttributePointer& attribute); Bitstream& operator>(AttributePointer& attribute); @@ -349,7 +356,7 @@ private: MetadataType _metadataType; RepeatedValueStreamer _metaObjectStreamer; - RepeatedValueStreamer _typeStreamerStreamer; + RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; RepeatedValueStreamer _sharedObjectStreamer; @@ -428,6 +435,50 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } +/// Contains the information required to read a type from the stream. +class TypeReader { +public: + + TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, + bool exactMatch = true, const QVector& fields = QVector()); + + const QByteArray& getTypeName() const { return _typeName; } + const TypeStreamer* getStreamer() const { return _streamer; } + + QVariant read(Bitstream& in) const; + + bool matchesExactly(const TypeStreamer* streamer) const; + + bool operator==(const TypeReader& other) const { return _typeName == other._typeName; } + bool operator!=(const TypeReader& other) const { return _typeName != other._typeName; } + +private: + + QByteArray _typeName; + const TypeStreamer* _streamer; + bool _exactMatch; + QVector _fields; +}; + +uint qHash(const TypeReader& typeReader, uint seed = 0); + +/// Contains the information required to read a metatype field from the stream and apply it. +class FieldReader { +public: + + FieldReader(const TypeReader& reader = TypeReader(), int index = -1); + + const TypeReader& getReader() const { return _reader; } + int getIndex() const { return _index; } + + void read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const; + +private: + + TypeReader _reader; + int _index; +}; + /// Contains the information required to read an object from the stream. class ObjectReader { public: @@ -438,8 +489,6 @@ public: const QByteArray& getClassName() const { return _className; } const QMetaObject* getMetaObject() const { return _metaObject; } - bool isNull() const { return _className.isEmpty(); } - QObject* read(Bitstream& in, QObject* object = NULL) const; bool operator==(const ObjectReader& other) const { return _className == other._className; } @@ -458,18 +507,33 @@ uint qHash(const ObjectReader& objectReader, uint seed = 0); class PropertyReader { public: - PropertyReader(const TypeStreamer* streamer = NULL, const QMetaProperty& property = QMetaProperty()); + PropertyReader(const TypeReader& reader = TypeReader(), const QMetaProperty& property = QMetaProperty()); - const TypeStreamer* getStreamer() const { return _streamer; } + const TypeReader& getReader() const { return _reader; } void read(Bitstream& in, QObject* object) const; private: - const TypeStreamer* _streamer; + TypeReader _reader; QMetaProperty _property; }; +/// Describes a metatype field. +class MetaField { +public: + + MetaField(const QByteArray& name = QByteArray(), const TypeStreamer* streamer = NULL); + + const QByteArray& getName() const { return _name; } + const TypeStreamer* getStreamer() const { return _streamer; } + +private: + + QByteArray _name; + const TypeStreamer* _streamer; +}; + Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. @@ -479,12 +543,18 @@ Q_DECLARE_METATYPE(const QMetaObject*) class TypeStreamer { public: + virtual ~TypeStreamer(); + void setType(int type) { _type = type; } int getType() const { return _type; } virtual void write(Bitstream& out, const QVariant& value) const = 0; virtual QVariant read(Bitstream& in) const = 0; + virtual const QVector& getMetaFields() const; + virtual int getFieldIndex(const QByteArray& name) const; + virtual void setField(int index, QVariant& object, const QVariant& value) const; + private: int _type; @@ -498,6 +568,16 @@ public: virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); } }; +/// A streamer that works with Bitstream's operators. +template class CompoundTypeStreamer : public SimpleTypeStreamer { +public: + + virtual const QVector& getMetaFields() const { return T::getMetaFields(); } + virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); } + virtual void setField(int index, QVariant& object, const QVariant& value) const { + static_cast(object.data())->setField(index, value); } +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); @@ -532,16 +612,18 @@ public: /// Registers a streamable type and its streamer. template int registerStreamableMetaType() { int type = qRegisterMetaType(); - Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer()); + Bitstream::registerTypeStreamer(type, new CompoundTypeStreamer()); return type; } /// Flags a class as streamable (use as you would Q_OBJECT). #define STREAMABLE public: \ static const int Type; \ - static int getFieldCount(); \ - void setFieldValue(int index, const QVariant& value); \ - private: + static const QVector& getMetaFields(); \ + static int getFieldIndex(const QByteArray& name); \ + void setField(int index, const QVariant& value); \ + private: \ + static QHash createFieldIndices(); /// Flags a field or base class as streaming. #define STREAM diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index 598b7b10d0..562dd6e02e 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -100,21 +100,45 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n"; - out << "int " << name << "::getFieldCount() {\n"; - out << " return " << str.fields.size(); - foreach(const QString& base, str.clazz.bases) { - out << " + " << base << "::getFieldCount()"; + out << "const QVector& " << name << "::getMetaFields() {\n"; + out << " static QVector metaFields = QVector()"; + foreach (const QString& base, str.clazz.bases) { + out << " << " << base << "::getMetaFields()"; + } + foreach (const Field& field, str.fields) { + out << "\n << MetaField(\"" << field.name << "\", Bitstream::getTypeStreamer(qMetaTypeId<" << + field.type << ">()))"; } out << ";\n"; + out << " return metaFields;\n"; out << "}\n"; - out << "void " << name << "::setFieldValue(int index, const QVariant& value) {\n"; + out << "int " << name << "::getFieldIndex(const QByteArray& name) {\n"; + out << " static QHash fieldIndices = createFieldIndices();\n"; + out << " return fieldIndices.value(name) - 1;\n"; + out << "}\n"; + + out << "QHash " << name << "::createFieldIndices() {\n"; + out << " QHash indices;\n"; + out << " int index = 0;\n"; + foreach (const QString& base, str.clazz.bases) { + out << " foreach (const MetaField& field, " << base << "::getMetaFields()) {\n"; + out << " indices.insert(field.getName(), index++);\n"; + out << " }\n"; + } + out << " foreach (const MetaField& field, getMetaFields()) {\n"; + out << " indices.insert(field.getName(), index++);\n"; + out << " }\n"; + out << " return indices;\n"; + out << "}\n"; + + out << "void " << name << "::setField(int index, const QVariant& value) {\n"; if (!str.clazz.bases.isEmpty()) { out << " int nextIndex;\n"; } foreach (const QString& base, str.clazz.bases) { - out << " if ((nextIndex = index - " << base << "::getFieldCount()) < 0) {\n"; - out << " " << base << "::setFieldValue(index, value);\n"; + out << " if ((nextIndex = index - " << base << "::getMetaFields().size()) < 0) {\n"; + out << " " << base << "::setField(index, value);\n"; out << " return;\n"; out << " }\n"; out << " index = nextIndex;\n";