From 6db385de7a738e746dc53faab7da4dd1128dfb75 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 2 Jun 2014 15:16:08 -0700 Subject: [PATCH 1/8] Ported over fixes that I made when working on delta-encoding for avatars. --- libraries/metavoxels/src/Bitstream.cpp | 58 ++++++++++-- libraries/metavoxels/src/Bitstream.h | 92 +++++++++++++++++++- libraries/metavoxels/src/DatagramSequencer.h | 2 +- libraries/metavoxels/src/SharedObject.cpp | 41 +++++---- libraries/metavoxels/src/SharedObject.h | 23 +++-- 5 files changed, 184 insertions(+), 32 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index ad929e533c..b3d5817fe9 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -193,7 +193,7 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) { continue; } connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*))); - QPointer& reference = _sharedObjectReferences[it.key()->getID()]; + QPointer& reference = _sharedObjectReferences[it.key()->getOriginID()]; if (reference) { _sharedObjectStreamer.removePersistentID(reference); reference->disconnect(this); @@ -227,7 +227,7 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { if (!it.value()) { continue; } - QPointer& reference = _sharedObjectReferences[it.value()->getRemoteID()]; + QPointer& reference = _sharedObjectReferences[it.value()->getRemoteOriginID()]; if (reference) { _sharedObjectStreamer.removePersistentValue(reference.data()); } @@ -411,6 +411,10 @@ Bitstream& Bitstream::operator>>(QUrl& url) { } Bitstream& Bitstream::operator<<(const QVariant& value) { + if (!value.isValid()) { + _typeStreamerStreamer << NULL; + return *this; + } const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); if (streamer) { _typeStreamerStreamer << streamer; @@ -424,7 +428,11 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { Bitstream& Bitstream::operator>>(QVariant& value) { TypeReader reader; _typeStreamerStreamer >> reader; - value = reader.read(*this); + if (reader.getTypeName().isEmpty()) { + value = QVariant(); + } else { + value = reader.read(*this); + } return *this; } @@ -656,6 +664,10 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { } Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { + if (!streamer) { + *this << QByteArray(); + return *this; + } const char* typeName = QMetaType::typeName(streamer->getType()); *this << QByteArray::fromRawData(typeName, strlen(typeName)); if (_metadataType == NO_METADATA) { @@ -702,6 +714,10 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray typeName; *this >> typeName; + if (typeName.isEmpty()) { + reader = TypeReader(); + return *this; + } const TypeStreamer* streamer = _typeStreamerSubstitutions.value(typeName); if (!streamer) { streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); @@ -847,9 +863,10 @@ Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { return *this << (int)0; } *this << object->getID(); - QPointer reference = _sharedObjectReferences.value(object->getID()); + *this << object->getOriginID(); + QPointer reference = _sharedObjectReferences.value(object->getOriginID()); if (reference) { - writeRawDelta((QObject*)object.data(), (QObject*)reference.data()); + writeRawDelta((const QObject*)object.data(), (const QObject*)reference.data()); } else { *this << (QObject*)object.data(); } @@ -863,7 +880,9 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { object = SharedObjectPointer(); return *this; } - QPointer reference = _sharedObjectReferences.value(id); + int originID; + *this >> originID; + QPointer reference = _sharedObjectReferences.value(originID); QPointer& pointer = _weakSharedObjectHash[id]; if (pointer) { ObjectReader objectReader; @@ -876,15 +895,19 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { } else { QObject* rawObject; if (reference) { - readRawDelta(rawObject, (QObject*)reference.data()); + readRawDelta(rawObject, (const QObject*)reference.data()); } else { *this >> rawObject; } pointer = static_cast(rawObject); if (pointer) { + if (reference) { + pointer->setOriginID(reference->getOriginID()); + } pointer->setRemoteID(id); + pointer->setRemoteOriginID(originID); } else { - qDebug() << "Null object" << pointer << reference; + qDebug() << "Null object" << pointer << reference << id; } } object = static_cast(pointer.data()); @@ -893,7 +916,7 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { void Bitstream::clearSharedObject(QObject* object) { SharedObject* sharedObject = static_cast(object); - _sharedObjectReferences.remove(sharedObject->getID()); + _sharedObjectReferences.remove(sharedObject->getOriginID()); int id = _sharedObjectStreamer.takePersistentID(sharedObject); if (id != 0) { emit sharedObjectCleared(id); @@ -1099,6 +1122,10 @@ uint qHash(const TypeReader& typeReader, uint seed) { return qHash(typeReader.getTypeName(), seed); } +QDebug& operator<<(QDebug& debug, const TypeReader& typeReader) { + return debug << typeReader.getTypeName(); +} + FieldReader::FieldReader(const TypeReader& reader, int index) : _reader(reader), _index(index) { @@ -1152,6 +1179,10 @@ uint qHash(const ObjectReader& objectReader, uint seed) { return qHash(objectReader.getClassName(), seed); } +QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader) { + return debug << objectReader.getClassName(); +} + PropertyReader::PropertyReader(const TypeReader& reader, const QMetaProperty& property) : _reader(reader), _property(property) { @@ -1236,3 +1267,12 @@ QVariant TypeStreamer::getValue(const QVariant& object, int index) const { void TypeStreamer::setValue(QVariant& object, int index, const QVariant& value) const { // nothing by default } + +QDebug& operator<<(QDebug& debug, const TypeStreamer* typeStreamer) { + return debug << (typeStreamer ? QMetaType::typeName(typeStreamer->getType()) : "null"); +} + +QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject) { + return debug << (metaObject ? metaObject->className() : "null"); +} + diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 34b66eb9f2..544d6dbd78 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -294,6 +294,9 @@ public: template void writeRawDelta(const QList& value, const QList& reference); template void readRawDelta(QList& value, const QList& reference); + template void writeRawDelta(const QVector& value, const QVector& reference); + template void readRawDelta(QVector& value, const QVector& reference); + template void writeRawDelta(const QSet& value, const QSet& reference); template void readRawDelta(QSet& value, const QSet& reference); @@ -339,6 +342,9 @@ public: template Bitstream& operator<<(const QList& list); template Bitstream& operator>>(QList& list); + template Bitstream& operator<<(const QVector& list); + template Bitstream& operator>>(QVector& list); + template Bitstream& operator<<(const QSet& set); template Bitstream& operator>>(QSet& set); @@ -472,6 +478,36 @@ template inline void Bitstream::readRawDelta(QList& value, const QLi } } +template inline void Bitstream::writeRawDelta(const QVector& value, const QVector& reference) { + *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 inline void Bitstream::readRawDelta(QVector& value, const QVector& reference) { + value = reference; + 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 inline void Bitstream::writeRawDelta(const QSet& value, const QSet& reference) { int addedOrRemoved = 0; foreach (const T& element, value) { @@ -600,6 +636,27 @@ template inline Bitstream& Bitstream::operator>>(QList& list) { return *this; } +template inline Bitstream& Bitstream::operator<<(const QVector& vector) { + *this << vector.size(); + foreach (const T& entry, vector) { + *this << entry; + } + return *this; +} + +template inline Bitstream& Bitstream::operator>>(QVector& vector) { + int size; + *this >> size; + vector.clear(); + vector.reserve(size); + for (int i = 0; i < size; i++) { + T entry; + *this >> entry; + vector.append(entry); + } + return *this; +} + template inline Bitstream& Bitstream::operator<<(const QSet& set) { *this << set.size(); foreach (const T& entry, set) { @@ -683,6 +740,8 @@ private: uint qHash(const TypeReader& typeReader, uint seed = 0); +QDebug& operator<<(QDebug& debug, const TypeReader& typeReader); + /// Contains the information required to read a metatype field from the stream and apply it. class FieldReader { public: @@ -726,6 +785,8 @@ private: uint qHash(const ObjectReader& objectReader, uint seed = 0); +QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader); + /// Contains the information required to read an object property from the stream and apply it. class PropertyReader { public: @@ -808,6 +869,10 @@ private: int _type; }; +QDebug& operator<<(QDebug& debug, const TypeStreamer* typeStreamer); + +QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject); + /// A streamer that works with Bitstream's operators. template class SimpleTypeStreamer : public TypeStreamer { public: @@ -818,11 +883,11 @@ public: virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { out.writeDelta(value.value(), reference.value()); } virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - in.readDelta(*static_cast(value.data()), reference.value()); } + T rawValue; in.readDelta(rawValue, reference.value()); value = QVariant::fromValue(rawValue); } virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { out.writeRawDelta(value.value(), reference.value()); } virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - in.readRawDelta(*static_cast(value.data()), reference.value()); } + T rawValue; in.readRawDelta(rawValue, reference.value()); value = QVariant::fromValue(rawValue); } }; /// A streamer for types compiled by mtc. @@ -858,6 +923,22 @@ public: static_cast*>(object.data())->replace(index, value.value()); } }; +/// A streamer for vector types. +template class CollectionTypeStreamer > : public SimpleTypeStreamer > { +public: + + virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; } + virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } + virtual void insert(QVariant& object, const QVariant& value) const { + static_cast*>(object.data())->append(value.value()); } + virtual void prune(QVariant& object, int size) const { + QVector* list = static_cast*>(object.data()); list->erase(list->begin() + size, list->end()); } + virtual QVariant getValue(const QVariant& object, int index) const { + return QVariant::fromValue(static_cast*>(object.constData())->at(index)); } + virtual void setValue(QVariant& object, int index, const QVariant& value) const { + static_cast*>(object.data())->replace(index, value.value()); } +}; + /// A streamer for set types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: @@ -940,6 +1021,13 @@ template int registerStreamableMetaType() { return type; } +/// Registers a collection type and its streamer. +template int registerCollectionMetaType() { + int type = qRegisterMetaType(); + Bitstream::registerTypeStreamer(type, new CollectionTypeStreamer()); + return type; +} + /// Flags a class as streamable (use as you would Q_OBJECT). #define STREAMABLE public: \ static const int Type; \ diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index ce9f36ba33..cf6ded74da 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -19,7 +19,7 @@ #include #include -#include "Bitstream.h" +#include "AttributeRegistry.h" class ReliableChannel; diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index b578d70959..47d69f4abe 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -23,7 +23,9 @@ REGISTER_META_OBJECT(SharedObject) SharedObject::SharedObject() : _id(++_lastID), - _remoteID(0) { + _originID(_id), + _remoteID(0), + _remoteOriginID(0) { _weakHash.insert(_id, this); } @@ -39,26 +41,33 @@ void SharedObject::decrementReferenceCount() { } } -SharedObject* SharedObject::clone(bool withID) const { +SharedObject* SharedObject::clone(bool withID, SharedObject* target) const { // default behavior is to make a copy using the no-arg constructor and copy the stored properties const QMetaObject* metaObject = this->metaObject(); - SharedObject* newObject = static_cast(metaObject->newInstance()); + if (!target) { + target = static_cast(metaObject->newInstance()); + } for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (property.isStored()) { - property.write(newObject, property.read(this)); + if (property.userType() == qMetaTypeId()) { + SharedObject* value = property.read(this).value().data(); + property.write(target, QVariant::fromValue(value ? value->clone(withID) : value)); + } else { + property.write(target, property.read(this)); + } } } foreach (const QByteArray& propertyName, dynamicPropertyNames()) { - newObject->setProperty(propertyName, property(propertyName)); + target->setProperty(propertyName, property(propertyName)); } if (withID) { - newObject->setID(_id); + target->setOriginID(_originID); } - return newObject; + return target; } -bool SharedObject::equals(const SharedObject* other) const { +bool SharedObject::equals(const SharedObject* other, bool sharedAncestry) const { if (!other) { return false; } @@ -67,7 +76,7 @@ bool SharedObject::equals(const SharedObject* other) const { } // default behavior is to compare the properties const QMetaObject* metaObject = this->metaObject(); - if (metaObject != other->metaObject()) { + if (metaObject != other->metaObject() && !sharedAncestry) { return false; } for (int i = 0; i < metaObject->propertyCount(); i++) { @@ -92,13 +101,15 @@ void SharedObject::dump(QDebug debug) const { debug << this; const QMetaObject* metaObject = this->metaObject(); for (int i = 0; i < metaObject->propertyCount(); i++) { - debug << metaObject->property(i).name() << metaObject->property(i).read(this); + QMetaProperty property = metaObject->property(i); + if (property.isStored()) { + debug << property.name() << property.read(this); + } + } + QList dynamicPropertyNames = this->dynamicPropertyNames(); + foreach (const QByteArray& propertyName, dynamicPropertyNames) { + debug << propertyName << property(propertyName); } -} - -void SharedObject::setID(int id) { - _weakHash.remove(_id); - _weakHash.insert(_id = id, this); } int SharedObject::_lastID = 0; diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index aba6b86bea..41c3c01ffe 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -41,31 +41,44 @@ public: /// Returns the unique local ID for this object. int getID() const { return _id; } + /// Returns the local origin ID for this object. + int getOriginID() const { return _originID; } + + void setOriginID(int originID) { _originID = originID; } + /// Returns the unique remote ID for this object, or zero if this is a local object. int getRemoteID() const { return _remoteID; } void setRemoteID(int remoteID) { _remoteID = remoteID; } + /// Returns the remote origin ID for this object, or zero if this is a local object. + int getRemoteOriginID() const { return _remoteOriginID; } + + void setRemoteOriginID(int remoteOriginID) { _remoteOriginID = remoteOriginID; } + int getReferenceCount() const { return _referenceCount.load(); } void incrementReferenceCount(); void decrementReferenceCount(); /// Creates a new clone of this object. - /// \param withID if true, give the clone the same ID as this object - virtual SharedObject* clone(bool withID = false) const; + /// \param withID if true, give the clone the same origin ID as this object + /// \target if non-NULL, a target object to populate (as opposed to creating a new instance of this object's class) + virtual SharedObject* clone(bool withID = false, SharedObject* target = NULL) const; /// Tests this object for equality with another. - virtual bool equals(const SharedObject* other) const; + /// \param sharedAncestry if true and the classes of the objects differ, compare their shared ancestry (assuming that + /// this is an instance of a superclass of the other object's class) rather than simply returning false. + virtual bool equals(const SharedObject* other, bool sharedAncestry = false) const; // Dumps the contents of this object to the debug output. virtual void dump(QDebug debug = QDebug(QtDebugMsg)) const; private: - void setID(int id); - int _id; + int _originID; int _remoteID; + int _remoteOriginID; QAtomicInt _referenceCount; static int _lastID; From ac7d23bf54af5e5e4d4df9bd776b813cfc802263 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 2 Jun 2014 19:00:48 -0700 Subject: [PATCH 2/8] Working on support for streaming Qt enums/flags. --- libraries/metavoxels/src/Bitstream.cpp | 14 +++++++++++ libraries/metavoxels/src/Bitstream.h | 15 +++++++++++ tests/metavoxels/src/MetavoxelTests.cpp | 33 ++++++++++++++++++++++--- tests/metavoxels/src/MetavoxelTests.h | 19 +++++++++++++- 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index b3d5817fe9..f3ba1cca4d 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1203,6 +1203,20 @@ void PropertyReader::readDelta(Bitstream& in, QObject* object, const QObject* re } } +PropertyWriter::PropertyWriter(const QMetaProperty& property, const TypeStreamer* streamer) : + _property(property), + _streamer(streamer) { +} + +void PropertyWriter::write(Bitstream& out, const QObject* object) const { + _streamer->write(out, _property.read(object)); +} + +void PropertyWriter::writeDelta(Bitstream& out, const QObject* object, const QObject* reference) const { + _streamer->writeDelta(out, _property.read(object), reference && object->metaObject() == reference->metaObject() ? + _property.read(reference) : QVariant()); +} + MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : _name(name), _streamer(streamer) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 544d6dbd78..7e1551346a 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -804,6 +804,21 @@ private: QMetaProperty _property; }; +/// Contains the information necessary to obtain an object property and write it to the stream. +class PropertyWriter { +public: + + PropertyWriter(const QMetaProperty& property = QMetaProperty(), const TypeStreamer* streamer = NULL); + + void write(Bitstream& out, const QObject* object) const; + void writeDelta(Bitstream& out, const QObject* object, const QObject* reference) const; + +private: + + QMetaProperty _property; + const TypeStreamer* _streamer; +}; + /// Describes a metatype field. class MetaField { public: diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 2400e086eb..1b7eaf9a22 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -51,6 +51,29 @@ static QByteArray createRandomBytes() { return createRandomBytes(MIN_BYTES, MAX_BYTES); } +static TestSharedObjectA::TestEnum getRandomTestEnum() { + switch (randIntInRange(0, 2)) { + case 0: return TestSharedObjectA::FIRST_TEST_ENUM; + case 1: return TestSharedObjectA::SECOND_TEST_ENUM; + case 2: + default: return TestSharedObjectA::THIRD_TEST_ENUM; + } +} + +static TestSharedObjectA::TestFlags getRandomTestFlags() { + TestSharedObjectA::TestFlags flags = 0; + if (randomBoolean()) { + flags |= TestSharedObjectA::FIRST_TEST_FLAG; + } + if (randomBoolean()) { + flags |= TestSharedObjectA::SECOND_TEST_FLAG; + } + if (randomBoolean()) { + flags |= TestSharedObjectA::THIRD_TEST_FLAG; + } + return flags; +} + static TestMessageC createRandomMessageC() { TestMessageC message; message.foo = randomBoolean(); @@ -64,7 +87,7 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { QByteArray array; QDataStream outStream(&array, QIODevice::WriteOnly); Bitstream out(outStream, metadataType); - SharedObjectPointer testObjectWrittenA = new TestSharedObjectA(randFloat()); + SharedObjectPointer testObjectWrittenA = new TestSharedObjectA(randFloat(), getRandomTestEnum(), getRandomTestFlags()); out << testObjectWrittenA; SharedObjectPointer testObjectWrittenB = new TestSharedObjectB(randFloat(), createRandomBytes()); out << testObjectWrittenB; @@ -175,7 +198,7 @@ bool MetavoxelTests::run() { static SharedObjectPointer createRandomSharedObject() { switch (randIntInRange(0, 2)) { - case 0: return new TestSharedObjectA(randFloat()); + case 0: return new TestSharedObjectA(randFloat(), getRandomTestEnum(), getRandomTestFlags()); case 1: return new TestSharedObjectB(); case 2: default: return SharedObjectPointer(); @@ -393,8 +416,10 @@ void Endpoint::readReliableChannel() { streamedBytesReceived += bytes.size(); } -TestSharedObjectA::TestSharedObjectA(float foo) : - _foo(foo) { +TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : + _foo(foo), + _baz(baz), + _bong(bong) { sharedObjectsCreated++; } diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index 206c818c6e..6e0b857328 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -70,16 +70,31 @@ private: /// A simple shared object. class TestSharedObjectA : public SharedObject { Q_OBJECT + Q_ENUMS(TestEnum) + Q_FLAGS(TestFlag TestFlags) Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged) + Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz) + Q_PROPERTY(TestFlags bong READ getBong WRITE setBong) public: - Q_INVOKABLE TestSharedObjectA(float foo = 0.0f); + enum TestEnum { FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM }; + + enum TestFlag { NO_TEST_FLAGS = 0x0, FIRST_TEST_FLAG = 0x01, SECOND_TEST_FLAG = 0x02, THIRD_TEST_FLAG = 0x03 }; + Q_DECLARE_FLAGS(TestFlags, TestFlag) + + Q_INVOKABLE TestSharedObjectA(float foo = 0.0f, TestEnum baz = FIRST_TEST_ENUM, TestFlags bong = 0); virtual ~TestSharedObjectA(); void setFoo(float foo); float getFoo() const { return _foo; } + void setBaz(TestEnum baz) { _baz = baz; } + TestEnum getBaz() const { return _baz; } + + void setBong(TestFlags bong) { _bong = bong; } + TestFlags getBong() const { return _bong; } + signals: void fooChanged(float foo); @@ -87,6 +102,8 @@ signals: private: float _foo; + TestEnum _baz; + TestFlags _bong; }; /// Another simple shared object. From 90e2145fc4d3bed08ea824a6857926b359cb3339 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 3 Jun 2014 17:17:02 -0700 Subject: [PATCH 3/8] Working on enum streaming. --- libraries/metavoxels/src/Bitstream.cpp | 230 ++++++++++++++++++------- libraries/metavoxels/src/Bitstream.h | 40 ++++- tests/metavoxels/src/MetavoxelTests.h | 2 +- 3 files changed, 208 insertions(+), 64 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index f3ba1cca4d..5ce04b272a 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -78,6 +78,21 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta for (const QMetaObject* superClass = metaObject; superClass; superClass = superClass->superClass()) { getMetaObjectSubClasses().insert(superClass, metaObject); } + + // register the streamers for all enumerators + for (int i = 0; i < metaObject->enumeratorCount(); i++) { + QMetaEnum metaEnum = metaObject->enumerator(i); + const TypeStreamer*& streamer = getEnumStreamers()[QPair(metaEnum.scope(), metaEnum.name())]; + if (!streamer) { + int highestValue = 0; + for (int j = 0; j < metaEnum.keyCount(); j++) { + highestValue = qMax(highestValue, metaEnum.value(j)); + } + streamer = new EnumTypeStreamer(QByteArray(metaEnum.scope()) + "::" + metaEnum.name(), + highestValue == 0 ? 0 : 1 + (int)(log(highestValue) / log(2.0))); + } + } + return 0; } @@ -280,16 +295,8 @@ void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { } const QMetaObject* metaObject = value->metaObject(); _metaObjectStreamer << metaObject; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored(value)) { - continue; - } - const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); - if (streamer) { - streamer->writeDelta(*this, property.read(value), reference && metaObject == reference->metaObject() ? - property.read(reference) : QVariant()); - } + foreach (const PropertyWriter& propertyWriter, getPropertyWriters(metaObject)) { + propertyWriter.writeDelta(*this, value, reference); } } @@ -466,15 +473,8 @@ Bitstream& Bitstream::operator<<(const QObject* object) { } const QMetaObject* metaObject = object->metaObject(); _metaObjectStreamer << metaObject; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored(object)) { - continue; - } - const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); - if (streamer) { - streamer->write(*this, property.read(object)); - } + foreach (const PropertyWriter& propertyWriter, getPropertyWriters(metaObject)) { + propertyWriter.write(*this, object); } return *this; } @@ -558,25 +558,12 @@ Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { if (_metadataType == NO_METADATA) { return *this; } - int storedPropertyCount = 0; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored() && getTypeStreamers().contains(property.userType())) { - storedPropertyCount++; - } - } - *this << storedPropertyCount; + const QVector& propertyWriters = getPropertyWriters(metaObject); + *this << propertyWriters.size(); QCryptographicHash hash(QCryptographicHash::Md5); - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; - } - const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType()); - if (!typeStreamer) { - continue; - } - _typeStreamerStreamer << typeStreamer; + foreach (const PropertyWriter& propertyWriter, propertyWriters) { + _typeStreamerStreamer << propertyWriter.getStreamer(); + const QMetaProperty& property = propertyWriter.getProperty(); if (_metadataType == FULL_METADATA) { *this << QByteArray::fromRawData(property.name(), strlen(property.name())); } else { @@ -629,25 +616,18 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; if (metaObject) { - int propertyIndex = 0; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; + const QVector& propertyWriters = getPropertyWriters(metaObject); + if (propertyWriters.size() == properties.size()) { + for (int i = 0; i < propertyWriters.size(); i++) { + const PropertyWriter& propertyWriter = propertyWriters.at(i); + if (!properties.at(i).getReader().matchesExactly(propertyWriter.getStreamer())) { + matches = false; + break; + } + const QMetaProperty& property = propertyWriter.getProperty(); + hash.addData(property.name(), strlen(property.name()) + 1); } - const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType()); - if (!typeStreamer) { - continue; - } - if (propertyIndex >= properties.size() || - !properties.at(propertyIndex).getReader().matchesExactly(typeStreamer)) { - matches = false; - break; - } - hash.addData(property.name(), strlen(property.name()) + 1); - propertyIndex++; - } - if (propertyIndex != properties.size()) { + } else { matches = false; } } @@ -668,7 +648,7 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { *this << QByteArray(); return *this; } - const char* typeName = QMetaType::typeName(streamer->getType()); + const char* typeName = streamer->getName(); *this << QByteArray::fromRawData(typeName, strlen(typeName)); if (_metadataType == NO_METADATA) { return *this; @@ -679,6 +659,9 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { case TypeReader::SIMPLE_TYPE: return *this; + case TypeReader::ENUM_TYPE: + return *this << streamer->getBits(); + case TypeReader::LIST_TYPE: case TypeReader::SET_TYPE: return *this << streamer->getValueStreamer(); @@ -720,7 +703,15 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { } const TypeStreamer* streamer = _typeStreamerSubstitutions.value(typeName); if (!streamer) { - streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + int index = typeName.indexOf("::"); + if (index == -1) { + streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + } else { + int postIndex = index + 2; + streamer = getEnumStreamers().value(QPair( + QByteArray::fromRawData(typeName.constData(), index), + QByteArray::fromRawData(typeName.constData() + postIndex, typeName.size() - postIndex))); + } } if (!streamer) { qWarning() << "Unknown type name: " << typeName << "\n"; @@ -735,7 +726,13 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { case TypeReader::SIMPLE_TYPE: reader = TypeReader(typeName, streamer); return *this; - + + case TypeReader::ENUM_TYPE: { + int bits; + *this >> bits; + reader = TypeReader(typeName, streamer); + return *this; + } case TypeReader::LIST_TYPE: case TypeReader::SET_TYPE: { TypeReader valueReader; @@ -923,6 +920,31 @@ void Bitstream::clearSharedObject(QObject* object) { } } +const QVector& Bitstream::getPropertyWriters(const QMetaObject* metaObject) { + QVector& propertyWriters = _propertyWriters[metaObject]; + if (propertyWriters.isEmpty()) { + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored()) { + continue; + } + const TypeStreamer* streamer; + if (property.isEnumType()) { + QMetaEnum metaEnum = property.enumerator(); + streamer = getEnumStreamers().value(QPair( + QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), + QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); + } else { + streamer = getTypeStreamers().value(property.userType()); + } + if (streamer) { + propertyWriters.append(PropertyWriter(property, streamer)); + } + } + } + return propertyWriters; +} + QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; @@ -938,6 +960,11 @@ QHash& Bitstream::getTypeStreamers() { return typeStreamers; } +QHash, const TypeStreamer*>& Bitstream::getEnumStreamers() { + static QHash, const TypeStreamer*> enumStreamers; + return enumStreamers; +} + QVector Bitstream::getPropertyReaders(const QMetaObject* metaObject) { QVector propertyReaders; if (!metaObject) { @@ -948,9 +975,17 @@ QVector Bitstream::getPropertyReaders(const QMetaObject* metaObj if (!property.isStored()) { continue; } - const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType()); - if (typeStreamer) { - propertyReaders.append(PropertyReader(TypeReader(QByteArray(), typeStreamer), property)); + const TypeStreamer* streamer; + if (property.isEnumType()) { + QMetaEnum metaEnum = property.enumerator(); + streamer = getEnumStreamers().value(QPair( + QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), + QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); + } else { + streamer = getTypeStreamers().value(property.userType()); + } + if (streamer) { + propertyReaders.append(PropertyReader(TypeReader(QByteArray(), streamer), property)); } } return propertyReaders; @@ -1225,6 +1260,10 @@ MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : TypeStreamer::~TypeStreamer() { } +const char* TypeStreamer::getName() const { + return QMetaType::typeName(_type); +} + const QVector& TypeStreamer::getMetaFields() const { static QVector emptyMetaFields; return emptyMetaFields; @@ -1246,6 +1285,10 @@ TypeReader::Type TypeStreamer::getReaderType() const { return TypeReader::SIMPLE_TYPE; } +int TypeStreamer::getBits() const { + return 0; +} + const TypeStreamer* TypeStreamer::getKeyStreamer() const { return NULL; } @@ -1290,3 +1333,68 @@ QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject) { return debug << (metaObject ? metaObject->className() : "null"); } +EnumTypeStreamer::EnumTypeStreamer(const QByteArray& name, int bits) : + _name(name), + _bits(bits) { +} + +const char* EnumTypeStreamer::getName() const { + return _name.constData(); +} + +TypeReader::Type EnumTypeStreamer::getReaderType() const { + return TypeReader::ENUM_TYPE; +} + +int EnumTypeStreamer::getBits() const { + return _bits; +} + +bool EnumTypeStreamer::equal(const QVariant& first, const QVariant& second) const { + return first.toInt() == second.toInt(); +} + +void EnumTypeStreamer::write(Bitstream& out, const QVariant& value) const { + int intValue = value.toInt(); + out.write(&intValue, _bits); +} + +QVariant EnumTypeStreamer::read(Bitstream& in) const { + int intValue = 0; + in.read(&intValue, _bits); + return intValue; +} + +void EnumTypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { + int intValue = value.toInt(), intReference = reference.toInt(); + if (intValue == intReference) { + out << false; + } else { + out << true; + out.write(&intValue, _bits); + } +} + +void EnumTypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { + bool changed; + in >> changed; + if (changed) { + int intValue = 0; + in.read(&intValue, _bits); + value = intValue; + } else { + value = reference; + } +} + +void EnumTypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { + int intValue = value.toInt(); + out.write(&intValue, _bits); +} + +void EnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { + int intValue = 0; + in.read(&intValue, _bits); + value = intValue; +} + diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 7e1551346a..3ea5eb7150 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -38,6 +38,7 @@ class FieldReader; class ObjectReader; class OwnedAttributeValue; class PropertyReader; +class PropertyWriter; class TypeReader; class TypeStreamer; @@ -396,6 +397,8 @@ private slots: private: + const QVector& getPropertyWriters(const QMetaObject* metaObject); + QDataStream& _underlying; quint8 _byte; int _position; @@ -415,9 +418,12 @@ private: QHash _metaObjectSubstitutions; QHash _typeStreamerSubstitutions; + QHash > _propertyWriters; + static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); + static QHash, const TypeStreamer*>& getEnumStreamers(); static QVector getPropertyReaders(const QMetaObject* metaObject); }; @@ -708,7 +714,7 @@ typedef QSharedPointer TypeReaderPointer; class TypeReader { public: - enum Type { SIMPLE_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE }; + enum Type { SIMPLE_TYPE, ENUM_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE }; TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, bool exactMatch = true, Type type = SIMPLE_TYPE, const TypeReaderPointer& keyReader = TypeReaderPointer(), @@ -804,12 +810,15 @@ private: QMetaProperty _property; }; -/// Contains the information necessary to obtain an object property and write it to the stream. +/// Contains the information required to obtain an object property and write it to the stream. class PropertyWriter { public: PropertyWriter(const QMetaProperty& property = QMetaProperty(), const TypeStreamer* streamer = NULL); + const QMetaProperty& getProperty() const { return _property; } + const TypeStreamer* getStreamer() const { return _streamer; } + void write(Bitstream& out, const QObject* object) const; void writeDelta(Bitstream& out, const QObject* object, const QObject* reference) const; @@ -848,6 +857,8 @@ public: void setType(int type) { _type = type; } int getType() const { return _type; } + virtual const char* getName() const; + virtual bool equal(const QVariant& first, const QVariant& second) const = 0; virtual void write(Bitstream& out, const QVariant& value) const = 0; @@ -866,6 +877,8 @@ public: virtual TypeReader::Type getReaderType() const; + virtual int getBits() const; + virtual const TypeStreamer* getKeyStreamer() const; virtual const TypeStreamer* getValueStreamer() const; @@ -905,6 +918,29 @@ public: T rawValue; in.readRawDelta(rawValue, reference.value()); value = QVariant::fromValue(rawValue); } }; +/// A streamer class for enumerated types. +class EnumTypeStreamer : public TypeStreamer { +public: + + EnumTypeStreamer(const QByteArray& name, int bits); + + virtual const char* getName() const; + virtual TypeReader::Type getReaderType() const; + virtual int getBits() const; + virtual bool equal(const QVariant& first, const QVariant& second) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; + virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; + virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; + virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; + virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; + +private: + + QByteArray _name; + int _bits; +}; + /// A streamer for types compiled by mtc. template class StreamableTypeStreamer : public SimpleTypeStreamer { public: diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index 6e0b857328..a4aa428a1e 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -80,7 +80,7 @@ public: enum TestEnum { FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM }; - enum TestFlag { NO_TEST_FLAGS = 0x0, FIRST_TEST_FLAG = 0x01, SECOND_TEST_FLAG = 0x02, THIRD_TEST_FLAG = 0x03 }; + enum TestFlag { NO_TEST_FLAGS = 0x0, FIRST_TEST_FLAG = 0x01, SECOND_TEST_FLAG = 0x02, THIRD_TEST_FLAG = 0x04 }; Q_DECLARE_FLAGS(TestFlags, TestFlag) Q_INVOKABLE TestSharedObjectA(float foo = 0.0f, TestEnum baz = FIRST_TEST_ENUM, TestFlags bong = 0); From 1f2a75fc453a6b6c16693ad2ec650c425313e691 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 3 Jun 2014 18:27:56 -0700 Subject: [PATCH 4/8] More enum streaming bits. --- libraries/metavoxels/src/Bitstream.cpp | 19 ++++++++++++------- libraries/metavoxels/src/Bitstream.h | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 5ce04b272a..0093e419f8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -730,7 +730,11 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { case TypeReader::ENUM_TYPE: { int bits; *this >> bits; - reader = TypeReader(typeName, streamer); + if (streamer && streamer->getReaderType() == type) { + reader = TypeReader(typeName, streamer); + } else { + reader = TypeReader(typeName, streamer, false, TypeReader::ENUM_TYPE, bits); + } return *this; } case TypeReader::LIST_TYPE: @@ -741,7 +745,7 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { valueReader.matchesExactly(streamer->getValueStreamer())) { reader = TypeReader(typeName, streamer); } else { - reader = TypeReader(typeName, streamer, false, (TypeReader::Type)type, TypeReaderPointer(), + reader = TypeReader(typeName, streamer, false, (TypeReader::Type)type, 0, TypeReaderPointer(), TypeReaderPointer(new TypeReader(valueReader))); } return *this; @@ -754,7 +758,7 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { valueReader.matchesExactly(streamer->getValueStreamer())) { reader = TypeReader(typeName, streamer); } else { - reader = TypeReader(typeName, streamer, false, TypeReader::MAP_TYPE, + reader = TypeReader(typeName, streamer, false, TypeReader::MAP_TYPE, 0, TypeReaderPointer(new TypeReader(keyReader)), TypeReaderPointer(new TypeReader(valueReader))); } return *this; @@ -814,14 +818,14 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { const QVector& localFields = streamer->getMetaFields(); if (fieldCount != localFields.size()) { reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, - TypeReaderPointer(), TypeReaderPointer(), fields); + 0, 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, TypeReader::STREAMABLE_TYPE, - TypeReaderPointer(), TypeReaderPointer(), fields); + 0, TypeReaderPointer(), TypeReaderPointer(), fields); return *this; } } @@ -829,7 +833,7 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { return *this; } reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, - TypeReaderPointer(), TypeReaderPointer(), fields); + 0, TypeReaderPointer(), TypeReaderPointer(), fields); return *this; } @@ -991,12 +995,13 @@ QVector Bitstream::getPropertyReaders(const QMetaObject* metaObj return propertyReaders; } -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, Type type, +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, Type type, int bits, const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader, const QVector& fields) : _typeName(typeName), _streamer(streamer), _exactMatch(exactMatch), _type(type), + _bits(bits), _keyReader(keyReader), _valueReader(valueReader), _fields(fields) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 3ea5eb7150..02be736e02 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -717,7 +717,7 @@ public: enum Type { SIMPLE_TYPE, ENUM_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE }; TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, bool exactMatch = true, - Type type = SIMPLE_TYPE, const TypeReaderPointer& keyReader = TypeReaderPointer(), + Type type = SIMPLE_TYPE, int bits = 0, const TypeReaderPointer& keyReader = TypeReaderPointer(), const TypeReaderPointer& valueReader = TypeReaderPointer(), const QVector& fields = QVector()); @@ -739,6 +739,7 @@ private: const TypeStreamer* _streamer; bool _exactMatch; Type _type; + int _bits; TypeReaderPointer _keyReader; TypeReaderPointer _valueReader; QVector _fields; From 54a5bc3a497d14dbbadb189ede1cbdfe46bfebb1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 4 Jun 2014 17:24:43 -0700 Subject: [PATCH 5/8] More enum stuff, with tests. --- libraries/metavoxels/src/Bitstream.cpp | 225 +++++++++++++++++++----- libraries/metavoxels/src/Bitstream.h | 28 ++- tests/metavoxels/src/MetavoxelTests.cpp | 29 ++- tests/metavoxels/src/MetavoxelTests.h | 23 ++- 4 files changed, 250 insertions(+), 55 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 0093e419f8..81ae371fb8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -71,6 +71,10 @@ IDStreamer& IDStreamer::operator>>(int& value) { return *this; } +static QByteArray getEnumName(const QMetaEnum& metaEnum) { + return QByteArray(metaEnum.scope()) + "::" + metaEnum.name(); +} + int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { getMetaObjects().insert(className, metaObject); @@ -84,12 +88,7 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta QMetaEnum metaEnum = metaObject->enumerator(i); const TypeStreamer*& streamer = getEnumStreamers()[QPair(metaEnum.scope(), metaEnum.name())]; if (!streamer) { - int highestValue = 0; - for (int j = 0; j < metaEnum.keyCount(); j++) { - highestValue = qMax(highestValue, metaEnum.value(j)); - } - streamer = new EnumTypeStreamer(QByteArray(metaEnum.scope()) + "::" + metaEnum.name(), - highestValue == 0 ? 0 : 1 + (int)(log(highestValue) / log(2.0))); + getEnumStreamersByName().insert(getEnumName(metaEnum), streamer = new EnumTypeStreamer(metaEnum)); } } @@ -135,6 +134,14 @@ void Bitstream::addTypeSubstitution(const QByteArray& typeName, int type) { _typeStreamerSubstitutions.insert(typeName, getTypeStreamers().value(type)); } +void Bitstream::addTypeSubstitution(const QByteArray& typeName, const char* replacementTypeName) { + const TypeStreamer* streamer = getTypeStreamers().value(QMetaType::type(replacementTypeName)); + if (!streamer) { + streamer = getEnumStreamersByName().value(replacementTypeName); + } + _typeStreamerSubstitutions.insert(typeName, streamer); +} + const int LAST_BIT_POSITION = BITS_IN_BYTE - 1; Bitstream& Bitstream::write(const void* data, int bits, int offset) { @@ -659,9 +666,27 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { case TypeReader::SIMPLE_TYPE: return *this; - case TypeReader::ENUM_TYPE: - return *this << streamer->getBits(); - + case TypeReader::ENUM_TYPE: { + QMetaEnum metaEnum = streamer->getMetaEnum(); + if (_metadataType == FULL_METADATA) { + *this << metaEnum.keyCount(); + for (int i = 0; i < metaEnum.keyCount(); i++) { + *this << QByteArray::fromRawData(metaEnum.key(i), strlen(metaEnum.key(i))); + *this << metaEnum.value(i); + } + } else { + *this << streamer->getBits(); + QCryptographicHash hash(QCryptographicHash::Md5); + for (int i = 0; i < metaEnum.keyCount(); i++) { + hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); + qint32 value = metaEnum.value(i); + hash.addData((const char*)&value, sizeof(qint32)); + } + QByteArray hashResult = hash.result(); + write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + } + return *this; + } case TypeReader::LIST_TYPE: case TypeReader::SET_TYPE: return *this << streamer->getValueStreamer(); @@ -694,6 +719,10 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { return *this; } +static int getBitsForHighestValue(int highestValue) { + return (highestValue == 0) ? 0 : 1 + (int)(log(highestValue) / log(2.0)); +} + Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray typeName; *this >> typeName; @@ -703,14 +732,9 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { } const TypeStreamer* streamer = _typeStreamerSubstitutions.value(typeName); if (!streamer) { - int index = typeName.indexOf("::"); - if (index == -1) { - streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); - } else { - int postIndex = index + 2; - streamer = getEnumStreamers().value(QPair( - QByteArray::fromRawData(typeName.constData(), index), - QByteArray::fromRawData(typeName.constData() + postIndex, typeName.size() - postIndex))); + streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + if (!streamer) { + streamer = getEnumStreamersByName().value(typeName); } } if (!streamer) { @@ -728,12 +752,50 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { return *this; case TypeReader::ENUM_TYPE: { - int bits; - *this >> bits; - if (streamer && streamer->getReaderType() == type) { - reader = TypeReader(typeName, streamer); + if (_metadataType == FULL_METADATA) { + int keyCount; + *this >> keyCount; + QMetaEnum metaEnum = (streamer && streamer->getReaderType() == TypeReader::ENUM_TYPE) ? + streamer->getMetaEnum() : QMetaEnum(); + QHash mappings; + bool matches = (keyCount == metaEnum.keyCount()); + int highestValue = 0; + for (int i = 0; i < keyCount; i++) { + QByteArray key; + int value; + *this >> key >> value; + highestValue = qMax(value, highestValue); + int localValue = metaEnum.keyToValue(key); + if (localValue != -1) { + mappings.insert(value, localValue); + } + matches &= (value == localValue); + } + if (matches) { + reader = TypeReader(typeName, streamer); + } else { + reader = TypeReader(typeName, streamer, getBitsForHighestValue(highestValue), mappings); + } } else { - reader = TypeReader(typeName, streamer, false, TypeReader::ENUM_TYPE, bits); + int bits; + *this >> bits; + QCryptographicHash hash(QCryptographicHash::Md5); + if (streamer && streamer->getReaderType() == TypeReader::ENUM_TYPE) { + QMetaEnum metaEnum = streamer->getMetaEnum(); + for (int i = 0; i < metaEnum.keyCount(); i++) { + hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); + qint32 value = metaEnum.value(i); + hash.addData((const char*)&value, sizeof(qint32)); + } + } + QByteArray localHashResult = hash.result(); + QByteArray remoteHashResult(localHashResult.size(), 0); + read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); + if (localHashResult == remoteHashResult) { + reader = TypeReader(typeName, streamer); + } else { + reader = TypeReader(typeName, streamer, bits, QHash()); + } } return *this; } @@ -745,7 +807,7 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { valueReader.matchesExactly(streamer->getValueStreamer())) { reader = TypeReader(typeName, streamer); } else { - reader = TypeReader(typeName, streamer, false, (TypeReader::Type)type, 0, TypeReaderPointer(), + reader = TypeReader(typeName, streamer, (TypeReader::Type)type, TypeReaderPointer(new TypeReader(valueReader))); } return *this; @@ -758,8 +820,8 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { valueReader.matchesExactly(streamer->getValueStreamer())) { reader = TypeReader(typeName, streamer); } else { - reader = TypeReader(typeName, streamer, false, TypeReader::MAP_TYPE, 0, - TypeReaderPointer(new TypeReader(keyReader)), TypeReaderPointer(new TypeReader(valueReader))); + reader = TypeReader(typeName, streamer, TypeReaderPointer(new TypeReader(keyReader)), + TypeReaderPointer(new TypeReader(valueReader))); } return *this; } @@ -817,23 +879,20 @@ 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& localFields = streamer->getMetaFields(); if (fieldCount != localFields.size()) { - reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, - 0, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, 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, TypeReader::STREAMABLE_TYPE, - 0, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, fields); return *this; } } reader = TypeReader(typeName, streamer); return *this; } - reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, - 0, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, fields); return *this; } @@ -969,6 +1028,11 @@ QHash, const TypeStreamer*>& Bitstream::getEnumStr return enumStreamers; } +QHash& Bitstream::getEnumStreamersByName() { + static QHash enumStreamersByName; + return enumStreamersByName; +} + QVector Bitstream::getPropertyReaders(const QMetaObject* metaObject) { QVector propertyReaders; if (!metaObject) { @@ -995,24 +1059,62 @@ QVector Bitstream::getPropertyReaders(const QMetaObject* metaObj return propertyReaders; } -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, Type type, int bits, - const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader, const QVector& fields) : +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer) : _typeName(typeName), _streamer(streamer), - _exactMatch(exactMatch), - _type(type), + _exactMatch(true) { +} + +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash& mappings) : + _typeName(typeName), + _streamer(streamer), + _exactMatch(false), + _type(ENUM_TYPE), _bits(bits), - _keyReader(keyReader), - _valueReader(valueReader), + _mappings(mappings) { +} + +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector& fields) : + _typeName(typeName), + _streamer(streamer), + _exactMatch(false), + _type(STREAMABLE_TYPE), _fields(fields) { } +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, + Type type, const TypeReaderPointer& valueReader) : + _typeName(typeName), + _streamer(streamer), + _exactMatch(false), + _type(type), + _valueReader(valueReader) { +} + +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, + const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader) : + _typeName(typeName), + _streamer(streamer), + _exactMatch(false), + _type(MAP_TYPE), + _keyReader(keyReader), + _valueReader(valueReader) { +} + QVariant TypeReader::read(Bitstream& in) const { if (_exactMatch) { return _streamer->read(in); } QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant(); switch (_type) { + case ENUM_TYPE: { + int value = 0; + in.read(&value, _bits); + if (_streamer) { + _streamer->setEnumValue(object, value, _mappings); + } + break; + } case STREAMABLE_TYPE: { foreach (const FieldReader& field, _fields) { field.read(in, _streamer, object); @@ -1069,6 +1171,14 @@ void TypeReader::readRawDelta(Bitstream& in, QVariant& object, const QVariant& r return; } switch (_type) { + case ENUM_TYPE: { + int value = 0; + in.read(&value, _bits); + if (_streamer) { + _streamer->setEnumValue(object, value, _mappings); + } + break; + } case STREAMABLE_TYPE: { foreach (const FieldReader& field, _fields) { field.readDelta(in, _streamer, object, reference); @@ -1269,6 +1379,10 @@ const char* TypeStreamer::getName() const { return QMetaType::typeName(_type); } +void TypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) const { + // nothing by default +} + const QVector& TypeStreamer::getMetaFields() const { static QVector emptyMetaFields; return emptyMetaFields; @@ -1294,6 +1408,10 @@ int TypeStreamer::getBits() const { return 0; } +QMetaEnum TypeStreamer::getMetaEnum() const { + return QMetaEnum(); +} + const TypeStreamer* TypeStreamer::getKeyStreamer() const { return NULL; } @@ -1338,9 +1456,17 @@ QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject) { return debug << (metaObject ? metaObject->className() : "null"); } -EnumTypeStreamer::EnumTypeStreamer(const QByteArray& name, int bits) : - _name(name), - _bits(bits) { +EnumTypeStreamer::EnumTypeStreamer(const QMetaEnum& metaEnum) : + _metaEnum(metaEnum), + _name(getEnumName(metaEnum)) { + + setType(QMetaType::Int); + + int highestValue = 0; + for (int j = 0; j < metaEnum.keyCount(); j++) { + highestValue = qMax(highestValue, metaEnum.value(j)); + } + _bits = getBitsForHighestValue(highestValue); } const char* EnumTypeStreamer::getName() const { @@ -1355,6 +1481,10 @@ int EnumTypeStreamer::getBits() const { return _bits; } +QMetaEnum EnumTypeStreamer::getMetaEnum() const { + return _metaEnum; +} + bool EnumTypeStreamer::equal(const QVariant& first, const QVariant& second) const { return first.toInt() == second.toInt(); } @@ -1403,3 +1533,18 @@ void EnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVaria value = intValue; } +void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) const { + if (_metaEnum.isFlag()) { + int combined = 0; + for (QHash::const_iterator it = mappings.constBegin(); it != mappings.constEnd(); it++) { + if (value & it.key()) { + combined |= it.value(); + } + } + object = combined; + + } else { + object = mappings.value(value); + } +} + diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 02be736e02..146713910f 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -236,6 +236,9 @@ public: /// Substitutes the supplied type for the given type name's default mapping. void addTypeSubstitution(const QByteArray& typeName, int type); + /// Substitutes the named type for the given type name's default mapping. + void addTypeSubstitution(const QByteArray& typeName, const char* replacementTypeName); + /// Writes a set of bits to the underlying stream. /// \param bits the number of bits to write /// \param offset the offset of the first bit @@ -424,6 +427,7 @@ private: static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); static QHash, const TypeStreamer*>& getEnumStreamers(); + static QHash& getEnumStreamersByName(); static QVector getPropertyReaders(const QMetaObject* metaObject); }; @@ -716,11 +720,18 @@ public: enum Type { SIMPLE_TYPE, ENUM_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE }; - TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, bool exactMatch = true, - Type type = SIMPLE_TYPE, int bits = 0, const TypeReaderPointer& keyReader = TypeReaderPointer(), - const TypeReaderPointer& valueReader = TypeReaderPointer(), - const QVector& fields = QVector()); + TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL); + + TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash& mappings); + + TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector& fields); + TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, Type type, + const TypeReaderPointer& valueReader); + + TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, + const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader); + const QByteArray& getTypeName() const { return _typeName; } const TypeStreamer* getStreamer() const { return _streamer; } @@ -740,6 +751,7 @@ private: bool _exactMatch; Type _type; int _bits; + QHash _mappings; TypeReaderPointer _keyReader; TypeReaderPointer _valueReader; QVector _fields; @@ -871,6 +883,8 @@ public: virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0; virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0; + virtual void setEnumValue(QVariant& object, int value, const QHash& mappings) const; + virtual const QVector& getMetaFields() const; virtual int getFieldIndex(const QByteArray& name) const; virtual void setField(QVariant& object, int index, const QVariant& value) const; @@ -879,6 +893,7 @@ public: virtual TypeReader::Type getReaderType() const; virtual int getBits() const; + virtual QMetaEnum getMetaEnum() const; virtual const TypeStreamer* getKeyStreamer() const; virtual const TypeStreamer* getValueStreamer() const; @@ -923,11 +938,12 @@ public: class EnumTypeStreamer : public TypeStreamer { public: - EnumTypeStreamer(const QByteArray& name, int bits); + EnumTypeStreamer(const QMetaEnum& metaEnum); virtual const char* getName() const; virtual TypeReader::Type getReaderType() const; virtual int getBits() const; + virtual QMetaEnum getMetaEnum() const; virtual bool equal(const QVariant& first, const QVariant& second) const; virtual void write(Bitstream& out, const QVariant& value) const; virtual QVariant read(Bitstream& in) const; @@ -935,9 +951,11 @@ public: virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; + virtual void setEnumValue(QVariant& object, int value, const QHash& mappings) const; private: + QMetaEnum _metaEnum; QByteArray _name; int _bits; }; diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 1b7eaf9a22..603f63b587 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -87,9 +87,11 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { QByteArray array; QDataStream outStream(&array, QIODevice::WriteOnly); Bitstream out(outStream, metadataType); - SharedObjectPointer testObjectWrittenA = new TestSharedObjectA(randFloat(), getRandomTestEnum(), getRandomTestFlags()); + SharedObjectPointer testObjectWrittenA = new TestSharedObjectA(randFloat(), TestSharedObjectA::SECOND_TEST_ENUM, + TestSharedObjectA::TestFlags(TestSharedObjectA::FIRST_TEST_FLAG | TestSharedObjectA::THIRD_TEST_FLAG)); out << testObjectWrittenA; - SharedObjectPointer testObjectWrittenB = new TestSharedObjectB(randFloat(), createRandomBytes()); + SharedObjectPointer testObjectWrittenB = new TestSharedObjectB(randFloat(), createRandomBytes(), + TestSharedObjectB::THIRD_TEST_ENUM, TestSharedObjectB::SECOND_TEST_FLAG); out << testObjectWrittenB; TestMessageC messageWritten = createRandomMessageC(); out << QVariant::fromValue(messageWritten); @@ -102,6 +104,10 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { in.addMetaObjectSubstitution("TestSharedObjectA", &TestSharedObjectB::staticMetaObject); in.addMetaObjectSubstitution("TestSharedObjectB", &TestSharedObjectA::staticMetaObject); in.addTypeSubstitution("TestMessageC", TestMessageA::Type); + in.addTypeSubstitution("TestSharedObjectA::TestEnum", "TestSharedObjectB::TestEnum"); + in.addTypeSubstitution("TestSharedObjectB::TestEnum", "TestSharedObjectA::TestEnum"); + in.addTypeSubstitution("TestSharedObjectA::TestFlags", "TestSharedObjectB::TestFlags"); + in.addTypeSubstitution("TestSharedObjectB::TestFlags", "TestSharedObjectA::TestFlags"); SharedObjectPointer testObjectReadA; in >> testObjectReadA; @@ -109,8 +115,11 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { qDebug() << "Wrong class for A" << testObjectReadA << metadataType; return true; } - if (metadataType == Bitstream::FULL_METADATA && static_cast(testObjectWrittenA.data())->getFoo() != - static_cast(testObjectReadA.data())->getFoo()) { + if (metadataType == Bitstream::FULL_METADATA && (static_cast(testObjectWrittenA.data())->getFoo() != + static_cast(testObjectReadA.data())->getFoo() || + static_cast(testObjectReadA.data())->getBaz() != TestSharedObjectB::SECOND_TEST_ENUM || + static_cast(testObjectReadA.data())->getBong() != + TestSharedObjectB::TestFlags(TestSharedObjectB::FIRST_TEST_FLAG | TestSharedObjectB::THIRD_TEST_FLAG))) { QDebug debug = qDebug() << "Failed to transfer shared field from A to B"; testObjectWrittenA->dump(debug); testObjectReadA->dump(debug); @@ -123,8 +132,10 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { qDebug() << "Wrong class for B" << testObjectReadB << metadataType; return true; } - if (metadataType == Bitstream::FULL_METADATA && static_cast(testObjectWrittenB.data())->getFoo() != - static_cast(testObjectReadB.data())->getFoo()) { + if (metadataType == Bitstream::FULL_METADATA && (static_cast(testObjectWrittenB.data())->getFoo() != + static_cast(testObjectReadB.data())->getFoo() || + static_cast(testObjectReadB.data())->getBaz() != TestSharedObjectA::THIRD_TEST_ENUM || + static_cast(testObjectReadB.data())->getBong() != TestSharedObjectA::SECOND_TEST_FLAG)) { QDebug debug = qDebug() << "Failed to transfer shared field from B to A"; testObjectWrittenB->dump(debug); testObjectReadB->dump(debug); @@ -433,9 +444,11 @@ void TestSharedObjectA::setFoo(float foo) { } } -TestSharedObjectB::TestSharedObjectB(float foo, const QByteArray& bar) : +TestSharedObjectB::TestSharedObjectB(float foo, const QByteArray& bar, TestEnum baz, TestFlags bong) : _foo(foo), - _bar(bar) { + _bar(bar), + _baz(baz), + _bong(bong) { sharedObjectsCreated++; } diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index a4aa428a1e..5e020b1e60 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -109,12 +109,23 @@ private: /// Another simple shared object. class TestSharedObjectB : public SharedObject { Q_OBJECT + Q_ENUMS(TestEnum) + Q_FLAGS(TestFlag TestFlags) Q_PROPERTY(float foo READ getFoo WRITE setFoo) Q_PROPERTY(QByteArray bar READ getBar WRITE setBar) - + Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz) + Q_PROPERTY(TestFlags bong READ getBong WRITE setBong) + public: - Q_INVOKABLE TestSharedObjectB(float foo = 0.0f, const QByteArray& bar = QByteArray()); + enum TestEnum { ZEROTH_TEST_ENUM, FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM, FOURTH_TEST_ENUM }; + + enum TestFlag { NO_TEST_FLAGS = 0x0, ZEROTH_TEST_FLAG = 0x01, FIRST_TEST_FLAG = 0x02, + SECOND_TEST_FLAG = 0x04, THIRD_TEST_FLAG = 0x08, FOURTH_TEST_FLAG = 0x10 }; + Q_DECLARE_FLAGS(TestFlags, TestFlag) + + Q_INVOKABLE TestSharedObjectB(float foo = 0.0f, const QByteArray& bar = QByteArray(), + TestEnum baz = FIRST_TEST_ENUM, TestFlags bong = 0); virtual ~TestSharedObjectB(); void setFoo(float foo) { _foo = foo; } @@ -123,10 +134,18 @@ public: void setBar(const QByteArray& bar) { _bar = bar; } const QByteArray& getBar() const { return _bar; } + void setBaz(TestEnum baz) { _baz = baz; } + TestEnum getBaz() const { return _baz; } + + void setBong(TestFlags bong) { _bong = bong; } + TestFlags getBong() const { return _bong; } + private: float _foo; QByteArray _bar; + TestEnum _baz; + TestFlags _bong; }; /// A simple test message. From 9ec2a5aec17b095db8648bc9abbae40351dace41 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 4 Jun 2014 17:39:42 -0700 Subject: [PATCH 6/8] Avoid ambiguous call to log on Windows. --- libraries/metavoxels/src/Bitstream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 81ae371fb8..cf9fa25d96 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -720,7 +720,7 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { } static int getBitsForHighestValue(int highestValue) { - return (highestValue == 0) ? 0 : 1 + (int)(log(highestValue) / log(2.0)); + return (highestValue == 0) ? 0 : 1 + (int)(glm::log(highestValue) / glm::log(2.0)); } Bitstream& Bitstream::operator>(TypeReader& reader) { From 37382304f7d1501658a14e75b418e06fd339880c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 4 Jun 2014 17:41:36 -0700 Subject: [PATCH 7/8] Perhaps this will avoid the ambiguity in Windows. --- libraries/metavoxels/src/Bitstream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index cf9fa25d96..dc74594816 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -720,7 +720,7 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { } static int getBitsForHighestValue(int highestValue) { - return (highestValue == 0) ? 0 : 1 + (int)(glm::log(highestValue) / glm::log(2.0)); + return (highestValue == 0) ? 0 : 1 + (int)(log((double)highestValue) / log(2.0)); } Bitstream& Bitstream::operator>(TypeReader& reader) { From fd2893bf6df8e6dba6d5b237a715180a557212d9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 4 Jun 2014 18:23:31 -0700 Subject: [PATCH 8/8] Get rid of the log calls entirely; use the same code that we use for the ID streamer. --- libraries/metavoxels/src/Bitstream.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index dc74594816..30d34580d7 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -47,11 +47,19 @@ IDStreamer::IDStreamer(Bitstream& stream) : _bits(1) { } -void IDStreamer::setBitsFromValue(int value) { - _bits = 1; - while (value >= (1 << _bits) - 1) { - _bits++; +static int getBitsForHighestValue(int highestValue) { + // if this turns out to be a bottleneck, there are fancier ways to do it (get the position of the highest set bit): + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious + int bits = 0; + while (highestValue != 0) { + bits++; + highestValue >>= 1; } + return bits; +} + +void IDStreamer::setBitsFromValue(int value) { + _bits = getBitsForHighestValue(value + 1); } IDStreamer& IDStreamer::operator<<(int value) { @@ -719,10 +727,6 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { return *this; } -static int getBitsForHighestValue(int highestValue) { - return (highestValue == 0) ? 0 : 1 + (int)(log((double)highestValue) / log(2.0)); -} - Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray typeName; *this >> typeName;