Merge branch 'master' of https://github.com/highfidelity/hifi into OculusOverlay

This commit is contained in:
Dev5 2014-06-11 10:10:43 -07:00
commit 98bd7c02e9
6 changed files with 746 additions and 32 deletions

View file

@ -14,6 +14,7 @@
#include <QCryptographicHash>
#include <QDataStream>
#include <QMetaType>
#include <QScriptValueIterator>
#include <QUrl>
#include <QtDebug>
@ -30,6 +31,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(uint)
REGISTER_SIMPLE_TYPE_STREAMER(float)
REGISTER_SIMPLE_TYPE_STREAMER(QByteArray)
REGISTER_SIMPLE_TYPE_STREAMER(QColor)
REGISTER_SIMPLE_TYPE_STREAMER(QScriptValue)
REGISTER_SIMPLE_TYPE_STREAMER(QString)
REGISTER_SIMPLE_TYPE_STREAMER(QUrl)
REGISTER_SIMPLE_TYPE_STREAMER(QVariantList)
@ -285,6 +287,12 @@ void Bitstream::writeDelta(const QVariant& value, const QVariant& reference) {
streamer->writeRawDelta(*this, value, reference);
}
void Bitstream::writeRawDelta(const QVariant& value, const QVariant& reference) {
const TypeStreamer* streamer = getTypeStreamers().value(value.userType());
_typeStreamerStreamer << streamer;
streamer->writeRawDelta(*this, value, reference);
}
void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) {
TypeReader typeReader;
_typeStreamerStreamer >> typeReader;
@ -309,6 +317,272 @@ void Bitstream::readRawDelta(QObject*& value, const QObject* reference) {
value = objectReader.readDelta(*this, reference);
}
void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& reference) {
if (reference.isUndefined() || reference.isNull()) {
*this << value;
} else if (reference.isBool()) {
if (value.isBool()) {
*this << false;
*this << value.toBool();
} else {
*this << true;
*this << value;
}
} else if (reference.isNumber()) {
if (value.isNumber()) {
*this << false;
*this << value.toNumber();
} else {
*this << true;
*this << value;
}
} else if (reference.isString()) {
if (value.isString()) {
*this << false;
*this << value.toString();
} else {
*this << true;
*this << value;
}
} else if (reference.isVariant()) {
if (value.isVariant()) {
*this << false;
writeRawDelta(value.toVariant(), reference.toVariant());
} else {
*this << true;
*this << value;
}
} else if (reference.isQObject()) {
if (value.isQObject()) {
*this << false;
writeRawDelta(value.toQObject(), reference.toQObject());
} else {
*this << true;
*this << value;
}
} else if (reference.isQMetaObject()) {
if (value.isQMetaObject()) {
*this << false;
*this << value.toQMetaObject();
} else {
*this << true;
*this << value;
}
} else if (reference.isDate()) {
if (value.isDate()) {
*this << false;
*this << value.toDateTime();
} else {
*this << true;
*this << value;
}
} else if (reference.isRegExp()) {
if (value.isRegExp()) {
*this << false;
*this << value.toRegExp();
} else {
*this << true;
*this << value;
}
} else if (reference.isArray()) {
if (value.isArray()) {
*this << false;
int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32();
*this << length;
int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32();
for (int i = 0; i < length; i++) {
if (i < referenceLength) {
writeDelta(value.property(i), reference.property(i));
} else {
*this << value.property(i);
}
}
} else {
*this << true;
*this << value;
}
} else if (reference.isObject()) {
if (value.isObject() && !(value.isArray() || value.isRegExp() || value.isDate() ||
value.isQMetaObject() || value.isQObject() || value.isVariant())) {
*this << false;
for (QScriptValueIterator it(value); it.hasNext(); ) {
it.next();
QScriptValue referenceValue = reference.property(it.scriptName());
if (it.value() != referenceValue) {
*this << it.scriptName();
writeRawDelta(it.value(), referenceValue);
}
}
for (QScriptValueIterator it(reference); it.hasNext(); ) {
it.next();
if (!value.property(it.scriptName()).isValid()) {
*this << it.scriptName();
writeRawDelta(QScriptValue(), it.value());
}
}
*this << QScriptString();
} else {
*this << true;
*this << value;
}
} else {
*this << value;
}
}
void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) {
if (reference.isUndefined() || reference.isNull()) {
*this >> value;
} else if (reference.isBool()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
bool boolValue;
*this >> boolValue;
value = QScriptValue(boolValue);
}
} else if (reference.isNumber()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
qsreal numberValue;
*this >> numberValue;
value = QScriptValue(numberValue);
}
} else if (reference.isString()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
QString stringValue;
*this >> stringValue;
value = QScriptValue(stringValue);
}
} else if (reference.isVariant()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
QVariant variant;
readRawDelta(variant, reference.toVariant());
value = ScriptCache::getInstance()->getEngine()->newVariant(variant);
}
} else if (reference.isQObject()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
QObject* object;
readRawDelta(object, reference.toQObject());
value = ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership);
}
} else if (reference.isQMetaObject()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
const QMetaObject* metaObject;
*this >> metaObject;
value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject);
}
} else if (reference.isDate()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
QDateTime dateTime;
*this >> dateTime;
value = ScriptCache::getInstance()->getEngine()->newDate(dateTime);
}
} else if (reference.isRegExp()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
QRegExp regExp;
*this >> regExp;
value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp);
}
} else if (reference.isArray()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
int length;
*this >> length;
value = ScriptCache::getInstance()->getEngine()->newArray(length);
int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32();
for (int i = 0; i < length; i++) {
QScriptValue element;
if (i < referenceLength) {
readDelta(element, reference.property(i));
} else {
*this >> element;
}
value.setProperty(i, element);
}
}
} else if (reference.isObject()) {
bool typeChanged;
*this >> typeChanged;
if (typeChanged) {
*this >> value;
} else {
// start by shallow-copying the reference
value = ScriptCache::getInstance()->getEngine()->newObject();
for (QScriptValueIterator it(reference); it.hasNext(); ) {
it.next();
value.setProperty(it.scriptName(), it.value());
}
// then apply the requested changes
forever {
QScriptString name;
*this >> name;
if (!name.isValid()) {
break;
}
QScriptValue scriptValue;
readRawDelta(scriptValue, reference.property(name));
value.setProperty(name, scriptValue);
}
}
} else {
*this >> value;
}
}
Bitstream& Bitstream::operator<<(bool value) {
if (value) {
_byte |= (1 << _position);
@ -350,6 +624,14 @@ Bitstream& Bitstream::operator>>(uint& value) {
return *this;
}
Bitstream& Bitstream::operator<<(qint64 value) {
return write(&value, 64);
}
Bitstream& Bitstream::operator>>(qint64& value) {
return read(&value, 64);
}
Bitstream& Bitstream::operator<<(float value) {
return write(&value, 32);
}
@ -358,6 +640,14 @@ Bitstream& Bitstream::operator>>(float& value) {
return read(&value, 32);
}
Bitstream& Bitstream::operator<<(double value) {
return write(&value, 64);
}
Bitstream& Bitstream::operator>>(double& value) {
return read(&value, 64);
}
Bitstream& Bitstream::operator<<(const glm::vec3& value) {
return *this << value.x << value.y << value.z;
}
@ -420,6 +710,40 @@ Bitstream& Bitstream::operator>>(QUrl& url) {
return *this;
}
Bitstream& Bitstream::operator<<(const QDateTime& dateTime) {
return *this << dateTime.toMSecsSinceEpoch();
}
Bitstream& Bitstream::operator>>(QDateTime& dateTime) {
qint64 msecsSinceEpoch;
*this >> msecsSinceEpoch;
dateTime = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch);
return *this;
}
Bitstream& Bitstream::operator<<(const QRegExp& regExp) {
*this << regExp.pattern();
Qt::CaseSensitivity caseSensitivity = regExp.caseSensitivity();
write(&caseSensitivity, 1);
QRegExp::PatternSyntax syntax = regExp.patternSyntax();
write(&syntax, 3);
return *this << regExp.isMinimal();
}
Bitstream& Bitstream::operator>>(QRegExp& regExp) {
QString pattern;
*this >> pattern;
Qt::CaseSensitivity caseSensitivity = (Qt::CaseSensitivity)0;
read(&caseSensitivity, 1);
QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)0;
read(&syntax, 3);
regExp = QRegExp(pattern, caseSensitivity, syntax);
bool minimal;
*this >> minimal;
regExp.setMinimal(minimal);
return *this;
}
Bitstream& Bitstream::operator<<(const QVariant& value) {
if (!value.isValid()) {
_typeStreamerStreamer << NULL;
@ -543,6 +867,185 @@ Bitstream& Bitstream::operator>>(QScriptString& string) {
return *this;
}
enum ScriptValueType {
INVALID_SCRIPT_VALUE,
UNDEFINED_SCRIPT_VALUE,
NULL_SCRIPT_VALUE,
BOOL_SCRIPT_VALUE,
NUMBER_SCRIPT_VALUE,
STRING_SCRIPT_VALUE,
VARIANT_SCRIPT_VALUE,
QOBJECT_SCRIPT_VALUE,
QMETAOBJECT_SCRIPT_VALUE,
DATE_SCRIPT_VALUE,
REGEXP_SCRIPT_VALUE,
ARRAY_SCRIPT_VALUE,
OBJECT_SCRIPT_VALUE
};
const int SCRIPT_VALUE_BITS = 4;
static void writeScriptValueType(Bitstream& out, ScriptValueType type) {
out.write(&type, SCRIPT_VALUE_BITS);
}
static ScriptValueType readScriptValueType(Bitstream& in) {
ScriptValueType type = (ScriptValueType)0;
in.read(&type, SCRIPT_VALUE_BITS);
return type;
}
Bitstream& Bitstream::operator<<(const QScriptValue& value) {
if (value.isUndefined()) {
writeScriptValueType(*this, UNDEFINED_SCRIPT_VALUE);
} else if (value.isNull()) {
writeScriptValueType(*this, NULL_SCRIPT_VALUE);
} else if (value.isBool()) {
writeScriptValueType(*this, BOOL_SCRIPT_VALUE);
*this << value.toBool();
} else if (value.isNumber()) {
writeScriptValueType(*this, NUMBER_SCRIPT_VALUE);
*this << value.toNumber();
} else if (value.isString()) {
writeScriptValueType(*this, STRING_SCRIPT_VALUE);
*this << value.toString();
} else if (value.isVariant()) {
writeScriptValueType(*this, VARIANT_SCRIPT_VALUE);
*this << value.toVariant();
} else if (value.isQObject()) {
writeScriptValueType(*this, QOBJECT_SCRIPT_VALUE);
*this << value.toQObject();
} else if (value.isQMetaObject()) {
writeScriptValueType(*this, QMETAOBJECT_SCRIPT_VALUE);
*this << value.toQMetaObject();
} else if (value.isDate()) {
writeScriptValueType(*this, DATE_SCRIPT_VALUE);
*this << value.toDateTime();
} else if (value.isRegExp()) {
writeScriptValueType(*this, REGEXP_SCRIPT_VALUE);
*this << value.toRegExp();
} else if (value.isArray()) {
writeScriptValueType(*this, ARRAY_SCRIPT_VALUE);
int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32();
*this << length;
for (int i = 0; i < length; i++) {
*this << value.property(i);
}
} else if (value.isObject()) {
writeScriptValueType(*this, OBJECT_SCRIPT_VALUE);
for (QScriptValueIterator it(value); it.hasNext(); ) {
it.next();
*this << it.scriptName();
*this << it.value();
}
*this << QScriptString();
} else {
writeScriptValueType(*this, INVALID_SCRIPT_VALUE);
}
return *this;
}
Bitstream& Bitstream::operator>>(QScriptValue& value) {
switch (readScriptValueType(*this)) {
case UNDEFINED_SCRIPT_VALUE:
value = QScriptValue(QScriptValue::UndefinedValue);
break;
case NULL_SCRIPT_VALUE:
value = QScriptValue(QScriptValue::NullValue);
break;
case BOOL_SCRIPT_VALUE: {
bool boolValue;
*this >> boolValue;
value = QScriptValue(boolValue);
break;
}
case NUMBER_SCRIPT_VALUE: {
qsreal numberValue;
*this >> numberValue;
value = QScriptValue(numberValue);
break;
}
case STRING_SCRIPT_VALUE: {
QString stringValue;
*this >> stringValue;
value = QScriptValue(stringValue);
break;
}
case VARIANT_SCRIPT_VALUE: {
QVariant variantValue;
*this >> variantValue;
value = ScriptCache::getInstance()->getEngine()->newVariant(variantValue);
break;
}
case QOBJECT_SCRIPT_VALUE: {
QObject* object;
*this >> object;
ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership);
break;
}
case QMETAOBJECT_SCRIPT_VALUE: {
const QMetaObject* metaObject;
*this >> metaObject;
ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject);
break;
}
case DATE_SCRIPT_VALUE: {
QDateTime dateTime;
*this >> dateTime;
value = ScriptCache::getInstance()->getEngine()->newDate(dateTime);
break;
}
case REGEXP_SCRIPT_VALUE: {
QRegExp regExp;
*this >> regExp;
value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp);
break;
}
case ARRAY_SCRIPT_VALUE: {
int length;
*this >> length;
value = ScriptCache::getInstance()->getEngine()->newArray(length);
for (int i = 0; i < length; i++) {
QScriptValue element;
*this >> element;
value.setProperty(i, element);
}
break;
}
case OBJECT_SCRIPT_VALUE: {
value = ScriptCache::getInstance()->getEngine()->newObject();
forever {
QScriptString name;
*this >> name;
if (!name.isValid()) {
break;
}
QScriptValue scriptValue;
*this >> scriptValue;
value.setProperty(name, scriptValue);
}
break;
}
default:
value = QScriptValue();
break;
}
return *this;
}
Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) {
_sharedObjectStreamer << object;
return *this;
@ -561,7 +1064,7 @@ Bitstream& Bitstream::operator<(const QMetaObject* metaObject) {
if (_metadataType == NO_METADATA) {
return *this;
}
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters().value(metaObject);
const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject);
*this << propertyWriters.size();
QCryptographicHash hash(QCryptographicHash::Md5);
foreach (const PropertyWriter& propertyWriter, propertyWriters) {
@ -600,7 +1103,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
}
int storedPropertyCount;
*this >> storedPropertyCount;
QVector<PropertyReader> properties(storedPropertyCount);
PropertyReaderVector properties(storedPropertyCount);
for (int i = 0; i < storedPropertyCount; i++) {
TypeReader typeReader;
*this >> typeReader;
@ -619,7 +1122,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
QCryptographicHash hash(QCryptographicHash::Md5);
bool matches = true;
if (metaObject) {
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters().value(metaObject);
const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject);
if (propertyWriters.size() == properties.size()) {
for (int i = 0; i < propertyWriters.size(); i++) {
const PropertyWriter& propertyWriter = propertyWriters.at(i);
@ -899,14 +1402,17 @@ Bitstream& Bitstream::operator>(AttributePointer& attribute) {
return *this;
}
const QString INVALID_STRING("%INVALID%");
Bitstream& Bitstream::operator<(const QScriptString& string) {
return *this << string.toString();
return *this << (string.isValid() ? string.toString() : INVALID_STRING);
}
Bitstream& Bitstream::operator>(QScriptString& string) {
QString rawString;
*this >> rawString;
string = ScriptCache::getInstance()->getEngine()->toStringHandle(rawString);
string = (rawString == INVALID_STRING) ? QScriptString() :
ScriptCache::getInstance()->getEngine()->toStringHandle(rawString);
return *this;
}
@ -990,17 +1496,17 @@ QHash<int, const TypeStreamer*>& Bitstream::getTypeStreamers() {
return typeStreamers;
}
const QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& Bitstream::getEnumStreamers() {
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers = createEnumStreamers();
const QHash<ScopeNamePair, const TypeStreamer*>& Bitstream::getEnumStreamers() {
static QHash<ScopeNamePair, const TypeStreamer*> enumStreamers = createEnumStreamers();
return enumStreamers;
}
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> Bitstream::createEnumStreamers() {
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers;
QHash<ScopeNamePair, const TypeStreamer*> Bitstream::createEnumStreamers() {
QHash<ScopeNamePair, const TypeStreamer*> enumStreamers;
foreach (const QMetaObject* metaObject, getMetaObjects()) {
for (int i = 0; i < metaObject->enumeratorCount(); i++) {
QMetaEnum metaEnum = metaObject->enumerator(i);
const TypeStreamer*& streamer = enumStreamers[QPair<QByteArray, QByteArray>(metaEnum.scope(), metaEnum.name())];
const TypeStreamer*& streamer = enumStreamers[ScopeNamePair(metaEnum.scope(), metaEnum.name())];
if (!streamer) {
streamer = new EnumTypeStreamer(metaEnum);
}
@ -1022,15 +1528,15 @@ QHash<QByteArray, const TypeStreamer*> Bitstream::createEnumStreamersByName() {
return enumStreamersByName;
}
const QHash<const QMetaObject*, QVector<PropertyReader> >& Bitstream::getPropertyReaders() {
static QHash<const QMetaObject*, QVector<PropertyReader> > propertyReaders = createPropertyReaders();
const QHash<const QMetaObject*, PropertyReaderVector>& Bitstream::getPropertyReaders() {
static QHash<const QMetaObject*, PropertyReaderVector> propertyReaders = createPropertyReaders();
return propertyReaders;
}
QHash<const QMetaObject*, QVector<PropertyReader> > Bitstream::createPropertyReaders() {
QHash<const QMetaObject*, QVector<PropertyReader> > propertyReaders;
QHash<const QMetaObject*, PropertyReaderVector> Bitstream::createPropertyReaders() {
QHash<const QMetaObject*, PropertyReaderVector> propertyReaders;
foreach (const QMetaObject* metaObject, getMetaObjects()) {
QVector<PropertyReader>& readers = propertyReaders[metaObject];
PropertyReaderVector& readers = propertyReaders[metaObject];
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (!property.isStored()) {
@ -1039,7 +1545,7 @@ QHash<const QMetaObject*, QVector<PropertyReader> > Bitstream::createPropertyRea
const TypeStreamer* streamer;
if (property.isEnumType()) {
QMetaEnum metaEnum = property.enumerator();
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
streamer = getEnumStreamers().value(ScopeNamePair(
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
} else {
@ -1053,15 +1559,15 @@ QHash<const QMetaObject*, QVector<PropertyReader> > Bitstream::createPropertyRea
return propertyReaders;
}
const QHash<const QMetaObject*, QVector<PropertyWriter> >& Bitstream::getPropertyWriters() {
static QHash<const QMetaObject*, QVector<PropertyWriter> > propertyWriters = createPropertyWriters();
const QHash<const QMetaObject*, PropertyWriterVector>& Bitstream::getPropertyWriters() {
static QHash<const QMetaObject*, PropertyWriterVector> propertyWriters = createPropertyWriters();
return propertyWriters;
}
QHash<const QMetaObject*, QVector<PropertyWriter> > Bitstream::createPropertyWriters() {
QHash<const QMetaObject*, QVector<PropertyWriter> > propertyWriters;
QHash<const QMetaObject*, PropertyWriterVector> Bitstream::createPropertyWriters() {
QHash<const QMetaObject*, PropertyWriterVector> propertyWriters;
foreach (const QMetaObject* metaObject, getMetaObjects()) {
QVector<PropertyWriter>& writers = propertyWriters[metaObject];
PropertyWriterVector& writers = propertyWriters[metaObject];
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (!property.isStored()) {
@ -1070,7 +1576,7 @@ QHash<const QMetaObject*, QVector<PropertyWriter> > Bitstream::createPropertyWri
const TypeStreamer* streamer;
if (property.isEnumType()) {
QMetaEnum metaEnum = property.enumerator();
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
streamer = getEnumStreamers().value(ScopeNamePair(
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
} else {
@ -1324,7 +1830,7 @@ void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVarian
}
ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject,
const QVector<PropertyReader>& properties) :
const PropertyReaderVector& properties) :
_className(className),
_metaObject(metaObject),
_properties(properties) {

View file

@ -29,6 +29,7 @@
class QByteArray;
class QColor;
class QDataStream;
class QScriptValue;
class QUrl;
class Attribute;
@ -44,6 +45,10 @@ class TypeStreamer;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
typedef QPair<QByteArray, QByteArray> ScopeNamePair;
typedef QVector<PropertyReader> PropertyReaderVector;
typedef QVector<PropertyWriter> PropertyWriterVector;
/// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that
/// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows
/// us to use the minimum number of bits to encode the IDs.
@ -287,11 +292,15 @@ public:
template<class T> void writeDelta(const T& value, const T& reference);
template<class T> void readDelta(T& value, const T& reference);
void writeRawDelta(const QVariant& value, const QVariant& reference);
void readRawDelta(QVariant& value, const QVariant& reference);
void writeRawDelta(const QObject* value, const QObject* reference);
void readRawDelta(QObject*& value, const QObject* reference);
void writeRawDelta(const QScriptValue& value, const QScriptValue& reference);
void readRawDelta(QScriptValue& value, const QScriptValue& reference);
template<class T> void writeRawDelta(const T& value, const T& reference);
template<class T> void readRawDelta(T& value, const T& reference);
@ -316,9 +325,15 @@ public:
Bitstream& operator<<(uint value);
Bitstream& operator>>(uint& value);
Bitstream& operator<<(qint64 value);
Bitstream& operator>>(qint64& value);
Bitstream& operator<<(float value);
Bitstream& operator>>(float& value);
Bitstream& operator<<(double value);
Bitstream& operator>>(double& value);
Bitstream& operator<<(const glm::vec3& value);
Bitstream& operator>>(glm::vec3& value);
@ -337,6 +352,12 @@ public:
Bitstream& operator<<(const QUrl& url);
Bitstream& operator>>(QUrl& url);
Bitstream& operator<<(const QDateTime& dateTime);
Bitstream& operator>>(QDateTime& dateTime);
Bitstream& operator<<(const QRegExp& regExp);
Bitstream& operator>>(QRegExp& regExp);
Bitstream& operator<<(const QVariant& value);
Bitstream& operator>>(QVariant& value);
@ -372,6 +393,9 @@ public:
Bitstream& operator<<(const QScriptString& string);
Bitstream& operator>>(QScriptString& string);
Bitstream& operator<<(const QScriptValue& value);
Bitstream& operator>>(QScriptValue& value);
Bitstream& operator<<(const SharedObjectPointer& object);
Bitstream& operator>>(SharedObjectPointer& object);
@ -422,14 +446,18 @@ private:
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
static QHash<int, const TypeStreamer*>& getTypeStreamers();
static const QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& getEnumStreamers();
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> createEnumStreamers();
static const QHash<ScopeNamePair, const TypeStreamer*>& getEnumStreamers();
static QHash<ScopeNamePair, const TypeStreamer*> createEnumStreamers();
static const QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
static QHash<QByteArray, const TypeStreamer*> createEnumStreamersByName();
static const QHash<const QMetaObject*, QVector<PropertyReader> >& getPropertyReaders();
static QHash<const QMetaObject*, QVector<PropertyReader> > createPropertyReaders();
static const QHash<const QMetaObject*, QVector<PropertyWriter> >& getPropertyWriters();
static QHash<const QMetaObject*, QVector<PropertyWriter> > createPropertyWriters();
static const QHash<const QMetaObject*, PropertyReaderVector>& getPropertyReaders();
static QHash<const QMetaObject*, PropertyReaderVector> createPropertyReaders();
static const QHash<const QMetaObject*, PropertyWriterVector>& getPropertyWriters();
static QHash<const QMetaObject*, PropertyWriterVector> createPropertyWriters();
};
template<class T> inline void Bitstream::writeDelta(const T& value, const T& reference) {

View file

@ -13,11 +13,95 @@
#include <QNetworkReply>
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QTextStream>
#include "AttributeRegistry.h"
#include "ScriptCache.h"
static int scriptValueMetaTypeId = qRegisterMetaType<QScriptValue>();
static bool scriptValueComparators = QMetaType::registerComparators<QScriptValue>();
bool operator==(const QScriptValue& first, const QScriptValue& second) {
if (first.isUndefined()) {
return second.isUndefined();
} else if (first.isNull()) {
return second.isNull();
} else if (first.isBool()) {
return second.isBool() && first.toBool() == second.toBool();
} else if (first.isNumber()) {
return second.isNumber() && first.toNumber() == second.toNumber();
} else if (first.isString()) {
return second.isString() && first.toString() == second.toString();
} else if (first.isVariant()) {
return second.isVariant() && first.toVariant() == second.toVariant();
} else if (first.isQObject()) {
return second.isQObject() && first.toQObject() == second.toQObject();
} else if (first.isQMetaObject()) {
return second.isQMetaObject() && first.toQMetaObject() == second.toQMetaObject();
} else if (first.isDate()) {
return second.isDate() && first.toDateTime() == second.toDateTime();
} else if (first.isRegExp()) {
return second.isRegExp() && first.toRegExp() == second.toRegExp();
} else if (first.isArray()) {
if (!second.isArray()) {
return false;
}
int length = first.property(ScriptCache::getInstance()->getLengthString()).toInt32();
if (second.property(ScriptCache::getInstance()->getLengthString()).toInt32() != length) {
return false;
}
for (int i = 0; i < length; i++) {
if (first.property(i) != second.property(i)) {
return false;
}
}
return true;
} else if (first.isObject()) {
if (!second.isObject()) {
return false;
}
int propertyCount = 0;
for (QScriptValueIterator it(first); it.hasNext(); ) {
it.next();
if (second.property(it.scriptName()) != it.value()) {
return false;
}
propertyCount++;
}
// make sure the second has exactly as many properties as the first
for (QScriptValueIterator it(second); it.hasNext(); ) {
it.next();
if (--propertyCount < 0) {
return false;
}
}
return true;
} else {
return !second.isValid();
}
}
bool operator!=(const QScriptValue& first, const QScriptValue& second) {
return !(first == second);
}
bool operator<(const QScriptValue& first, const QScriptValue& second) {
return first.lessThan(second);
}
ScriptCache* ScriptCache::getInstance() {
static ScriptCache cache;
return &cache;

View file

@ -65,6 +65,12 @@ private:
QScriptString _generatorString;
};
Q_DECLARE_METATYPE(QScriptValue)
bool operator==(const QScriptValue& first, const QScriptValue& second);
bool operator!=(const QScriptValue& first, const QScriptValue& second);
bool operator<(const QScriptValue& first, const QScriptValue& second);
/// A program loaded from the network.
class NetworkProgram : public Resource {
Q_OBJECT

View file

@ -11,6 +11,8 @@
#include <stdlib.h>
#include <QScriptValueIterator>
#include <SharedUtil.h>
#include <MetavoxelMessages.h>
@ -41,6 +43,8 @@ static int streamedBytesReceived = 0;
static int sharedObjectsCreated = 0;
static int sharedObjectsDestroyed = 0;
static int objectMutationsPerformed = 0;
static int scriptObjectsCreated = 0;
static int scriptMutationsPerformed = 0;
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
@ -79,6 +83,48 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() {
return flags;
}
static QScriptValue createRandomScriptValue(bool complex = false) {
scriptObjectsCreated++;
switch (randIntInRange(0, complex ? 5 : 3)) {
case 0:
return QScriptValue(QScriptValue::NullValue);
case 1:
return QScriptValue(randomBoolean());
case 2:
return QScriptValue(randFloat());
case 3:
return QScriptValue(QString(createRandomBytes()));
case 4: {
int length = randIntInRange(2, 6);
QScriptValue value = ScriptCache::getInstance()->getEngine()->newArray(length);
for (int i = 0; i < length; i++) {
value.setProperty(i, createRandomScriptValue());
}
return value;
}
default: {
QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject();
if (randomBoolean()) {
value.setProperty("foo", createRandomScriptValue());
}
if (randomBoolean()) {
value.setProperty("bar", createRandomScriptValue());
}
if (randomBoolean()) {
value.setProperty("baz", createRandomScriptValue());
}
if (randomBoolean()) {
value.setProperty("bong", createRandomScriptValue());
}
return value;
}
}
}
static TestMessageC createRandomMessageC() {
TestMessageC message;
message.foo = randomBoolean();
@ -86,6 +132,7 @@ static TestMessageC createRandomMessageC() {
message.baz = randFloat();
message.bong.foo = createRandomBytes();
message.bong.baz = getRandomTestEnum();
message.bizzle = createRandomScriptValue(true);
return message;
}
@ -201,6 +248,7 @@ bool MetavoxelTests::run() {
datagramsReceived << "with" << bytesReceived << "bytes";
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed;
qDebug();
qDebug() << "Running serialization tests...";
@ -284,7 +332,7 @@ static QVariant createRandomMessage() {
}
static SharedObjectPointer mutate(const SharedObjectPointer& state) {
switch(randIntInRange(0, 3)) {
switch (randIntInRange(0, 4)) {
case 0: {
SharedObjectPointer newState = state->clone(true);
static_cast<TestSharedObjectA*>(newState.data())->setFoo(randFloat());
@ -303,6 +351,38 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) {
objectMutationsPerformed++;
return newState;
}
case 3: {
SharedObjectPointer newState = state->clone(true);
QScriptValue oldValue = static_cast<TestSharedObjectA*>(newState.data())->getBizzle();
QScriptValue newValue = ScriptCache::getInstance()->getEngine()->newObject();
for (QScriptValueIterator it(oldValue); it.hasNext(); ) {
it.next();
newValue.setProperty(it.scriptName(), it.value());
}
switch (randIntInRange(0, 2)) {
case 0: {
QScriptValue oldArray = oldValue.property("foo");
int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32();
QScriptValue newArray = ScriptCache::getInstance()->getEngine()->newArray(oldLength);
for (int i = 0; i < oldLength; i++) {
newArray.setProperty(i, oldArray.property(i));
}
newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue(true));
break;
}
case 1:
newValue.setProperty("bar", QScriptValue(randFloat()));
break;
default:
newValue.setProperty("baz", createRandomScriptValue(true));
break;
}
static_cast<TestSharedObjectA*>(newState.data())->setBizzle(newValue);
scriptMutationsPerformed++;
objectMutationsPerformed++;
return newState;
}
default:
return state;
}
@ -503,7 +583,10 @@ TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) :
_foo(foo),
_baz(baz),
_bong(bong) {
sharedObjectsCreated++;
sharedObjectsCreated++;
_bizzle = ScriptCache::getInstance()->getEngine()->newObject();
_bizzle.setProperty("foo", ScriptCache::getInstance()->getEngine()->newArray(4));
}
TestSharedObjectA::~TestSharedObjectA() {

View file

@ -16,6 +16,7 @@
#include <QVariantList>
#include <DatagramSequencer.h>
#include <ScriptCache.h>
class SequencedTestMessage;
@ -96,7 +97,8 @@ class TestSharedObjectA : public SharedObject {
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)
Q_PROPERTY(QScriptValue bizzle READ getBizzle WRITE setBizzle)
public:
enum TestEnum { FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM };
@ -116,6 +118,9 @@ public:
void setBong(TestFlags bong) { _bong = bong; }
TestFlags getBong() const { return _bong; }
void setBizzle(const QScriptValue& bizzle) { _bizzle = bizzle; }
const QScriptValue& getBizzle() const { return _bizzle; }
signals:
void fooChanged(float foo);
@ -125,6 +130,7 @@ private:
float _foo;
TestEnum _baz;
TestFlags _bong;
QScriptValue _bizzle;
};
DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum)
@ -204,6 +210,7 @@ class TestMessageC : STREAM public TestMessageA {
public:
STREAM TestMessageB bong;
STREAM QScriptValue bizzle;
};
DECLARE_STREAMABLE_METATYPE(TestMessageC)