mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-09 13:12:40 +02:00
Merge pull request #2985 from ey6es/metavoxels
Pulled in fixes for delta-encoding from "deltatars," added support for version-resilient streaming of Qt enums/flags.
This commit is contained in:
commit
b59b26d87b
7 changed files with 702 additions and 130 deletions
|
@ -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) {
|
||||
|
@ -71,6 +79,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);
|
||||
|
||||
|
@ -78,6 +90,16 @@ 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<QByteArray, QByteArray>(metaEnum.scope(), metaEnum.name())];
|
||||
if (!streamer) {
|
||||
getEnumStreamersByName().insert(getEnumName(metaEnum), streamer = new EnumTypeStreamer(metaEnum));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -120,6 +142,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) {
|
||||
|
@ -193,7 +223,7 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) {
|
|||
continue;
|
||||
}
|
||||
connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*)));
|
||||
QPointer<SharedObject>& reference = _sharedObjectReferences[it.key()->getID()];
|
||||
QPointer<SharedObject>& reference = _sharedObjectReferences[it.key()->getOriginID()];
|
||||
if (reference) {
|
||||
_sharedObjectStreamer.removePersistentID(reference);
|
||||
reference->disconnect(this);
|
||||
|
@ -227,7 +257,7 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) {
|
|||
if (!it.value()) {
|
||||
continue;
|
||||
}
|
||||
QPointer<SharedObject>& reference = _sharedObjectReferences[it.value()->getRemoteID()];
|
||||
QPointer<SharedObject>& reference = _sharedObjectReferences[it.value()->getRemoteOriginID()];
|
||||
if (reference) {
|
||||
_sharedObjectStreamer.removePersistentValue(reference.data());
|
||||
}
|
||||
|
@ -280,16 +310,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,6 +433,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 +450,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;
|
||||
}
|
||||
|
||||
|
@ -458,15 +488,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;
|
||||
}
|
||||
|
@ -550,25 +573,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<PropertyWriter>& 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 {
|
||||
|
@ -621,25 +631,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<PropertyWriter>& 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;
|
||||
}
|
||||
}
|
||||
|
@ -656,7 +659,11 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
|||
}
|
||||
|
||||
Bitstream& Bitstream::operator<(const TypeStreamer* streamer) {
|
||||
const char* typeName = QMetaType::typeName(streamer->getType());
|
||||
if (!streamer) {
|
||||
*this << QByteArray();
|
||||
return *this;
|
||||
}
|
||||
const char* typeName = streamer->getName();
|
||||
*this << QByteArray::fromRawData(typeName, strlen(typeName));
|
||||
if (_metadataType == NO_METADATA) {
|
||||
return *this;
|
||||
|
@ -667,6 +674,27 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) {
|
|||
case TypeReader::SIMPLE_TYPE:
|
||||
return *this;
|
||||
|
||||
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();
|
||||
|
@ -702,9 +730,16 @@ 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()));
|
||||
if (!streamer) {
|
||||
streamer = getEnumStreamersByName().value(typeName);
|
||||
}
|
||||
}
|
||||
if (!streamer) {
|
||||
qWarning() << "Unknown type name: " << typeName << "\n";
|
||||
|
@ -719,7 +754,55 @@ Bitstream& Bitstream::operator>(TypeReader& reader) {
|
|||
case TypeReader::SIMPLE_TYPE:
|
||||
reader = TypeReader(typeName, streamer);
|
||||
return *this;
|
||||
|
||||
|
||||
case TypeReader::ENUM_TYPE: {
|
||||
if (_metadataType == FULL_METADATA) {
|
||||
int keyCount;
|
||||
*this >> keyCount;
|
||||
QMetaEnum metaEnum = (streamer && streamer->getReaderType() == TypeReader::ENUM_TYPE) ?
|
||||
streamer->getMetaEnum() : QMetaEnum();
|
||||
QHash<int, int> 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 {
|
||||
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<int, int>());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
case TypeReader::LIST_TYPE:
|
||||
case TypeReader::SET_TYPE: {
|
||||
TypeReader valueReader;
|
||||
|
@ -728,7 +811,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, (TypeReader::Type)type,
|
||||
TypeReaderPointer(new TypeReader(valueReader)));
|
||||
}
|
||||
return *this;
|
||||
|
@ -741,8 +824,8 @@ Bitstream& Bitstream::operator>(TypeReader& reader) {
|
|||
valueReader.matchesExactly(streamer->getValueStreamer())) {
|
||||
reader = TypeReader(typeName, streamer);
|
||||
} else {
|
||||
reader = TypeReader(typeName, streamer, false, TypeReader::MAP_TYPE,
|
||||
TypeReaderPointer(new TypeReader(keyReader)), TypeReaderPointer(new TypeReader(valueReader)));
|
||||
reader = TypeReader(typeName, streamer, TypeReaderPointer(new TypeReader(keyReader)),
|
||||
TypeReaderPointer(new TypeReader(valueReader)));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -800,23 +883,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<MetaField>& localFields = streamer->getMetaFields();
|
||||
if (fieldCount != localFields.size()) {
|
||||
reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE,
|
||||
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,
|
||||
TypeReaderPointer(), TypeReaderPointer(), fields);
|
||||
reader = TypeReader(typeName, streamer, fields);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
reader = TypeReader(typeName, streamer);
|
||||
return *this;
|
||||
}
|
||||
reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE,
|
||||
TypeReaderPointer(), TypeReaderPointer(), fields);
|
||||
reader = TypeReader(typeName, streamer, fields);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -847,9 +927,10 @@ Bitstream& Bitstream::operator<(const SharedObjectPointer& object) {
|
|||
return *this << (int)0;
|
||||
}
|
||||
*this << object->getID();
|
||||
QPointer<SharedObject> reference = _sharedObjectReferences.value(object->getID());
|
||||
*this << object->getOriginID();
|
||||
QPointer<SharedObject> 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 +944,9 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
|
|||
object = SharedObjectPointer();
|
||||
return *this;
|
||||
}
|
||||
QPointer<SharedObject> reference = _sharedObjectReferences.value(id);
|
||||
int originID;
|
||||
*this >> originID;
|
||||
QPointer<SharedObject> reference = _sharedObjectReferences.value(originID);
|
||||
QPointer<SharedObject>& pointer = _weakSharedObjectHash[id];
|
||||
if (pointer) {
|
||||
ObjectReader objectReader;
|
||||
|
@ -876,15 +959,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<SharedObject*>(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<SharedObject*>(pointer.data());
|
||||
|
@ -893,13 +980,38 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
|
|||
|
||||
void Bitstream::clearSharedObject(QObject* object) {
|
||||
SharedObject* sharedObject = static_cast<SharedObject*>(object);
|
||||
_sharedObjectReferences.remove(sharedObject->getID());
|
||||
_sharedObjectReferences.remove(sharedObject->getOriginID());
|
||||
int id = _sharedObjectStreamer.takePersistentID(sharedObject);
|
||||
if (id != 0) {
|
||||
emit sharedObjectCleared(id);
|
||||
}
|
||||
}
|
||||
|
||||
const QVector<PropertyWriter>& Bitstream::getPropertyWriters(const QMetaObject* metaObject) {
|
||||
QVector<PropertyWriter>& 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, QByteArray>(
|
||||
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<QByteArray, const QMetaObject*>& Bitstream::getMetaObjects() {
|
||||
static QHash<QByteArray, const QMetaObject*> metaObjects;
|
||||
return metaObjects;
|
||||
|
@ -915,6 +1027,16 @@ QHash<int, const TypeStreamer*>& Bitstream::getTypeStreamers() {
|
|||
return typeStreamers;
|
||||
}
|
||||
|
||||
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& Bitstream::getEnumStreamers() {
|
||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers;
|
||||
return enumStreamers;
|
||||
}
|
||||
|
||||
QHash<QByteArray, const TypeStreamer*>& Bitstream::getEnumStreamersByName() {
|
||||
static QHash<QByteArray, const TypeStreamer*> enumStreamersByName;
|
||||
return enumStreamersByName;
|
||||
}
|
||||
|
||||
QVector<PropertyReader> Bitstream::getPropertyReaders(const QMetaObject* metaObject) {
|
||||
QVector<PropertyReader> propertyReaders;
|
||||
if (!metaObject) {
|
||||
|
@ -925,31 +1047,78 @@ QVector<PropertyReader> 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, QByteArray>(
|
||||
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;
|
||||
}
|
||||
|
||||
TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, Type type,
|
||||
const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader, const QVector<FieldReader>& fields) :
|
||||
TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer) :
|
||||
_typeName(typeName),
|
||||
_streamer(streamer),
|
||||
_exactMatch(exactMatch),
|
||||
_type(type),
|
||||
_keyReader(keyReader),
|
||||
_valueReader(valueReader),
|
||||
_exactMatch(true) {
|
||||
}
|
||||
|
||||
TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash<int, int>& mappings) :
|
||||
_typeName(typeName),
|
||||
_streamer(streamer),
|
||||
_exactMatch(false),
|
||||
_type(ENUM_TYPE),
|
||||
_bits(bits),
|
||||
_mappings(mappings) {
|
||||
}
|
||||
|
||||
TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector<FieldReader>& 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);
|
||||
|
@ -1006,6 +1175,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);
|
||||
|
@ -1099,6 +1276,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 +1333,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) {
|
||||
|
@ -1172,6 +1357,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) {
|
||||
|
@ -1180,6 +1379,14 @@ MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) :
|
|||
TypeStreamer::~TypeStreamer() {
|
||||
}
|
||||
|
||||
const char* TypeStreamer::getName() const {
|
||||
return QMetaType::typeName(_type);
|
||||
}
|
||||
|
||||
void TypeStreamer::setEnumValue(QVariant& object, int value, const QHash<int, int>& mappings) const {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
const QVector<MetaField>& TypeStreamer::getMetaFields() const {
|
||||
static QVector<MetaField> emptyMetaFields;
|
||||
return emptyMetaFields;
|
||||
|
@ -1201,6 +1408,14 @@ TypeReader::Type TypeStreamer::getReaderType() const {
|
|||
return TypeReader::SIMPLE_TYPE;
|
||||
}
|
||||
|
||||
int TypeStreamer::getBits() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QMetaEnum TypeStreamer::getMetaEnum() const {
|
||||
return QMetaEnum();
|
||||
}
|
||||
|
||||
const TypeStreamer* TypeStreamer::getKeyStreamer() const {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1236,3 +1451,104 @@ 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");
|
||||
}
|
||||
|
||||
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 {
|
||||
return _name.constData();
|
||||
}
|
||||
|
||||
TypeReader::Type EnumTypeStreamer::getReaderType() const {
|
||||
return TypeReader::ENUM_TYPE;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash<int, int>& mappings) const {
|
||||
if (_metaEnum.isFlag()) {
|
||||
int combined = 0;
|
||||
for (QHash<int, int>::const_iterator it = mappings.constBegin(); it != mappings.constEnd(); it++) {
|
||||
if (value & it.key()) {
|
||||
combined |= it.value();
|
||||
}
|
||||
}
|
||||
object = combined;
|
||||
|
||||
} else {
|
||||
object = mappings.value(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ class FieldReader;
|
|||
class ObjectReader;
|
||||
class OwnedAttributeValue;
|
||||
class PropertyReader;
|
||||
class PropertyWriter;
|
||||
class TypeReader;
|
||||
class TypeStreamer;
|
||||
|
||||
|
@ -235,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
|
||||
|
@ -294,6 +298,9 @@ public:
|
|||
template<class T> void writeRawDelta(const QList<T>& value, const QList<T>& reference);
|
||||
template<class T> void readRawDelta(QList<T>& value, const QList<T>& reference);
|
||||
|
||||
template<class T> void writeRawDelta(const QVector<T>& value, const QVector<T>& reference);
|
||||
template<class T> void readRawDelta(QVector<T>& value, const QVector<T>& reference);
|
||||
|
||||
template<class T> void writeRawDelta(const QSet<T>& value, const QSet<T>& reference);
|
||||
template<class T> void readRawDelta(QSet<T>& value, const QSet<T>& reference);
|
||||
|
||||
|
@ -339,6 +346,9 @@ public:
|
|||
template<class T> Bitstream& operator<<(const QList<T>& list);
|
||||
template<class T> Bitstream& operator>>(QList<T>& list);
|
||||
|
||||
template<class T> Bitstream& operator<<(const QVector<T>& list);
|
||||
template<class T> Bitstream& operator>>(QVector<T>& list);
|
||||
|
||||
template<class T> Bitstream& operator<<(const QSet<T>& set);
|
||||
template<class T> Bitstream& operator>>(QSet<T>& set);
|
||||
|
||||
|
@ -390,6 +400,8 @@ private slots:
|
|||
|
||||
private:
|
||||
|
||||
const QVector<PropertyWriter>& getPropertyWriters(const QMetaObject* metaObject);
|
||||
|
||||
QDataStream& _underlying;
|
||||
quint8 _byte;
|
||||
int _position;
|
||||
|
@ -409,9 +421,13 @@ private:
|
|||
QHash<QByteArray, const QMetaObject*> _metaObjectSubstitutions;
|
||||
QHash<QByteArray, const TypeStreamer*> _typeStreamerSubstitutions;
|
||||
|
||||
QHash<const QMetaObject*, QVector<PropertyWriter> > _propertyWriters;
|
||||
|
||||
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
|
||||
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
||||
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& getEnumStreamers();
|
||||
static QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
||||
static QVector<PropertyReader> getPropertyReaders(const QMetaObject* metaObject);
|
||||
};
|
||||
|
||||
|
@ -472,6 +488,36 @@ template<class T> inline void Bitstream::readRawDelta(QList<T>& value, const QLi
|
|||
}
|
||||
}
|
||||
|
||||
template<class T> inline void Bitstream::writeRawDelta(const QVector<T>& value, const QVector<T>& 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<class T> inline void Bitstream::readRawDelta(QVector<T>& value, const QVector<T>& 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<class T> inline void Bitstream::writeRawDelta(const QSet<T>& value, const QSet<T>& reference) {
|
||||
int addedOrRemoved = 0;
|
||||
foreach (const T& element, value) {
|
||||
|
@ -600,6 +646,27 @@ template<class T> inline Bitstream& Bitstream::operator>>(QList<T>& list) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<class T> inline Bitstream& Bitstream::operator<<(const QVector<T>& vector) {
|
||||
*this << vector.size();
|
||||
foreach (const T& entry, vector) {
|
||||
*this << entry;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T> inline Bitstream& Bitstream::operator>>(QVector<T>& 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<class T> inline Bitstream& Bitstream::operator<<(const QSet<T>& set) {
|
||||
*this << set.size();
|
||||
foreach (const T& entry, set) {
|
||||
|
@ -651,13 +718,20 @@ typedef QSharedPointer<TypeReader> 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(),
|
||||
const TypeReaderPointer& valueReader = TypeReaderPointer(),
|
||||
const QVector<FieldReader>& fields = QVector<FieldReader>());
|
||||
TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL);
|
||||
|
||||
TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash<int, int>& mappings);
|
||||
|
||||
TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector<FieldReader>& 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; }
|
||||
|
||||
|
@ -676,6 +750,8 @@ private:
|
|||
const TypeStreamer* _streamer;
|
||||
bool _exactMatch;
|
||||
Type _type;
|
||||
int _bits;
|
||||
QHash<int, int> _mappings;
|
||||
TypeReaderPointer _keyReader;
|
||||
TypeReaderPointer _valueReader;
|
||||
QVector<FieldReader> _fields;
|
||||
|
@ -683,6 +759,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 +804,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:
|
||||
|
@ -743,6 +823,24 @@ private:
|
|||
QMetaProperty _property;
|
||||
};
|
||||
|
||||
/// 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;
|
||||
|
||||
private:
|
||||
|
||||
QMetaProperty _property;
|
||||
const TypeStreamer* _streamer;
|
||||
};
|
||||
|
||||
/// Describes a metatype field.
|
||||
class MetaField {
|
||||
public:
|
||||
|
@ -772,6 +870,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;
|
||||
|
@ -783,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<int, int>& mappings) const;
|
||||
|
||||
virtual const QVector<MetaField>& getMetaFields() const;
|
||||
virtual int getFieldIndex(const QByteArray& name) const;
|
||||
virtual void setField(QVariant& object, int index, const QVariant& value) const;
|
||||
|
@ -790,6 +892,9 @@ public:
|
|||
|
||||
virtual TypeReader::Type getReaderType() const;
|
||||
|
||||
virtual int getBits() const;
|
||||
virtual QMetaEnum getMetaEnum() const;
|
||||
|
||||
virtual const TypeStreamer* getKeyStreamer() const;
|
||||
virtual const TypeStreamer* getValueStreamer() const;
|
||||
|
||||
|
@ -808,6 +913,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 T> class SimpleTypeStreamer : public TypeStreamer {
|
||||
public:
|
||||
|
@ -818,11 +927,37 @@ public:
|
|||
virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const {
|
||||
out.writeDelta(value.value<T>(), reference.value<T>()); }
|
||||
virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const {
|
||||
in.readDelta(*static_cast<T*>(value.data()), reference.value<T>()); }
|
||||
T rawValue; in.readDelta(rawValue, reference.value<T>()); value = QVariant::fromValue(rawValue); }
|
||||
virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const {
|
||||
out.writeRawDelta(value.value<T>(), reference.value<T>()); }
|
||||
virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const {
|
||||
in.readRawDelta(*static_cast<T*>(value.data()), reference.value<T>()); }
|
||||
T rawValue; in.readRawDelta(rawValue, reference.value<T>()); value = QVariant::fromValue(rawValue); }
|
||||
};
|
||||
|
||||
/// A streamer class for enumerated types.
|
||||
class EnumTypeStreamer : public TypeStreamer {
|
||||
public:
|
||||
|
||||
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;
|
||||
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;
|
||||
virtual void setEnumValue(QVariant& object, int value, const QHash<int, int>& mappings) const;
|
||||
|
||||
private:
|
||||
|
||||
QMetaEnum _metaEnum;
|
||||
QByteArray _name;
|
||||
int _bits;
|
||||
};
|
||||
|
||||
/// A streamer for types compiled by mtc.
|
||||
|
@ -858,6 +993,22 @@ public:
|
|||
static_cast<QList<T>*>(object.data())->replace(index, value.value<T>()); }
|
||||
};
|
||||
|
||||
/// A streamer for vector types.
|
||||
template<class T> class CollectionTypeStreamer<QVector<T> > : public SimpleTypeStreamer<QVector<T> > {
|
||||
public:
|
||||
|
||||
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<QVector<T>*>(object.data())->append(value.value<T>()); }
|
||||
virtual void prune(QVariant& object, int size) const {
|
||||
QVector<T>* list = static_cast<QVector<T>*>(object.data()); list->erase(list->begin() + size, list->end()); }
|
||||
virtual QVariant getValue(const QVariant& object, int index) const {
|
||||
return QVariant::fromValue(static_cast<const QVector<T>*>(object.constData())->at(index)); }
|
||||
virtual void setValue(QVariant& object, int index, const QVariant& value) const {
|
||||
static_cast<QVector<T>*>(object.data())->replace(index, value.value<T>()); }
|
||||
};
|
||||
|
||||
/// A streamer for set types.
|
||||
template<class T> class CollectionTypeStreamer<QSet<T> > : public SimpleTypeStreamer<QSet<T> > {
|
||||
public:
|
||||
|
@ -940,6 +1091,13 @@ template<class T> int registerStreamableMetaType() {
|
|||
return type;
|
||||
}
|
||||
|
||||
/// Registers a collection type and its streamer.
|
||||
template<class T> int registerCollectionMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new CollectionTypeStreamer<T>());
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Flags a class as streamable (use as you would Q_OBJECT).
|
||||
#define STREAMABLE public: \
|
||||
static const int Type; \
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
#include "Bitstream.h"
|
||||
#include "AttributeRegistry.h"
|
||||
|
||||
class ReliableChannel;
|
||||
|
||||
|
|
|
@ -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<SharedObject*>(metaObject->newInstance());
|
||||
if (!target) {
|
||||
target = static_cast<SharedObject*>(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<SharedObjectPointer>()) {
|
||||
SharedObject* value = property.read(this).value<SharedObjectPointer>().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<QByteArray> 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,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());
|
||||
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);
|
||||
|
@ -79,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;
|
||||
|
||||
|
@ -86,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<TestSharedObjectA*>(testObjectWrittenA.data())->getFoo() !=
|
||||
static_cast<TestSharedObjectB*>(testObjectReadA.data())->getFoo()) {
|
||||
if (metadataType == Bitstream::FULL_METADATA && (static_cast<TestSharedObjectA*>(testObjectWrittenA.data())->getFoo() !=
|
||||
static_cast<TestSharedObjectB*>(testObjectReadA.data())->getFoo() ||
|
||||
static_cast<TestSharedObjectB*>(testObjectReadA.data())->getBaz() != TestSharedObjectB::SECOND_TEST_ENUM ||
|
||||
static_cast<TestSharedObjectB*>(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);
|
||||
|
@ -100,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<TestSharedObjectB*>(testObjectWrittenB.data())->getFoo() !=
|
||||
static_cast<TestSharedObjectA*>(testObjectReadB.data())->getFoo()) {
|
||||
if (metadataType == Bitstream::FULL_METADATA && (static_cast<TestSharedObjectB*>(testObjectWrittenB.data())->getFoo() !=
|
||||
static_cast<TestSharedObjectA*>(testObjectReadB.data())->getFoo() ||
|
||||
static_cast<TestSharedObjectA*>(testObjectReadB.data())->getBaz() != TestSharedObjectA::THIRD_TEST_ENUM ||
|
||||
static_cast<TestSharedObjectA*>(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);
|
||||
|
@ -175,7 +209,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 +427,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++;
|
||||
}
|
||||
|
||||
|
@ -408,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++;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = 0x04 };
|
||||
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,17 +102,30 @@ signals:
|
|||
private:
|
||||
|
||||
float _foo;
|
||||
TestEnum _baz;
|
||||
TestFlags _bong;
|
||||
};
|
||||
|
||||
/// 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; }
|
||||
|
@ -106,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.
|
||||
|
|
Loading…
Reference in a new issue