Commit 2eb3e181 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère
Browse files

Improved QCborWriter using QByteArray instead of QIODevice (std::string

would be faster but not so Qt-ish) after comparing with protobuf (which
is only beaten by std::string appends)
parent 30101d39
......@@ -8,7 +8,7 @@ QT += widgets
TARGET = QBind
CONFIG += console
CONFIG += c++11 console
CONFIG -= app_bundle
TEMPLATE = app
......@@ -30,6 +30,20 @@ DEFINES += QT_DEPRECATED_WARNINGS
gcc:QMAKE_CXXFLAGS += -ftemplate-backtrace-limit=0
#PROTO_FILE = persons.proto
#protoc.output = $${OUT_PWD}/${QMAKE_FILE_IN_BASE}.pb.cc
#protoc.variable_out = GENERATED_SOURCES
#protoc.input = PROTO_FILE
#win32 {
# PROTO_PATH = /path/to/protobuf-3.7.1-msvc2019
# DEFINES += PROTOBUF_USE_DLLS
# protoc.commands = $${PROTO_PATH}/bin/protoc -I=$$relative_path($${PWD}, $${OUT_PWD}) --cpp_out=. ${QMAKE_FILE_NAME}
# INCLUDEPATH += $${PROTO_PATH}/include
# DEPENDPATH += $${PROTO_PATH}/include
# LIBS += -L$${PROTO_PATH}/lib -llibprotobuf
#}
#QMAKE_EXTRA_COMPILERS += protoc
SOURCES += \
main.cpp
......@@ -40,3 +54,6 @@ HEADERS += \
QVariant_impl.h \
QModel_impl.h \
QData_impl.h
DISTFILES += \
persons.proto
......@@ -693,8 +693,9 @@ struct IReader : public IBind
double d; // matches most used numbers more easily than types below
qint64 i; // matches most used integrals more easily than types below
quint64 u;
QVariant v;
QByteArray bytes;
if (_bind(b) || _bind(d) || _bind(i) || _bind(u) || _bind(bytes)) {
if (_bind(b) || _bind(d) || _bind(i) || _bind(u) || _bind(v) || _bind(bytes)) {
return true;
}
_setChoice(false);
......
......@@ -105,7 +105,7 @@ class QCborWriter : public IWriter // TODO Support CBOR tags for IBind BindNativ
Q_DISABLE_COPY(QCborWriter)
public:
~QCborWriter() { while (!levels.empty()) _out(); }
QCborWriter(QIODevice* io) : io(io) { Q_ASSERT(io); }
QCborWriter(QByteArray* io) : io(io) { Q_ASSERT(io); }
// Shortcuts
/**/ Val<Cursor> value ( ) { return Cursor(this).value(); }
......@@ -117,61 +117,58 @@ protected:
bool _isOk() noexcept { return io; }
bool _sequence(quint32* rows=nullptr) { levels.push_back(rows);
return Q_LIKELY (rows)
? putInteger (*rows , cbor::ArrayType )
: putMajorValue (cbor::IndefiniteLength, cbor::ArrayType ); }
if (Q_LIKELY(rows)) { putInteger (*rows , cbor::ArrayType); }
else { putMajorValue(cbor::IndefiniteLength, cbor::ArrayType); } return true; }
bool _record (quint32* cols=nullptr) { levels.push_back(cols);
return Q_LIKELY (cols)
? putInteger (*cols , cbor::MapType )
: putMajorValue (cbor::IndefiniteLength, cbor::MapType ); }
bool _null ( ) { return putSimpleValue(cbor::Null ); }
bool _bind ( const char* s) { return putInteger (strlen(s) ,cbor::TextStringType) && (Q_LIKELY(io->write(s)) || *s=='\0'); }
if (Q_LIKELY(cols)) { putInteger (*cols , cbor::MapType ); }
else { putMajorValue(cbor::IndefiniteLength, cbor::MapType ); } return true; }
bool _null ( ) { putSimpleValue(cbor::Null ); return true; }
bool _bind ( const char* s) { auto size = strlen(s); putInteger(size, cbor::TextStringType); io->append(s, size); return true; }
bool _bind ( QString&& s) { QUtf8String u(s.toUtf8());
return putInteger (quint64(u.size()) ,cbor::TextStringType) && (Q_LIKELY(io->write(u)) || u.isEmpty()); }
putInteger (quint64(u.size()), cbor::TextStringType); io->append(u); return true; }
//static const char mimeHeader[] = "Content-Type:text/plain;charset=utf-16\xD\xA\xD\xA";
//QByteArray utf16(QByteArray::fromRawData(reinterpret_cast<const char*>(s.utf16()), s.size()*int(sizeof(QChar))));
//return putMajorValue (cbor::NextByteUnsignedInt,cbor::TagType ) && io->putChar(cbor::MimeMessage) &&
// putInteger (sizeof(mimeHeader)+
// utf16.size() ,cbor::ByteStringType) && io->write(mimeHeader, sizeof(mimeHeader))
// && (Q_LIKELY(io->write(utf16)) || s[0]==QChar('\0')); }
bool _bind ( QByteArray&& s) { return putInteger (quint64(s.size()) ,cbor::ByteStringType) && (Q_LIKELY(io->write(s)) || *s=='\0'); }
bool _bind ( bool&& b) { return putSimpleValue(b ? cbor::True : cbor::False); }
bool _bind ( float&& n) { return Q_LIKELY(putSimpleValue(cbor::Next32BitFloat)) && putBigEndian(n); }
bool _bind ( double&& n) { return Q_LIKELY(putSimpleValue(cbor::Next64BitFloat)) && putBigEndian(n); }
bool _bind ( quint64&& t) { return putInteger(quint64( t), cbor::UnsignedIntType); }
bool _bind ( qint64&& t) { return t<0 ? putInteger(quint64(-1-t), cbor::NegativeIntType) // see https://tools.ietf.org/html/rfc7049#section-2.1
: putInteger(quint64( t), cbor::UnsignedIntType); }
bool _item(QName n) { return (Q_LIKELY(putInteger(qstrlen(n), cbor::TextStringType)) && (Q_LIKELY(io->write(n)) || *n=='\0')); }
bool _item( ) { return true ; }
bool _out ( ) { bool definite = levels.back(); levels.pop_back(); return definite ? true : putSimpleValue(cbor::Break); }
bool _bind ( QByteArray&& s) { putInteger (quint64(s.size()), cbor::ByteStringType); io->append(s); return true; }
bool _bind ( bool&& b) { putSimpleValue(b ? cbor::True : cbor::False); return true; }
bool _bind ( float&& n) { putSimpleValue(cbor::Next32BitFloat); putBigEndian(n); return true; }
bool _bind ( double&& n) { putSimpleValue(cbor::Next64BitFloat); putBigEndian(n); return true; }
bool _bind ( quint64&& t) { putInteger(quint64( t), cbor::UnsignedIntType); return true; }
bool _bind ( qint64&& t) { if (t<0) { putInteger(quint64(-1-t), cbor::NegativeIntType); } // see https://tools.ietf.org/html/rfc7049#section-2.1
else { putInteger(quint64( t), cbor::UnsignedIntType); } return true; }
bool _item(QName n) { putInteger(qstrlen(n), cbor::TextStringType); io->append(n); return true; }
bool _item( ) { return true; }
bool _out ( ) { bool definite = levels.back(); levels.pop_back(); if (!definite) putSimpleValue(cbor::Break); return true; }
private:
inline bool putMajorValue (cbor:: MajorValue v, cbor::MajorType majorType) { return io->putChar (v|char ( majorType << 5)); }
inline bool putSimpleValue(cbor::SimpleValue v ) { return io->putChar (v|char (cbor::SimpleValueType << 5)); }
inline bool putInteger ( quint64 n, cbor::MajorType majorType)
inline void putMajorValue (cbor:: MajorValue v, cbor::MajorType majorType) { io->append(v|char ( majorType << 5)); }
inline void putSimpleValue(cbor::SimpleValue v ) { io->append(v|char (cbor::SimpleValueType << 5)); }
inline void putInteger ( quint64 n, cbor::MajorType majorType)
{
// See https://tools.ietf.org/html/rfc7049#section-2.2 if more performance is needed
if (n < 24 ) { return putMajorValue(cbor::MajorValue(n) , majorType) ; } else
if (n <= 0xff ) { return Q_LIKELY(putMajorValue(cbor::NextByteUnsignedInt , majorType)) && putBigEndian(quint8 (n)); } else
if (n <= 0xffff ) { return Q_LIKELY(putMajorValue(cbor::Next16BitUnsignedInt, majorType)) && putBigEndian(quint16(n)); } else
if (n <= 0xffffffffL) { return Q_LIKELY(putMajorValue(cbor::Next32BitUnsignedInt, majorType)) && putBigEndian(quint32(n)); } else
{ return Q_LIKELY(putMajorValue(cbor::Next64BitUnsignedInt, majorType)) && putBigEndian(quint64(n)); }
if (n < 24 ) { putMajorValue(cbor::MajorValue(n) , majorType) ; } else
if (n <= 0xff ) { putMajorValue(cbor::NextByteUnsignedInt , majorType); putBigEndian(quint8 (n)); } else
if (n <= 0xffff ) { putMajorValue(cbor::Next16BitUnsignedInt, majorType); putBigEndian(quint16(n)); } else
if (n <= 0xffffffffL) { putMajorValue(cbor::Next32BitUnsignedInt, majorType); putBigEndian(quint32(n)); } else
{ putMajorValue(cbor::Next64BitUnsignedInt, majorType); putBigEndian(quint64(n)); }
}
template<typename T> inline bool putBigEndian(T&& t) {
template<typename T> inline void putBigEndian(T&& t) {
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
return io->write(reinterpret_cast<char *>(&t), sizeof(t));
io->append(reinterpret_cast<char *>(&t), sizeof(t));
#else
using TValue = typename std::remove_reference<T>::type;
if (sizeof(TValue)==1) { return io->putChar( char(t )); }
if (sizeof(TValue)==2) { union { TValue t; quint16 bytes; } u = { t }; u.bytes = qbswap(u.bytes); return io->write(reinterpret_cast<char *>(&u),sizeof(TValue)); }
if (sizeof(TValue)==4) { union { TValue t; quint32 bytes; } u = { t }; u.bytes = qbswap(u.bytes); return io->write(reinterpret_cast<char *>(&u),sizeof(TValue)); }
if (sizeof(TValue)==8) { union { TValue t; quint64 bytes; } u = { t }; u.bytes = qbswap(u.bytes); return io->write(reinterpret_cast<char *>(&u),sizeof(TValue)); }
return false;
if (sizeof(TValue)==1) { io->append( char(t )); } else
if (sizeof(TValue)==2) { union { TValue t; quint16 bytes; } u = { t }; u.bytes = qbswap(u.bytes); io->append(reinterpret_cast<char *>(&u),sizeof(TValue)); } else
if (sizeof(TValue)==4) { union { TValue t; quint32 bytes; } u = { t }; u.bytes = qbswap(u.bytes); io->append(reinterpret_cast<char *>(&u),sizeof(TValue)); } else
if (sizeof(TValue)==8) { union { TValue t; quint64 bytes; } u = { t }; u.bytes = qbswap(u.bytes); io->append(reinterpret_cast<char *>(&u),sizeof(TValue)); }
#endif
}
QIODevice* io;
QByteArray* io;
std::vector<bool> levels; //!< minimal dynamic context to ensure well-formedness in case Cursor is abandoned: true indicates Definite levels with prefixed size
};
......
......@@ -52,6 +52,10 @@
#include "QVariant_impl.h" // QVariantBuilder, QVariantVisitor and QBind<QVariant,_> support
#include "QModel_impl.h" // Q*ModelWriter support
#ifdef PROTOBUF
#include "persons.pb.h"
#endif
#include <QtCore/qvariant.h>
#include <QtGui/qstandarditemmodel.h>
......@@ -244,7 +248,8 @@ int main(int argc, char *argv[])
// Temporary buffers
QString s;
QBuffer b; b.open(QIODevice::ReadWrite);
QByteArray ba; ba.reserve(1000);
QBuffer b(&ba); b.open(QIODevice::ReadWrite);
QVariant v;
QDataStream d(&b);
......@@ -295,8 +300,8 @@ int main(int argc, char *argv[])
}
STOP("Variant",v.toString());
START {
b.seek(0); b.buffer().clear();
QCborWriter(&b).value().sequence(5)
ba.clear();
QCborWriter(&ba).value().sequence(5)
.bind(1.333333333333f)
.bind(PI)
.bind(ascii)
......@@ -304,7 +309,7 @@ int main(int argc, char *argv[])
.bind(color)
;
}
STOP("Cbor",QString::fromUtf8(b.buffer().toHex()));
STOP("Cbor",QString::fromUtf8(ba.toHex()));
START {
b.seek(0); b.buffer().clear();
QCborStreamWriter s(&b);
......@@ -357,10 +362,10 @@ int main(int argc, char *argv[])
}
STOP("Writables","");
START {
b.seek(0); b.buffer().clear();
QCborWriter(&b).bind(writables);
ba.clear();
QCborWriter(&ba).bind(writables);
}
STOP("Writables>Cbor",QString::fromUtf8(b.buffer().toHex()));
STOP("Writables>Cbor",QString::fromUtf8(ba.toHex()));
START {
b.seek(0); b.buffer().clear();
QJsonWriter(&b).bind(writables);
......@@ -395,9 +400,9 @@ int main(int argc, char *argv[])
STOP("Variant",v.toString());
START {
b.seek(0); b.buffer().clear();
QCborWriter(&b).bind(transform);
QCborWriter(&ba).bind(transform);
}
STOP("Cbor",QString::fromUtf8(b.buffer().toHex()));
STOP("Cbor",QString::fromUtf8(ba.toHex()));
START {
b.seek(0); b.buffer().clear();
......@@ -425,16 +430,26 @@ int main(int argc, char *argv[])
};
}
STOP("QDataStream",QString::fromUtf8(b.buffer().toHex()));
QByteArray str; str.reserve(500); int size=transform.size();
START {
str.clear();
str.append(reinterpret_cast<char*>(&size),sizeof(size));
for (int i=0; i < size; i++) {
str.append(reinterpret_cast<char*>(&transform[i]),sizeof(transform[i]));
};
}
STOP("QByteArray",str.toHex());
QBindable<> writable;
START {
writable = transform;
}
STOP("Writable","");
START {
b.seek(0); b.buffer().clear();
QCborWriter(&b).bind(writable);
ba.clear();
QCborWriter(&ba).bind(writable);
}
STOP("Writable>Cbor",QString::fromUtf8(b.buffer().toHex()));
STOP("Writable>Cbor",QString::fromUtf8(ba.toHex()));
START {
b.seek(0); b.buffer().clear();
QJsonWriter(&b).bind(writable);
......@@ -473,10 +488,10 @@ int main(int argc, char *argv[])
}
STOP("Variant",v.toString());
START {
b.seek(0); b.buffer().clear();
QCborWriter(&b).bind(person);
ba.clear();
QCborWriter(&ba).bind(person);
}
STOP("Cbor",QString::fromUtf8(b.buffer().toHex()));
STOP("Cbor",QString::fromUtf8(ba.toHex()));
START {
b.seek(0); b.buffer().clear();
......@@ -514,16 +529,45 @@ int main(int argc, char *argv[])
d << person;
}
STOP("QDataStream",QString::fromUtf8(b.buffer().toHex()));
QByteArray str; str.reserve(100); double dbl=1.75; int i=18;
START {
str.clear();
str.append("John");
str.append("Doe");
str.append(reinterpret_cast<char*>(&dbl),sizeof(dbl));
str.append(reinterpret_cast<char*>(&i ),sizeof(i ));
str.append("+44 1234567");
str.append("+44 2345678");
str.append("unicode is likely U+01 \x01 + U+1F \x1F + U+A4 ¤ U+B0 ° U+D8 Ø U+FF ÿ");
}
STOP("QByteArray",str.toHex());
#ifdef PROTOBUF
std::string buf; // faster than QByteArray
pb::Person pb;
pb.set_firstname("John");
pb.set_lastname("Doe");
pb.set_height(1.75);
pb.set_age(18);
pb.add_phones("+44 1234567");
pb.add_phones("+44 2345678");
pb.set_comments("unicode is likely U+01 \x01 + U+1F \x1F + U+A4 ¤ U+B0 ° U+D8 Ø U+FF ÿ");
START {
buf.clear();
buf = pb.SerializeAsString();
}
STOP("protobuf",QByteArray::fromStdString(buf).toHex());
#endif
QBindable<> writable;
START {
writable = person;
}
STOP("Writable","");
START {
b.seek(0); b.buffer().clear();
QCborWriter(&b).bind(writable);
ba.clear();
QCborWriter(&ba).bind(writable);
}
STOP("Writable>Cbor",QString::fromUtf8(b.buffer().toHex()));
STOP("Writable>Cbor",QString::fromUtf8(ba.toHex()));
START {
b.seek(0); b.buffer().clear();
QJsonWriter(&b).bind(writable);
......@@ -577,12 +621,12 @@ int main(int argc, char *argv[])
STOP("JsonValue>P",QString::fromUtf8(Text(p)+Text(visitorErrors)))
//---------------------------------------------------------------------
START {
roundtrip.seek(0); b.seek(0); b.buffer().clear();
roundtrip.seek(0); ba.clear();
QJsonReader r(&roundtrip);
r.bind(QCborWriter(&b).value());
r.bind(QCborWriter(&ba).value());
readerErrors = r.errors;
}
STOP("Json>Cbor",QString::fromUtf8(b.buffer().toHex()))
STOP("Json>Cbor",ba.toHex())
START {
roundtrip.seek(0); jv = QJsonValue();
QJsonReader r(&roundtrip);
......@@ -613,8 +657,8 @@ int main(int argc, char *argv[])
}
STOP("Cbor>P",QString(Text(p)+Text(readerErrors)))
START {
roundtrip.seek(0);
QCborWriter(&roundtrip).bind(p);
roundtrip.buffer().clear();
QCborWriter(&roundtrip.buffer()).bind(p);
}
STOP("P>Cbor",QString::fromUtf8(roundtrip.buffer().toHex()))
START {
......@@ -714,10 +758,10 @@ int main(int argc, char *argv[])
}
STOP("Cbor>CborValue",cv.toDiagnosticNotation()+QString::fromUtf8((Text(readerErrors))));
START {
b.seek(0); b.buffer().clear();
QCborWriter(&b).bind(cv);
ba.clear();
QCborWriter(&ba).bind(cv);
}
STOP("Cbor<CborValue",QString::fromUtf8(b.buffer().toHex()));
STOP("Cbor<CborValue",ba.toHex());
//---------------------------------------------------------------------
START {
roundtrip.seek(0); b.seek(0); b.buffer().clear();
......
syntax = "proto2";
package pb;
message Person {
optional string firstName = 1;
optional string lastName = 2;
optional double height = 3;
optional int64 age = 4;
repeated string phones = 5;
optional string comments = 6;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment