Commit 05aff862 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère

Replaced QUtf8String with QData<TContent> to tag QByteArray content

Increased performance by avoiding a few unnecessary utf8-16 conversions
parent 397c8329
#include "QBind_impl.h"
QIdentifier::QIdentifier(QName n) : QData(QByteArray(n.ascii(), n.size())) { /* no need to check QName content */ }
......@@ -46,12 +46,14 @@ gcc:QMAKE_CXXFLAGS += -ftemplate-backtrace-limit=0
SOURCES += \
QBind.cpp \
QData.cpp \
data.cpp \
main.cpp
HEADERS += \
QBind_impl.h \
QCbor_impl.h \
QData.h \
QJson_impl.h \
QSettings_impl.h \
QVariant_impl.h \
......
This diff is collapsed.
......@@ -122,9 +122,9 @@ protected:
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* u8) { auto size = qstrlen(u8); putInteger(size, cbor::TextStringType); io->append(u8, size); return true; }
bool _bind ( QString&& s) { QUtf8String u(s.toUtf8());
putInteger (quint64(u.size()), cbor::TextStringType); io->append(u); return true; }
bool _bind ( QUtf8DataView u) { putInteger (quint64(u.size()), cbor::TextStringType); io->append(u.data(), u.size()); return true; }
bool _bind ( QString&& s) { QUtf8Data u(s.toUtf8());
putInteger (quint64(u.size()), cbor::TextStringType); io->append(u.utf8()); 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) &&
......@@ -140,9 +140,10 @@ protected:
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(n.size(), cbor::TextStringType); io->append(n.utf8()); return true; }
bool _item( ) { return true; }
bool _out ( ) { bool definite = levels.back(); levels.pop_back(); if (!definite) putSimpleValue(cbor::Break); return true; }
bool _item(QIdentifier& n) { putInteger(quint64(n.size()), cbor::TextStringType); io->append(n.utf8() ); return true; }
bool _item(QIdentifierLiteral n) { putInteger(quint64(n.size()), cbor::TextStringType); io->append(n.utf8(), n.size()); return true; }
bool _item( ) { return true; }
bool _out ( ) { bool definite = levels.back(); levels.pop_back(); if (!definite) putSimpleValue(cbor::Break); return true; }
private:
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)); }
......@@ -190,10 +191,12 @@ public:
/**/ Seq<Cursor> sequence(quint32* s=nullptr) { return Cursor(this).value().sequence(s); }
template<typename T> Cursor bind ( T&& t) { return Cursor(this).value().bind(std::forward<T>(t)); }
protected:
bool _sequence(quint32* =nullptr) { levels.push(Step( )); return true; }
bool _record (quint32* =nullptr) { levels.push(Step(QName(""))); return true; }
bool _null ( ) { set(QCborValue( )); return true; }
bool _bind ( const char* u8) { set(QCborValue( u8 )); return true; }
bool _sequence(quint32* =nullptr) { levels.push(Step( )); return true; }
bool _record (quint32* =nullptr) { levels.push(Step(qBindExpectedItem)); return true; }
bool _null ( ) { set(QCborValue( )); return true; }
bool _bind ( QUtf8DataView u) { set(QCborValue(QString::fromUtf8(u.data()))); return true; }
bool _bind ( bool&& b) { set(QCborValue( b )); return true; }
bool _bind ( float&& d) { set(QCborValue(double(d))); return true; }
bool _bind ( double&& d) { set(QCborValue( d )); return true; }
......@@ -202,9 +205,9 @@ protected:
bool _bind ( qint64&& n) { set(QCborValue( n )); return true; }
bool _bind ( QByteArray&& s) { set(QCborValue( s )); return true; }
bool _item(QName n) { levels.last().key=n ; return true; }
bool _item( ) { levels.last().key=QName(); return true; }
bool _out ( ) { auto level = levels.pop(); set(!level.key.isNull() ? QCborValue(level.object) : QCborValue(level.array)); return true; }
bool _item(QIdentifier& n) { levels.last().key=n ; return true; }
bool _item( ) { levels.last().key=QIdentifier(); return true; }
bool _out ( ) { auto level = levels.pop(); set(!level.key.isNull() ? QCborValue(level.object) : QCborValue(level.array)); return true; }
private:
void set(const QCborValue& v) {
if (levels.isEmpty()) {
......@@ -212,14 +215,14 @@ private:
}
else {
if (!levels.last().key.isNull())
levels.last().object[QString::fromLatin1(levels.last().key.latin1())]=v;
levels.last().object[levels.last().key.latin1()]=v;
else
levels.last().array.append(v);
}
}
QCborValue* cbor;
struct Step { QName key; /* TODO union */ QCborMap object; QCborArray array; Step(QName k=QName()) : key(k) {} };
struct Step { QIdentifier key; /* TODO union */ QCborMap object; QCborArray array; Step(QIdentifier k=QIdentifier()) : key(k) {} };
QStack<Step> levels = QStack<Step>(); //!< minimal dynamic context to implement out() and ensure actual building in case QCborBuilder is abandoned
};
......@@ -232,16 +235,16 @@ public:
QCborVisitor(QCborValue* v) : cbor(v) { Q_ASSERT(v); }
void reset(QCborValue* v) { cbor=v; Q_ASSERT(v); steps.resize(0); errors.resize(0); }
struct Error { QName error; QByteArray path; template<class T> T bind(Val<T>&& value) { return value.bind(QUtf8String(error.utf8())+' '+path); } };
struct Error { QIdentifierLiteral error; QAsciiData path; template<class T> T bind(Val<T>&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } };
QVector<Error> errors;
QByteArray currentPath() {
QAsciiData currentPath() {
QByteArray path;
Q_FOREACH(Step s, steps) {
if (!s.key.isNull()) { path.append('{').append( s.key.utf8()); }
else { path.append('[').append(QByteArray::number(s.idx )); }
if (!s.key.isNull()) { path.append('{').append( s.key.utf8() ); }
else { path.append('[').append(QByteArray::number(s.idx )); }
}
return path;
return QAsciiData(path);
}
// Shortcuts
......@@ -252,7 +255,7 @@ protected:
bool _sequence(quint32* =nullptr) { if (current().isArray ()) { steps.push(Step()); return true; } _reportError(qBindExpectedSequence); return false; }
bool _record (quint32* =nullptr) { if (current().isMap ()) { steps.push(Step()); return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (current().isNull ()) { return true; } _reportError(qBindExpectedNull ); return false; }
bool _bind ( QUtf8String& u) { QString s; if (_bind(s) ) { u.bytes() = s.toUtf8 (); return true; } return false; }
bool _bind ( QUtf8Data& u) { QString s; if (_bind(s) ) { u . set (s.toUtf8() ); return true; } return false; }
bool _bind ( QString& v) { if (current().isString ()) { v = current().toString (); return true; } _reportError(qBindExpectedText ); return false; }
bool _bind ( bool& v) { if (current().isBool ()) { v = current().toBool (); return true; } _reportError(qBindExpectedBoolean ); return false; }
bool _bind ( qint64& t) { if (current().isInteger ()) { t = current().toInteger (); return true; } _reportError(qBindExpectedInteger ); return false; }
......@@ -261,20 +264,20 @@ protected:
bool _bind ( double& v) { if (current().isDouble ()) { v = current().toDouble (); return true; } _reportError(qBindExpectedDecimal ); return false; }
bool _bind ( QByteArray& v) { QString s; if (current().isByteArray()) { v = current().toByteArray(); return true; } _reportError(qBindExpectedBytes ); return false; }
bool _item(QName k) { steps.last().key= k ; return !(steps.last().item = current(1).toMap ().value(QString::fromLatin1(steps.last().key.latin1()))).isUndefined(); }
bool _item(QIdentifier& k) { steps.last().key=QName(k); return !(steps.last().item = current(1).toMap ().value(QString::fromLatin1(steps.last().key.latin1()))).isUndefined(); }
bool _item( ) { steps.last().idx++ ; return !(steps.last().item = current(1).toArray(). at( steps.last().idx )).isUndefined(); }
bool _out ( ) { steps.pop() ; return true; }
bool _item(QIdentifierLiteral u) { steps.last().key=u; return !(steps.last().item = current(1).toMap().value(steps.last().key.latin1())).isUndefined(); }
bool _item(QIdentifier& k) { steps.last().key=k; return !(steps.last().item = current(1).toMap().value(steps.last().key.latin1())).isUndefined(); }
bool _item( ) { steps.last().idx++; return !(steps.last().item = current(1).toArray(). at(steps.last().idx )).isUndefined(); }
bool _out ( ) { steps.pop() ; return true; }
bool _isOk() { return cbor; }
void _setChoice(bool v) { isChoice=v; }
void _reportError(QName e) { if (!isChoice)
void _reportError(QIdentifierLiteral e) { if (!isChoice)
errors.append(Error{ e, currentPath() }); }
private:
const QCborValue& current(int outer=0) const { Q_ASSERT(0<=outer); return steps.size()-outer <= 0 ? *cbor : steps[steps.size()-outer-1].item; }
QCborValue* cbor;
struct Step { QName key; int idx=-1; QCborValue item; Step() = default; };
struct Step { QIdentifier key; int idx=-1; QCborValue item; Step() = default; };
QStack<Step> steps = QStack<Step>();
bool isChoice = false;
};
......@@ -290,7 +293,7 @@ class QCborReader : public IReader, public QCborStreamReader
public:
QCborReader(QIODevice* io) : QCborStreamReader(io), cacheVisitor(&cachedValue) { Q_ASSERT(io); }
struct Error { QName error; qint64 index; QCborError cborError; template<class T> T bind(Val<T>&& value) { QByteArray utf8(error.utf8()); utf8.append(' ').append(QByteArray::number(index)); return value.bind(utf8.constData()); } };
struct Error { QIdentifierLiteral error; qint64 index; QCborError cborError; template<class T> T bind(Val<T>&& value) { QByteArray utf8(error.utf8()); utf8.append(' ').append(QByteArray::number(index)); return value.bind(utf8.constData()); } };
QVector<Error> errors;
// Shortcuts
......@@ -305,7 +308,7 @@ protected:
skipTag(); if (isMap () && enterContainer()) { levels.push(Level()); return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (caching) { cacheLevel++; return caching->_null() && cacheOut(); }
skipTag(); if (isNull () && next ()) { return true; } _reportError(qBindExpectedNull ); return false; }
bool _bind ( QUtf8String& u) { QString s; if (_bind(s)) { u.bytes() = s.toUtf8(); return true; } return false; }
bool _bind ( QUtf8Data& u) { QString s; if (_bind(s)) { u.set(s.toUtf8()); return true; } return false; }
bool _bind ( QString& s) { if (caching) { cacheLevel++; return caching->_bind(s) && cacheOut(); }
skipTag(); if (isString()) {
s.resize(0);
......@@ -365,7 +368,7 @@ protected:
else { if (quint64( std::numeric_limits<qint64>::max())<i) { _reportError(qBindExpectedSmallerNumber); return false; } t= qint64(i); return true; }
}
bool _item( QName u) { if (caching) { return caching->_item(u); }
bool _item( QIdentifierLiteral u) { if (caching) { return caching->_item(u); }
QIdentifier s;
while (true) {
if (levels.last().cachedItems. contains(QIdentifier(u))) { // must be checked before we consume _item(s)
......@@ -391,7 +394,7 @@ protected:
else if (!_item(s)) { // record() end reached
return false;
}
else if (u == s.constData()) {
else if (u == s ) {
return true ;
}
else {
......@@ -410,7 +413,7 @@ protected:
return false;
}
QString s;
if (!_bind(s)) {
if (!_bind(s) || s.isEmpty()) {
return false;
}
k = QIdentifier(s.toLatin1());
......@@ -430,7 +433,7 @@ protected:
bool _any() { return next(); }
bool _isOk() { return lastError()==QCborError::NoError; }
void _setChoice(bool b) { isChoice = b; }
void _reportError(QName e) { if (!isChoice) errors.append(Error{ e, currentOffset(), lastError() }); }
void _reportError(QIdentifierLiteral e) { if (!isChoice) errors.append(Error{ e, currentOffset(), lastError() }); }
private:
void skipTag() { if (isTag()) next(); }
bool getNumber(double& d) { skipTag();
......@@ -458,7 +461,7 @@ private:
// Read/Write caching of out-of-order item keys
struct Level { QMap<QIdentifier,QCborValue> cachedItems; };
QStack<Level> levels = QStack<Level>(); //!< dynamic context required to cache out-of-order item(QName)
QStack<Level> levels = QStack<Level>(); //!< dynamic context required to cache out-of-order item(QIdentifierLiteral)
quint8 cacheLevel = 0;
QCborValue cachedValue ; //!< Only used AFTER --cacheLevel==1
qint64 cachingAfter= 0; //!< Only used AFTER --cacheLevel==1
......@@ -550,11 +553,15 @@ struct QBind<QCborMap, TResult> {
static TResult bind(Val<TResult>&& v, QCborMap&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
auto s(v.record(&size));
auto r(v.record(&size));
for (QCborValue key : j.keys()) {
s = s.bind(QName(key.toString().toUtf8()),QCborValue(j[key])); // TODO directly use toUtf8 to remove utf8>utf16>utf8 encoding
auto k = key.toString();
if (!k.isEmpty()) {
auto id = QIdentifier(k.toLatin1());
r = r.item(id).bind(QCborValue(j[key]));
}
}
return s;
return r;
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
}
......@@ -568,7 +575,7 @@ struct QBind<QCborMap, TResult> {
while ((i = s.item(k))) {
QCborValue json;
if ((s = i.bind(json)))
j.insert(QString::fromUtf8(k),json);
j.insert(k.latin1(),json);
}
return s;
}
......
#include "QData.h"
template<> QData<Utf8>::QData(QDataView<Utf8> v);
QIdentifier::QIdentifier(QIdentifierLiteral n) : QData(QByteArray::fromRawData(n.data(), n.size())) { /* no need to check QIdentifierLiteral content or take ownership */ }
QIdentifier::QIdentifier(QLatin1String l) : QData(QByteArray(l.data(), l.size())) {}
#pragma once
#include <QtCore/qbytearray.h>
#include <QtCore/qdatastream.h>
// Content types
//! Non-empty string of [0x20..0x7E] ASCII subset with a preference for [A..Za..z_][0..9A..Za..z_]* identifiers
struct AsciiIdentifier {
static bool isValid(const char* s, int size)
{
if ((s == nullptr && size != 0) || size < 1)
return false;
for (int i=0; i<size; ++i) {
if (s[i] < 0x20/*' '*/ || 0x7F/*DEL*/ == s[i])
return false;
if (i==0 && (s[i]!='_' && (s[0]<'A' || 'z'<s[0] || ('Z'<s[0] && s[0]<'a'))))
qWarning("Non alphabetic first character may not be supported by IBind");
if (i< 0 && (s[i]!='_' && (s[i]<'0' || 'z'<s[i] || ('9'<s[i] && s[i]<'A')
|| ('Z'<s[i] && s[i]<'a'))))
qWarning("Non alphanumeric or underscore character may not be supported by IBind");
}
return true;
}
};
struct AsciiPrintable
{
static bool isValid(const char* s, int size)
{
if ((s == nullptr && size != 0) || size < 0) return false;
for (int i=1; i<size; ++i) {
if (s[i] < 0x20/*' '*/ || 0x7F/*DEL*/ <= s[i]) return false;
}
return true;
}
};
struct Ascii
{
static bool isValid(const char* s, int size)
{
if ((s == nullptr && size != 0) || size < 0) return false;
for (int i=1; i<size; ++i) {
if (s[i] <= 0) return false;
}
return true;
}
};
struct Latin1
{
static bool isValid(const char* s, int size) { return !((s == nullptr && size != 0) || size < 0); }
};
struct Utf8
{
static bool isValid(const char* s, int size) { return !((s == nullptr && size != 0) || size < 0); }
};
template<class TContent> class QDataView;
//! Associates a QByteArray with TContent type to allow static checks and optimizations when data is transferred
template<class TContent> class QData
{
public:
using Content = TContent;
explicit QData( QByteArray&& b) : m_bytes(b) { Q_ASSERT(Content::isValid(m_bytes.data(), m_bytes.size())); }
explicit QData(const QByteArray& b) : m_bytes(b) { Q_ASSERT(Content::isValid(m_bytes.data(), m_bytes.size())); }
QData(QDataView<TContent> v);
QData() : m_bytes() {}
constexpr bool isNull() const noexcept { return m_bytes.isNull (); }
constexpr bool isEmpty() const noexcept { return m_bytes.isEmpty(); }
constexpr int size() const noexcept { return m_bytes.size (); }
constexpr const char* data() const noexcept { return m_bytes.data(); }
void set(QByteArray&& b) noexcept { m_bytes=b; }
void set(QByteArray& b) noexcept { m_bytes=b; }
protected:
template<class T> friend bool operator==(const QData <T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator!=(const QData <T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator< (const QData <T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator<=(const QData <T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator> (const QData <T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator>=(const QData <T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator==(const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator!=(const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator< (const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator<=(const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator> (const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator>=(const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator==(const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator!=(const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator< (const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator<=(const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator> (const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator>=(const QDataView<T> &a, const QData <T> &b) noexcept;
friend QDataStream &operator<<(QDataStream &out, const QData<TContent> &p) { return out << p.m_bytes; }
friend QDataStream &operator>>(QDataStream &out, QData<TContent> &p) { return out >> p.m_bytes; }
QByteArray m_bytes;
};
#include <QtCore/qstring.h>
#ifdef NO_DEBUG
#define CONSTEXPR_IF_NO_DEBUG constexpr
#else
#define CONSTEXPR_IF_NO_DEBUG
#endif
#ifdef NO_DEBUG
#define DEBUG_CHECK(cond)
#else
#define DEBUG_CHECK(cond) Q_ASSERT(cond)
#endif
template<class TContent>
class QDataView
{
public:
using Content = TContent;
CONSTEXPR_IF_NO_DEBUG explicit QDataView(const char* s ) : m_bytes(s, s ? int(qstrlen(s)) : 0) { DEBUG_CHECK(Content::isValid(m_bytes.data(), m_bytes.size())); }
template<int Size>
CONSTEXPR_IF_NO_DEBUG explicit QDataView(const char(&s)[Size] ) : m_bytes(s, Size-int(sizeof('\0')) ) { DEBUG_CHECK(Content::isValid(m_bytes.data(), m_bytes.size())); }
CONSTEXPR_IF_NO_DEBUG explicit QDataView(const char* s, int size ) : m_bytes(s, s+size ) { DEBUG_CHECK(Content::isValid(m_bytes.data(), m_bytes.size())); }
CONSTEXPR_IF_NO_DEBUG explicit QDataView(const char* s, const char* end) : m_bytes(s, end ) { DEBUG_CHECK(Content::isValid(m_bytes.data(), m_bytes.size())); }
explicit QDataView( QByteArray&& s) : m_bytes(s.constData(), s.size()) { DEBUG_CHECK(Content::isValid(m_bytes.data(), m_bytes.size())); }
explicit QDataView(const QByteArray& s) : m_bytes(s.constData(), s.size()) { DEBUG_CHECK(Content::isValid(m_bytes.data(), m_bytes.size())); }
QDataView(QData<Content>&& d) : m_bytes(d.data(), d.size()) {}
QDataView(QData<Content>& d) : m_bytes(d.data(), d.size()) {}
constexpr QDataView() : m_bytes() {}
constexpr bool isNull() const noexcept { return m_bytes.isNull (); }
constexpr bool isEmpty() const noexcept { return m_bytes.isEmpty(); }
constexpr int size() const noexcept { return m_bytes.size (); }
constexpr const char* data() const noexcept { return m_bytes.data(); }
inline QData<Content> content() const noexcept { return *this; }
protected:
template<class T> friend bool operator==(const QDataView<T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator!=(const QDataView<T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator< (const QDataView<T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator<=(const QDataView<T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator> (const QDataView<T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator>=(const QDataView<T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator==(const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator!=(const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator< (const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator<=(const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator> (const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator>=(const QData <T> &a, const QDataView<T> &b) noexcept;
template<class T> friend bool operator==(const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator!=(const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator< (const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator<=(const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator> (const QDataView<T> &a, const QData <T> &b) noexcept;
template<class T> friend bool operator>=(const QDataView<T> &a, const QData <T> &b) noexcept;
QLatin1String m_bytes;
};
template<class T> QData<T>::QData(QDataView<T> v) : m_bytes(v.data(), v.size()) {}
template<class T> inline bool operator==(const QDataView<T> &a, const QDataView<T> &b) noexcept { return (a.m_bytes.size() == b.m_bytes.size()) && (memcmp(a.m_bytes.data(), b.m_bytes.data(), a.m_bytes.size())==0); }
template<class T> inline bool operator!=(const QDataView<T> &a, const QDataView<T> &b) noexcept { return !(a==b); }
template<class T> inline bool operator< (const QDataView<T> &a, const QDataView<T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) < 0; }
template<class T> inline bool operator<=(const QDataView<T> &a, const QDataView<T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) <= 0; }
template<class T> inline bool operator> (const QDataView<T> &a, const QDataView<T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) > 0; }
template<class T> inline bool operator>=(const QDataView<T> &a, const QDataView<T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) >= 0; }
template<class T> inline bool operator==(const QData <T> &a, const QDataView<T> &b) noexcept { return (a.size() == b.m_bytes.size()) && (memcmp(a.data(), b.m_bytes.data(), a.size())==0); }
template<class T> inline bool operator!=(const QData <T> &a, const QDataView<T> &b) noexcept { return !(a==b); }
template<class T> inline bool operator< (const QData <T> &a, const QDataView<T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) < 0; }
template<class T> inline bool operator<=(const QData <T> &a, const QDataView<T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) <= 0; }
template<class T> inline bool operator> (const QData <T> &a, const QDataView<T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) > 0; }
template<class T> inline bool operator>=(const QData <T> &a, const QDataView<T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) >= 0; }
template<class T> inline bool operator==(const QDataView<T> &a, const QData <T> &b) noexcept { return (a.m_bytes.size() == b.size()) && (memcmp(a.m_bytes.data(), b.data(), a.m_bytes.size())==0); }
template<class T> inline bool operator!=(const QDataView<T> &a, const QData <T> &b) noexcept { return !(a==b); }
template<class T> inline bool operator< (const QDataView<T> &a, const QData <T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) < 0; }
template<class T> inline bool operator<=(const QDataView<T> &a, const QData <T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) <= 0; }
template<class T> inline bool operator> (const QDataView<T> &a, const QData <T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) > 0; }
template<class T> inline bool operator>=(const QDataView<T> &a, const QData <T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) >= 0; }
template<class T> inline bool operator==(const QData <T> &a, const QData <T> &b) noexcept { return (a.m_bytes.size() == b.m_bytes.size()) && (memcmp(a.m_bytes.data(), b.m_bytes.data(), a.m_bytes.size())==0); }
template<class T> inline bool operator!=(const QData <T> &a, const QData <T> &b) noexcept { return !(a==b); }
template<class T> inline bool operator< (const QData <T> &a, const QData <T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) < 0; }
template<class T> inline bool operator<=(const QData <T> &a, const QData <T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) <= 0; }
template<class T> inline bool operator> (const QData <T> &a, const QData <T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) > 0; }
template<class T> inline bool operator>=(const QData <T> &a, const QData <T> &b) noexcept { return qstrcmp(a.m_bytes, b.m_bytes) >= 0; }
// //////////////////////////////////////////////////////////////////////////
// Specialized QData
#include <QtCore/qmetatype.h>
//! Explicitely utf8 encoded string of char
class QUtf8Data : public QData<Utf8>
{
public:
using QData::QData;
const QByteArray& utf8() const noexcept { return m_bytes; }
protected:
friend QDataStream &operator<<(QDataStream &out, const QUtf8Data &p) { return out << QString(p.m_bytes); }
friend QDataStream &operator>>(QDataStream &out, QUtf8Data &p) { QString s; out >> s; p.m_bytes = s.toUtf8(); return out; }
};
Q_DECLARE_METATYPE(QUtf8Data)
using QUtf8DataView = QDataView<Utf8>;
class QAsciiData : public QData<Ascii>
{
public:
using QData::QData;
const QByteArray& utf8() const noexcept { return m_bytes; }
constexpr QLatin1String latin1() const noexcept { return QLatin1String(m_bytes.data(), m_bytes.size()); }
};
Q_DECLARE_METATYPE(QAsciiData)
using QAsciiDataView = QDataView<Ascii>;
class QIdentifierLiteral;
//! A string using a highly-interoperable, printable, subset of ASCII that is
//! compatible with utf8, latin1 and other ANSI codepages without transformation
//! \warning This implementation only enforces input ASCII subset if DEBUG is defined
class QIdentifier : public QData<AsciiIdentifier>
{
public:
using QData::QData;
QIdentifier() : QData() {}
explicit QIdentifier(QIdentifierLiteral);
explicit QIdentifier(QLatin1String );
explicit QIdentifier( QByteArray&& b) : QData(b) {}
explicit QIdentifier(const QByteArray& b) : QData(b) {}
const QByteArray& utf8() const noexcept { return m_bytes; }
constexpr QLatin1String latin1() const noexcept { return QLatin1String(m_bytes.data(), m_bytes.size()); }
};
Q_DECLARE_METATYPE(QIdentifier)
//! Read-only view of a null or non-empty string literal using a highly-interoperable, printable, subset of ASCII that is
//! compatible with utf8, latin1 and other ANSI codepages without transformation
//! \warning This implementation only enforces input ASCII subset if DEBUG is defined
class QIdentifierLiteral : public QDataView<AsciiIdentifier>
{
public:
using QDataView::QDataView;
constexpr inline const char* utf8() const noexcept { return m_bytes.data(); } // TODO Remove ?
constexpr inline QLatin1String latin1() const noexcept { return m_bytes; }
};
inline QIdentifierLiteral operator""_idv(const char* s, size_t sz) { return QIdentifierLiteral(s, int(sz)); }
......@@ -57,7 +57,7 @@ class QDataWriter : public IWriter // allows runtime flexibility but requires QB
{
Q_DISABLE_COPY(QDataWriter)
public:
QDataWriter(QDataStream* io) : io(io) { Q_ASSERT(io); version = QAsciiPrintable(QByteArray::number(io->version())); }
QDataWriter(QDataStream* io) : io(io) { Q_ASSERT(io); version = QAsciiData(QByteArray::number(io->version())); }
BindMode mode() const noexcept { return BindMode::Write; }
......@@ -72,21 +72,21 @@ protected:
bool _sequence(quint32* s=nullptr) { if (s) *io << *s; return true; }
bool _record (quint32* s=nullptr) { if (s) *io << *s; return true; }
bool _null ( ) { *io << nullptr; return true; }
bool _bind ( const char* u8) { QByteArray ba = QByteArray::fromRawData(u8, int(qstrlen(u8)+sizeof('\0'))); return _bind(ba); }
bool _bind ( QUtf8DataView u) { QByteArray ba = QByteArray::fromRawData(u.data(), u.size()+int(sizeof('\0'))); return _bind(ba); }
template<typename T>
bool _bind ( T&& t) { *io << t; return true; }
bool _isOk() noexcept { return io && io->status()==QDataStream::Ok; }
bool _out ( ) { return true; }
bool _item( ) { return true; }
bool _item(QIdentifier&) { return true; } // record keys are implicit, maps should be serialized as a sequence of records with key and value items
bool _item( QName) { return true; } // for QBaseWriter
bool _out ( ) { return true; }
bool _item( ) { return true; }
bool _item(QIdentifier& ) { return true; } // record keys are implicit, maps should be serialized as a sequence of records with key and value items
bool _item(QIdentifierLiteral) { return true; } // for QBaseWriter
void _meta(QMetaData& m) { m[qmDataStreamVersion]=version; }
private:
QDataStream* io;
QAsciiPrintable version;
QAsciiData version;
};
template<typename T> struct BindSupport<T ,QDataWriter> : BindNative {}; //!< \remark Use meta() or QVariant to encapsulate Ts that are not statically known (provided qRegisterMetaType<T>() and qRegisterMetaTypeStreamOperators<T>() are called)
......