diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 6bad482ca1..070ad550d8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -577,16 +577,17 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { if (_metadataType == NO_METADATA) { return *this; } - TypeStreamer::Category category = streamer->getCategory(); - *this << (int)category; - switch (category) { - case TypeStreamer::SIMPLE_CATEGORY: + TypeReader::Type type = streamer->getReaderType(); + *this << (int)type; + switch (type) { + case TypeReader::SIMPLE_TYPE: return *this; - case TypeStreamer::LIST_CATEGORY: + case TypeReader::LIST_TYPE: + case TypeReader::SET_TYPE: return *this << streamer->getValueStreamer(); - case TypeStreamer::MAP_CATEGORY: + case TypeReader::MAP_TYPE: return *this << streamer->getKeyStreamer() << streamer->getValueStreamer(); default: @@ -628,35 +629,36 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { reader = TypeReader(typeName, streamer); return *this; } - int category; - *this >> category; - switch (category) { - case TypeStreamer::SIMPLE_CATEGORY: + int type; + *this >> type; + switch (type) { + case TypeReader::SIMPLE_TYPE: reader = TypeReader(typeName, streamer); return *this; - case TypeStreamer::LIST_CATEGORY: { + case TypeReader::LIST_TYPE: + case TypeReader::SET_TYPE: { TypeReader valueReader; *this >> valueReader; - if (streamer && streamer->getCategory() == TypeStreamer::LIST_CATEGORY && + if (streamer && streamer->getReaderType() == type && valueReader.matchesExactly(streamer->getValueStreamer())) { reader = TypeReader(typeName, streamer); } else { - reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), + reader = TypeReader(typeName, streamer, false, (TypeReader::Type)type, TypeReaderPointer(), TypeReaderPointer(new TypeReader(valueReader))); } return *this; } - case TypeStreamer::MAP_CATEGORY: { + case TypeReader::MAP_TYPE: { TypeReader keyReader, valueReader; *this >> keyReader >> valueReader; - if (streamer && streamer->getCategory() == TypeStreamer::MAP_CATEGORY && + if (streamer && streamer->getReaderType() == TypeReader::MAP_TYPE && keyReader.matchesExactly(streamer->getKeyStreamer()) && valueReader.matchesExactly(streamer->getValueStreamer())) { reader = TypeReader(typeName, streamer); } else { - reader = TypeReader(typeName, streamer, false, TypeReaderPointer(new TypeReader(keyReader)), - TypeReaderPointer(new TypeReader(valueReader))); + reader = TypeReader(typeName, streamer, false, TypeReader::MAP_TYPE, + TypeReaderPointer(new TypeReader(keyReader)), TypeReaderPointer(new TypeReader(valueReader))); } return *this; } @@ -714,20 +716,23 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { // if all fields are the same type and in the right order, we can use the (more efficient) default streamer const QVector<MetaField>& localFields = streamer->getMetaFields(); if (fieldCount != localFields.size()) { - reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, + TypeReaderPointer(), TypeReaderPointer(), 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, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, + TypeReaderPointer(), TypeReaderPointer(), fields); return *this; } } reader = TypeReader(typeName, streamer); return *this; } - reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, + TypeReaderPointer(), TypeReaderPointer(), fields); return *this; } @@ -823,11 +828,12 @@ QVector<PropertyReader> Bitstream::getPropertyReaders(const QMetaObject* metaObj return propertyReaders; } -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, Type type, const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader, const QVector<FieldReader>& fields) : _typeName(typeName), _streamer(streamer), _exactMatch(exactMatch), + _type(type), _keyReader(keyReader), _valueReader(valueReader), _fields(fields) { @@ -838,10 +844,28 @@ QVariant TypeReader::read(Bitstream& in) const { return _streamer->read(in); } QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant(); - if (_valueReader) { - int size; - in >> size; - if (_keyReader) { + switch (_type) { + case STREAMABLE_TYPE: { + foreach (const FieldReader& field, _fields) { + field.read(in, _streamer, object); + } + break; + } + case LIST_TYPE: + case SET_TYPE: { + int size; + in >> size; + for (int i = 0; i < size; i++) { + QVariant value = _valueReader->read(in); + if (_streamer) { + _streamer->insert(object, value); + } + } + break; + } + case MAP_TYPE: { + int size; + in >> size; for (int i = 0; i < size; i++) { QVariant key = _keyReader->read(in); QVariant value = _valueReader->read(in); @@ -849,18 +873,10 @@ QVariant TypeReader::read(Bitstream& in) const { _streamer->insert(object, key, value); } } - } else { - for (int i = 0; i < size; i++) { - QVariant value = _valueReader->read(in); - if (_streamer) { - _streamer->insert(object, value); - } - } - } - } else { - foreach (const FieldReader& field, _fields) { - field.read(in, _streamer, object); + break; } + default: + break; } return object; } @@ -870,13 +886,67 @@ void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& refe _streamer->readDelta(in, object, reference); return; } - if (_valueReader) { - // TODO: collection deltas - - } else { - foreach (const FieldReader& field, _fields) { - field.readDelta(in, _streamer, object, reference); - } + bool changed; + in >> changed; + if (!changed) { + object = reference; + return; + } + switch (_type) { + case STREAMABLE_TYPE: { + foreach (const FieldReader& field, _fields) { + field.readDelta(in, _streamer, object, reference); + } + break; + } + case LIST_TYPE: { + object = reference; + int size, referenceSize; + in >> size >> referenceSize; + + break; + } + case SET_TYPE: { + object = reference; + int addedOrRemoved; + in >> addedOrRemoved; + for (int i = 0; i < addedOrRemoved; i++) { + QVariant value = _valueReader->read(in); + if (_streamer && !_streamer->remove(object, value)) { + _streamer->insert(object, value); + } + } + break; + } + case MAP_TYPE: { + object = reference; + int added; + in >> added; + for (int i = 0; i < added; i++) { + QVariant key = _keyReader->read(in); + QVariant value = _valueReader->read(in); + if (_streamer) { + _streamer->insert(object, key, value); + } + } + int modified; + in >> modified; + for (int i = 0; i < modified; i++) { + QVariant key = _keyReader->read(in); + + } + int removed; + in >> removed; + for (int i = 0; i < removed; i++) { + QVariant key = _keyReader->read(in); + if (_streamer) { + _streamer->remove(object, key); + } + } + break; + } + default: + break; } } @@ -958,8 +1028,8 @@ void TypeStreamer::setField(int index, QVariant& object, const QVariant& value) // nothing by default } -TypeStreamer::Category TypeStreamer::getCategory() const { - return SIMPLE_CATEGORY; +TypeReader::Type TypeStreamer::getReaderType() const { + return TypeReader::SIMPLE_TYPE; } const TypeStreamer* TypeStreamer::getKeyStreamer() const { @@ -977,3 +1047,7 @@ void TypeStreamer::insert(QVariant& object, const QVariant& element) const { void TypeStreamer::insert(QVariant& object, const QVariant& key, const QVariant& value) const { // nothing by default } + +bool TypeStreamer::remove(QVariant& object, const QVariant& key) const { + return false; +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 49b4ddd95f..cfee647d28 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -271,9 +271,15 @@ public: template<class T> void writeDelta(const T& value, const T& reference); template<class T> void readDelta(T& value, const T& reference); + template<class T> void writeDelta(const QList<T>& value, const QList<T>& reference); + template<class T> void readDelta(QList<T>& value, const QList<T>& reference); + template<class T> void writeDelta(const QSet<T>& value, const QSet<T>& reference); template<class T> void readDelta(QSet<T>& value, const QSet<T>& reference); + template<class K, class V> void writeDelta(const QHash<K, V>& value, const QHash<K, V>& reference); + template<class K, class V> void readDelta(QHash<K, V>& value, const QHash<K, V>& reference); + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); @@ -403,12 +409,171 @@ template<class T> inline void Bitstream::readDelta(T& value, const T& reference) } } +template<class T> inline void Bitstream::writeDelta(const QList<T>& value, const QList<T>& reference) { + if (value == reference) { + *this << false; + return; + } + *this << true; + *this << value.size(); + *this << reference.size(); + for (int i = 0; i < value.size(); i++) { + if (i < reference.size()) { + writeDelta(value.at(i), reference.at(i)); + } else { + *this << value.at(i); + } + } +} + +template<class T> inline void Bitstream::readDelta(QList<T>& value, const QList<T>& reference) { + value = reference; + bool changed; + *this >> changed; + if (!changed) { + return; + } + int size, referenceSize; + *this >> size >> referenceSize; + if (size < value.size()) { + value.erase(value.begin() + size, value.end()); + } + for (int i = 0; i < size; i++) { + if (i < referenceSize) { + readDelta(value[i], reference.at(i)); + } else { + T element; + *this >> element; + value.append(element); + } + } +} + template<class T> inline void Bitstream::writeDelta(const QSet<T>& value, const QSet<T>& reference) { - + if (value == reference) { + *this << false; + return; + } + *this << true; + int addedOrRemoved = 0; + foreach (const T& element, value) { + if (!reference.contains(element)) { + addedOrRemoved++; + } + } + foreach (const T& element, reference) { + if (!value.contains(element)) { + addedOrRemoved++; + } + } + *this << addedOrRemoved; + foreach (const T& element, value) { + if (!reference.contains(element)) { + *this << element; + } + } + foreach (const T& element, reference) { + if (!value.contains(element)) { + *this << element; + } + } } template<class T> inline void Bitstream::readDelta(QSet<T>& value, const QSet<T>& reference) { - + value = reference; + bool changed; + *this >> changed; + if (!changed) { + return; + } + int addedOrRemoved; + *this >> addedOrRemoved; + for (int i = 0; i < addedOrRemoved; i++) { + T element; + *this >> element; + if (!value.remove(element)) { + value.insert(element); + } + } +} + +template<class K, class V> inline void Bitstream::writeDelta(const QHash<K, V>& value, const QHash<K, V>& reference) { + if (value == reference) { + *this << false; + return; + } + *this << true; + int added = 0; + int modified = 0; + for (typename QHash<K, V>::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { + typename QHash<K, V>::const_iterator previous = reference.find(it.key()); + if (previous == reference.constEnd()) { + added++; + } else if (previous.value() != it.value()) { + modified++; + } + } + *this << added; + for (typename QHash<K, V>::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { + if (!reference.contains(it.key())) { + *this << it.key(); + *this << it.value(); + } + } + *this << modified; + for (typename QHash<K, V>::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { + typename QHash<K, V>::const_iterator previous = reference.find(it.key()); + if (previous != reference.constEnd() && previous.value() != it.value()) { + *this << it.key(); + writeDelta(it.value(), previous.value()); + } + } + int removed = 0; + for (typename QHash<K, V>::const_iterator it = reference.constBegin(); it != reference.constEnd(); it++) { + if (!value.contains(it.key())) { + removed++; + } + } + *this << removed; + for (typename QHash<K, V>::const_iterator it = reference.constBegin(); it != reference.constEnd(); it++) { + if (!value.contains(it.key())) { + *this << it.key(); + } + } +} + +template<class K, class V> inline void Bitstream::readDelta(QHash<K, V>& value, const QHash<K, V>& reference) { + value = reference; + bool changed; + *this >> changed; + if (!changed) { + return; + } + int added; + *this >> added; + for (int i = 0; i < added; i++) { + K key; + V mapping; + *this >> key >> mapping; + value.insert(key, mapping); + } + int modified; + *this >> modified; + for (int i = 0; i < modified; i++) { + K key; + *this >> key; + V& mapping = value[key]; + V newMapping; + readDelta(newMapping, mapping); + mapping = newMapping; + } + int removed; + *this >> removed; + for (int i = 0; i < removed; i++) { + K key; + *this >> key; + value.remove(key); + } } template<class T> inline Bitstream& Bitstream::operator<<(const QList<T>& list) { @@ -483,8 +648,11 @@ typedef QSharedPointer<TypeReader> TypeReaderPointer; class TypeReader { public: + enum Type { SIMPLE_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE }; + TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, bool exactMatch = true, - const TypeReaderPointer& keyReader = TypeReaderPointer(), const TypeReaderPointer& valueReader = TypeReaderPointer(), + Type type = SIMPLE_TYPE, const TypeReaderPointer& keyReader = TypeReaderPointer(), + const TypeReaderPointer& valueReader = TypeReaderPointer(), const QVector<FieldReader>& fields = QVector<FieldReader>()); const QByteArray& getTypeName() const { return _typeName; } @@ -503,6 +671,7 @@ private: QByteArray _typeName; const TypeStreamer* _streamer; bool _exactMatch; + Type _type; TypeReaderPointer _keyReader; TypeReaderPointer _valueReader; QVector<FieldReader> _fields; @@ -607,15 +776,14 @@ public: virtual int getFieldIndex(const QByteArray& name) const; virtual void setField(int index, QVariant& object, const QVariant& value) const; - enum Category { SIMPLE_CATEGORY, STREAMABLE_CATEGORY, LIST_CATEGORY, MAP_CATEGORY }; - - virtual Category getCategory() const; + virtual TypeReader::Type getReaderType() const; virtual const TypeStreamer* getKeyStreamer() const; virtual const TypeStreamer* getValueStreamer() const; virtual void insert(QVariant& object, const QVariant& value) const; virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const; + virtual bool remove(QVariant& object, const QVariant& key) const; private: @@ -638,7 +806,7 @@ public: template<class T> class StreamableTypeStreamer : public SimpleTypeStreamer<T> { public: - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::STREAMABLE_CATEGORY; } + virtual TypeReader::Type getReaderType() const { return TypeReader::STREAMABLE_TYPE; } virtual const QVector<MetaField>& 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 { @@ -653,7 +821,7 @@ template<class T> class CollectionTypeStreamer : public SimpleTypeStreamer<T> { template<class T> class CollectionTypeStreamer<QList<T> > : public SimpleTypeStreamer<QList<T> > { public: - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } + virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); } virtual void insert(QVariant& object, const QVariant& value) const { static_cast<QList<T>*>(object.data())->append(value.value<T>()); } @@ -663,21 +831,25 @@ public: template<class T> class CollectionTypeStreamer<QSet<T> > : public SimpleTypeStreamer<QSet<T> > { public: - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } + virtual TypeReader::Type getReaderType() const { return TypeReader::SET_TYPE; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); } virtual void insert(QVariant& object, const QVariant& value) const { static_cast<QSet<T>*>(object.data())->insert(value.value<T>()); } + virtual bool remove(QVariant& object, const QVariant& key) const { + return static_cast<QSet<T>*>(object.data())->remove(key.value<T>()); } }; /// A streamer for hash types. template<class K, class V> class CollectionTypeStreamer<QHash<K, V> > : public SimpleTypeStreamer<QHash<K, V> > { public: - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::MAP_CATEGORY; } + virtual TypeReader::Type getReaderType() const { return TypeReader::MAP_TYPE; } virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<K>()); } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<V>()); } virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const { static_cast<QHash<K, V>*>(object.data())->insert(key.value<K>(), value.value<V>()); } + virtual bool remove(QVariant& object, const QVariant& key) const { + return static_cast<QHash<K, V>*>(object.data())->remove(key.value<K>()); } }; /// Macro for registering simple type streamers. diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index e6f1827870..861b7310b8 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -173,6 +173,11 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) { out << "}\n"; out << "template<> void Bitstream::writeDelta(const " << name << "& value, const " << name << "& reference) {\n"; + out << " if (value == reference) {\n"; + out << " *this << false;\n"; + out << " return;\n"; + out << " }\n"; + out << " *this << true;\n"; foreach (const QString& base, str.clazz.bases) { out << " writeDelta(static_cast<const " << base << "&>(value), static_cast<const " << base << "&>(reference));\n"; @@ -183,6 +188,12 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) { out << "}\n"; out << "template<> void Bitstream::readDelta(" << name << "& value, const " << name << "& reference) {\n"; + out << " bool changed;\n"; + out << " *this >> changed;\n"; + out << " if (!changed) {\n"; + out << " value = reference;\n"; + out << " return;\n"; + out << " }\n"; foreach (const QString& base, str.clazz.bases) { out << " readDelta(static_cast<" << base << "&>(value), static_cast<const " << base << "&>(reference));\n";