Commit 4a60576d authored by EXT Arnaud Clère's avatar EXT Arnaud Clère

WIP Replace QUtf8String with tagged QByteArray types

Use AsciiIdentifier subset to read item names
parent 3c735968
#include "QBind_impl.h"
QIdentifier::QIdentifier(QName n) : QBytes(QByteArray(n.ascii(), n.size())) { /* no need to check QName content */ }
......@@ -45,6 +45,7 @@ gcc:QMAKE_CXXFLAGS += -ftemplate-backtrace-limit=0
#QMAKE_EXTRA_COMPILERS += protoc
SOURCES += \
QBind.cpp \
data.cpp \
main.cpp
......
......@@ -50,6 +50,7 @@
Class& operator=(Class&& o) noexcept { if (this!=&o) { Statements } return *this; }
#include <QtCore/qbytearray.h>
//! Explicitely utf8 encoded string of char
//! \warning This implementation opportunistically uses inheritance whereas QBind only needs distinct types
//! for QString, QByteArray, and utf8 strings to avoid costly utf8-utf16 conversions and bytes-utf8 ambiguities
......@@ -64,10 +65,63 @@ public:
#include <QtCore/qmetatype.h>
Q_DECLARE_METATYPE(QUtf8String)
// Content tags
struct AsciiIdentifier {}; //!< [0-9A-Za-z_] ASCII subset
struct AsciiPrintable {}; //!< [0x20..0x7F) ASCII subset
struct Ascii {};
struct Latin1 {};
struct Utf8 {};
struct Utf8Mime {};
//! Associates a QByteArray with a Content tag type to allow static checks and optimizations when data is transferred
template<class TContent>
class QBytes
{
public:
using Content = TContent;
QBytes() = default;
explicit QBytes( QByteArray&& b) : m_bytes(b) {}
explicit QBytes(const QByteArray& b) : m_bytes(b) {}
constexpr inline bool isNull() const noexcept { return m_bytes.isNull(); }
constexpr inline int size() const noexcept { return m_bytes.size (); }
inline bool operator==(const char *s) const noexcept { return m_bytes==s; }
inline bool operator!=(const char *s) const noexcept { return m_bytes!=s; }
inline operator QByteArray() const noexcept { return m_bytes ; }
inline const char* constData() const noexcept { return m_bytes.data(); }
inline QByteArray& data() noexcept { return m_bytes ; }
protected:
QByteArray m_bytes;
};
using QUtf8Bytes = QBytes<Utf8>;
Q_DECLARE_METATYPE(QUtf8Bytes)
using QAsciiPrintable = QBytes<AsciiPrintable>;
Q_DECLARE_METATYPE(QAsciiPrintable)
#include <QtCore/qstring.h>
class QName;
class QIdentifier : public QBytes<AsciiIdentifier>
{
public:
using QBytes::QBytes;
QIdentifier() = default;
QIdentifier(QName n);
inline operator QLatin1String() const noexcept { return QLatin1String(m_bytes); }
inline bool operator==(const char *s) const noexcept { return m_bytes.compare(s,Qt::CaseInsensitive)==0; }
inline bool operator!=(const char *s) const noexcept { return m_bytes.compare(s,Qt::CaseInsensitive)!=0; }
inline bool operator< (QIdentifier s) const noexcept { return m_bytes.compare(s,Qt::CaseInsensitive)< 0; }
};
Q_DECLARE_METATYPE(QIdentifier)
//! Read-only view of a 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 does not enforce input ASCII subset
//! \warning This implementation only enforces input ASCII subset if DEBUG is defined
class QName
{
public:
......@@ -92,6 +146,7 @@ public:
}
#endif
}
inline explicit QName(const QIdentifier& id) : m_ascii(id.constData(), id.size()) {}
constexpr inline bool isNull() const noexcept { return m_ascii.isNull(); }
constexpr inline int size() const noexcept { return m_ascii.size (); }
......@@ -99,16 +154,16 @@ public:
constexpr inline const char* utf8() const noexcept { return m_ascii.data (); }
constexpr inline const char* latin1() const noexcept { return m_ascii.latin1(); }
constexpr inline operator QLatin1String() const noexcept { return m_ascii ; }
constexpr inline QLatin1String toLatin1() const noexcept { return m_ascii ; }
/**/ inline QUtf8String toUtf8 () const noexcept { return QUtf8String(m_ascii.data()); }
inline QIdentifier toIdentifier() const noexcept { return QIdentifier(QByteArray(m_ascii.data(), m_ascii.size())); }
inline QUtf8Bytes toUtf8() const noexcept { return QUtf8Bytes (QByteArray(m_ascii.data(), m_ascii.size())); }
inline bool operator==(const char *s) const noexcept { return m_ascii==s; }
inline bool operator!=(const char *s) const noexcept { return m_ascii!=s; }
private:
QLatin1String m_ascii;
};
constexpr inline QName operator""_name(const char* s, size_t sz) { return QName(s, int(sz)); }
constexpr inline QName operator""_idv(const char* s, size_t sz) { return QName(s, int(sz)); }
// //////////////////////////////////////////////////////////////////////////
// Standard error names
......@@ -141,12 +196,12 @@ static QName qBindIgnoredBytes ("Ignored bytes" );
#include <QtCore/qstring.h>
//! QMetaData is optional data about current data to enable optimized processing (e.g. tag, , style, etc.) and/or transmission (QAbstractItemModel headers, CBOR tags, XML attribute, CSS, numpy.ndarray shapes, etc.)
class QMetaData : public std::vector<std::pair<QName,QUtf8String>>
class QMetaData : public std::vector<std::pair<QName,QAsciiPrintable>>
{
public:
using vector::vector;
iterator find(QName n) { return std::find_if(begin(), end(), [n](std::pair<QName,QUtf8String>& item){ return item.first==n; }); }
QUtf8String& operator[](QName n) { return find(n)->second; }
iterator find(QName n) { return std::find_if(begin(), end(), [n](std::pair<QName,QAsciiPrintable>& item){ return item.first==n; }); }
QAsciiPrintable& operator[](QName n) { return find(n)->second; }
};
static QName qmDataStreamVersion("qmDataStreamVersion"); //!< Allows IBind support of QDataStream
......@@ -249,14 +304,14 @@ public:
template<typename T> T_ with (T& t, QBindFunction<T> customBind) { return customBind(t, std::move(unsafeThis())) ? std::move(outer) : T_(); }
// Literal metadata support
/**/ Val<T_> meta(QName n, QUtf8String m) { return meta({{n,m}}); }
/**/ Val<T_> meta(QName n, QAsciiPrintable m) { return meta({{n,m}}); }
/**/ Val<T_> meta (QMetaData&& m) { return meta ( m); }
/**/ Seq<T_> sequence( quint32 s) { return sequence(&s); }
/**/ Rec<T_> record ( quint32 s) { return record (&s); }
// Shortcuts
template<typename T> Seq<T_> operator<<( T&& t) { return sequence().bind(std::forward<T>(t)); } // stream compatible
/**/ Rec<T_> record (QName n) { return meta({{qmName,n.toUtf8()}}).record(); }
/**/ Rec<T_> record (QName n) { return meta({{qmName,QAsciiPrintable(QByteArray(n.ascii()))}}).record(); }
private:
Val<TResult> unsafeThis() noexcept { return Val<TResult>(outer->_unsafeCopy()); }
......@@ -321,7 +376,7 @@ public:
operator TResult() { return out(); /* calls T_::operator TResult() if T_ != TResult */ }
/**/ T_ out ( ) { return outer->_out ( ) ? std::move(outer) : T_ (); }
Val<Rec<T_>> item(QUtf8String& n) { return outer->_item(n) ? Val<Rec<T_>>(std::move(*this)) : Val<Rec<T_>>(); }
Val<Rec<T_>> item(QIdentifier& n) { return outer->_item(n) ? Val<Rec<T_>>(std::move(*this)) : Val<Rec<T_>>(); }
Val<Rec<T_>> item( QName n) { return outer->_item(n) ? Val<Rec<T_>>(std::move(*this)) : Val<Rec<T_>>(); }
// Shortcuts
......@@ -336,7 +391,7 @@ public:
template<typename T> Rec<T_> with (QName n, T& t, QBindFunction<T> customBind) { return item(n).bind (t, customBind); }
private:
template<typename T, class TResult, typename TEnabledIf> friend struct QBind;
Val<TResult> unsafeItem(QUtf8String& n) noexcept { return outer->_item(n) ? Val<TResult>(outer._unsafeCopy()) : Val<TResult>(); }
Val<TResult> unsafeItem(QIdentifier& n) noexcept { return outer->_item(n) ? Val<TResult>(outer._unsafeCopy()) : Val<TResult>(); }
T_ outer = T_();
};
......@@ -399,7 +454,7 @@ private:
template<typename T> bool _bind(BindGeneric, T&& t) { return QBind<RemoveCvRef<T>,Cur>::bind(Val<Cur>(_unsafeCopy()),std::forward<T>(t)); }
bool _item( QName n) { return Q_LIKELY(impl) && impl->_item(n); }
bool _item(QUtf8String& n) { return Q_LIKELY(impl) && impl->_item(n); }
bool _item(QIdentifier& n) { return Q_LIKELY(impl) && impl->_item(n); }
bool _item( ) { return Q_LIKELY(impl) && impl->_item( ); }
bool _out ( ) { return Q_LIKELY(impl) && impl->_out ( ); }
private:
......@@ -439,7 +494,7 @@ struct IBind {
virtual bool _record (quint32* size=nullptr) = 0;
virtual bool _null ( ) = 0;
virtual bool _item( QUtf8String& n) = 0;
virtual bool _item( QIdentifier& n) = 0;
virtual bool _item( QName n) = 0;
virtual bool _item( ) = 0;
virtual bool _out ( ) = 0; //!< End of sequence or record
......@@ -565,7 +620,7 @@ struct IWriter : public IBind
virtual bool _item( ) = 0;
virtual bool _item( QName n) = 0;
virtual bool _item(QUtf8String& n) { return _item(QName(n)); }
virtual bool _item(QIdentifier& n) { return _item(QName(n)); }
//! End of sequence or record
//! Few IWriter need to process this (contiguous IWriter need to mark the end of indefinite sequences and records for instance)
......@@ -680,8 +735,8 @@ struct IReader : public IBind
virtual BindMode mode() const noexcept { return BindMode::Read; }
virtual bool _item( ) = 0;
virtual bool _item(QUtf8String& n) = 0;
virtual bool _item( QName u) { QUtf8String s; return _item(s) && u==s; }
virtual bool _item(QIdentifier& n) = 0;
virtual bool _item( QName u) { QIdentifier s; return _item(s) && s.constData()==u; }
virtual bool _bind(QUtf8String& u) = 0;
virtual bool _bind( QString& s) { QUtf8String u; if (!_bind(u)) return false; s=QString::fromUtf8(u); return true; }
......@@ -700,9 +755,9 @@ struct IReader : public IBind
virtual bool _bind( QVariant& dst) {
_setChoice(true);
quint32 size=0; QUtf8String key; QVariant item;
if (_sequence(&size)) { QVariantList l; while (_item( )) { l.append( _bind(item) ? item : QVariant()); } dst = l; return _out(); }
if (_record (&size)) { QVariantMap l; while (_item(key)) { l.insert(key, _bind(item) ? item : QVariant()); } dst = l; return _out(); }
quint32 size=0; QIdentifier key; QVariant item;
if (_sequence(&size)) { QVariantList l; while (_item( )) { l.append( _bind(item) ? item : QVariant()); } dst = l; return _out(); }
if (_record (&size)) { QVariantMap l; while (_item(key)) { l.insert(QByteArray(key), _bind(item) ? item : QVariant()); } dst = l; return _out(); }
bool b; if (_bind( b)) { dst = QVariant(b); return true; }
quint64 u; if (_bind( u)) { dst = QVariant(u); return true; }
qint64 l; if (_bind( l)) { dst = QVariant(l); return true; }
......@@ -901,8 +956,8 @@ TResult qbind(Val<TResult>&& v, T* t) {
if (p.isEnumType()) {
pv.convert(QVariant::Int);
i = i.meta({{qmName, p.isFlagType() ?
QUtf8String(p.enumerator().valueToKeys(pv.value<int>())) :
QUtf8String(p.enumerator().valueToKey (pv.value<int>()))}});
QAsciiPrintable(p.enumerator().valueToKeys(pv.value<int>())) :
QAsciiPrintable(p.enumerator().valueToKey (pv.value<int>()))}});
}
r = i.bind(pv);
}
......@@ -1153,7 +1208,7 @@ struct QBind<Val<Cursor>> {
if ( (srcRec = src.record())) {
/**/ dstRec = dst.record();
QUtf8String srcKey; Val<Cursor> srcVal; Val<Cursor> dstVal;
QIdentifier srcKey; Val<Cursor> srcVal; Val<Cursor> dstVal;
while((srcVal = srcRec.unsafeItem(srcKey))) {
dstVal = dstRec.unsafeItem(srcKey);
srcRec = Rec<Cursor>(srcVal.bind(std::move(dstVal)));
......
......@@ -262,7 +262,7 @@ protected:
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(QUtf8String& k) { steps.last().key=QName(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; }
......@@ -366,10 +366,10 @@ protected:
}
bool _item( QName u) { if (caching) { return caching->_item(u); }
QUtf8String s;
QIdentifier s;
while (true) {
if (levels.last().cachedItems. contains(u.utf8())) { // must be checked before we consume _item(s)
cachedValue = levels.last().cachedItems.take(u.utf8());
if (levels.last().cachedItems. contains(QIdentifier(u))) { // must be checked before we consume _item(s)
cachedValue = levels.last().cachedItems.take(QIdentifier(u));
Q_ASSERT(!cacheLevel);
if (!device()->isSequential()) {
cachingAfter = device()->pos();
......@@ -391,7 +391,7 @@ protected:
else if (!_item(s)) { // record() end reached
return false;
}
else if (u == s ) {
else if (u == s.constData()) {
return true ;
}
else {
......@@ -405,7 +405,7 @@ protected:
continue; // until !_item(s) or u==s
}
}}
bool _item(QUtf8String& k) { if (caching) { return caching->_item(k); }
bool _item(QIdentifier& k) { if (caching) { return caching->_item(k); }
if (!hasNext()) {
return false;
}
......@@ -413,7 +413,7 @@ protected:
if (!_bind(s)) {
return false;
}
k = s.toUtf8();
k = QIdentifier(s.toLatin1());
return true; }
bool _item( ) { if (caching) { return caching->_item(); }
if (!hasNext()) {
......@@ -457,7 +457,7 @@ private:
return true; }
// Read/Write caching of out-of-order item keys
struct Level { QMap<QUtf8String,QCborValue> cachedItems; };
struct Level { QMap<QIdentifier,QCborValue> cachedItems; };
QStack<Level> levels = QStack<Level>(); //!< dynamic context required to cache out-of-order item(QName)
quint8 cacheLevel = 0;
QCborValue cachedValue ; //!< Only used AFTER --cacheLevel==1
......@@ -564,7 +564,7 @@ struct QBind<QCborMap, TResult> {
}
else if (v->mode()==Read) {
auto s(v.record());
QUtf8String k; Val<Rec<TResult>> i;
QIdentifier k; Val<Rec<TResult>> i;
while ((i = s.item(k))) {
QCborValue json;
if ((s = i.bind(json)))
......
......@@ -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 = QUtf8String::number(io->version()); }
QDataWriter(QDataStream* io) : io(io) { Q_ASSERT(io); version = QAsciiPrintable(QByteArray::number(io->version())); }
BindMode mode() const noexcept { return BindMode::Write; }
......@@ -80,13 +80,13 @@ protected:
bool _out ( ) { return true; }
bool _item( ) { return true; }
bool _item(QUtf8String&) { return true; } // record keys are implicit, maps should be serialized as a sequence of records with key and value items
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
void _meta(QMetaData& m) { m[qmDataStreamVersion]=version; }
private:
QDataStream* io;
QUtf8String version;
QAsciiPrintable 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)
......
......@@ -104,16 +104,16 @@ public:
QJsonVisitor(const QJsonValue* v) : json(v) { Q_ASSERT(v); }
void reset(QJsonValue* v) { json=v; Q_ASSERT(v); steps.resize(0); errors.resize(0); }
struct Error { QName error; QUtf8String path; template<class T> T bind(Val<T>&& value) { return value.bind(QUtf8String(error.utf8())+' '+path); } };
struct Error { QName error; QAsciiPrintable path; template<class T> T bind(Val<T>&& value) { return value.bind((QByteArray(error.ascii())+' '+path).data()); } };
QVector<Error> errors;
QUtf8String currentPath() {
QUtf8String path;
QAsciiPrintable currentPath() {
QByteArray path;
Q_FOREACH(Step s, steps) {
if (!s.key.isNull()) { path.append('{').append(s.key.toUtf8() ); }
else { path.append('[').append(QUtf8String::number(s.idx)); }
if (!s.key.isNull()) { path.append('{').append(s.key.data() ); } // QByteArray& provides size
else { path.append('[').append(QByteArray::number(s.idx)); }
}
return path;
return QAsciiPrintable(path);
}
// Shortcuts
......@@ -135,7 +135,7 @@ protected:
bool _bind ( double& v) { if (current().isDouble()) { v = current().toDouble(); return true; } _reportError(qBindExpectedDecimal ); return false; }
bool _item( QName u) { steps.last().key=u; return !(steps.last().item = current(1).toObject().value(steps.last().key)).isUndefined(); }
bool _item(QUtf8String& k) { steps.last().key=k; return !(steps.last().item = current(1).toObject().value(steps.last().key)).isUndefined(); }
bool _item(QIdentifier& k) { steps.last().key=k; return !(steps.last().item = current(1).toObject().value(steps.last().key)).isUndefined(); }
bool _item( ) { steps.last().idx++; return !(steps.last().item = current(1).toArray (). at(steps.last().idx)).isUndefined(); }
bool _out ( ) { steps.removeLast(); return true; }
......@@ -146,7 +146,7 @@ private:
const QJsonValue& current(int outer=0) const { Q_ASSERT(0<=outer && json); return steps.size()-outer <= 0 ? *json : steps[steps.size()-outer-1].item; }
const QJsonValue* json;
struct Step { QString key; int idx=-1; QJsonValue item; Step() = default; };
struct Step { QIdentifier key; int idx=-1; QJsonValue item; Step() = default; };
QStack<Step> steps = QStack<Step>();
bool isChoice = false;
};
......@@ -227,7 +227,7 @@ class QJsonReader : public IReader
public:
QJsonReader(QIODevice* io) : io(io), cacheWriter(&cachedValue), cacheReader(&cachedValue) { Q_ASSERT(io); }
struct Step { int index; const char* end; QMap<QUtf8String,QJsonValue/*TODO QVariant for meta() support*/> cachedItems; Step(int i=-1, const char* e=nullptr) : index(i), end(e) {} };
struct Step { int index; const char* end; QMap<QIdentifier,QJsonValue/*TODO QVariant for meta() support*/> cachedItems; Step(int i=-1, const char* e=nullptr) : index(i), end(e) {} };
struct Error { QName error; int line; int column; int index; template<class T> T bind(Val<T>&& value) { QUtf8String utf8(error.utf8()); utf8.append(' ').append(QUtf8String::number(line)).append(':').append(QUtf8String::number(column)); return value.bind(utf8); } };
QVector<Error> errors;
......@@ -295,9 +295,9 @@ protected:
bool _bind ( QVariant& dst) {
_setChoice(true);
quint32 size=0; QUtf8String key; QVariant item;
if ( _sequence(&size)) { _setChoice(false); QVariantList l; while (_item( )) { l.append( _bind(item) ? item : QVariant()); } dst = l; return _out(); }
if ( _record (&size)) { _setChoice(false); QVariantMap l; while (_item(key)) { l.insert(key, _bind(item) ? item : QVariant()); } dst = l; return _out(); }
quint32 size=0; QIdentifier key; QVariant item;
if ( _sequence(&size)) { _setChoice(false); QVariantList l; while (_item( )) { l.append( _bind(item) ? item : QVariant()); } dst = l; return _out(); }
if ( _record (&size)) { _setChoice(false); QVariantMap l; while (_item(key)) { l.insert(key.operator QLatin1String(), _bind(item) ? item : QVariant()); } dst = l; return _out(); }
bool b; if (_bind(b)) { _setChoice(false); dst = QVariant(b ); return true; }
quint64 u; if (_bind(u)) { _setChoice(false); dst = QVariant(u ); return true; } // may fail after consuming integer part
qint64 l; if (_bind(l)) { _setChoice(false); dst = QVariant(l ); return true; } // may fail after consuming integer part
......@@ -319,10 +319,10 @@ protected:
}
return get(*level.end, "}"); }
bool _item( QName u) { if (caching) { return caching->_item(u); }
QUtf8String s;
QIdentifier s;
while (true) {
if (levels.last().cachedItems. contains(u.utf8())) { // must be checked before we consume _item(s)
cachedValue = levels.last().cachedItems.take(u.utf8());
if (levels.last().cachedItems. contains(QIdentifier(u.utf8()))) { // must be checked before we consume _item(s)
cachedValue = levels.last().cachedItems.take(QIdentifier(u.utf8()));
cacheReader.reset(&cachedValue);
Q_ASSERT(!cacheLevel);
caching = &cacheReader; // let outer Cursor drive QJsonReader depending on bound T
......@@ -331,7 +331,7 @@ protected:
else if (!_item(s)) { // record() end reached
return false;
}
else if (u == s ) {
else if (u == s.constData() ) {
return true ;
}
else { // read cachedValue using a dedicated Cursor and QBind<Val<Cursor>> accepting value of any 'shape' (outer QBind is specialized on outer T which is not usually generic enough)
......@@ -342,13 +342,18 @@ protected:
continue; // until !_item(s) or u==s
}
}}
bool _item(QUtf8String& k) { if (caching) { return caching->_item(k); }
bool _item(QIdentifier& k) { if (caching) { return caching->_item(k); }
Step& level = levels.last();
if (-1 < level.index && !get(',',level.end)) {
return false;
}
level.index++;
return _bind(k) && get(':',level.end); }
QUtf8String s;
if (!_bind(s)) {
return false;
}
k = QIdentifier(s.constData()); // TODO check errors
return get(':',level.end); }
bool _item( ) { if (caching) { return caching->_item(); }
Step& level = levels.last();
if (-1 < level.index && !get(',',level.end)) {
......@@ -581,7 +586,7 @@ struct QBind<QJsonObject, TResult> {
}
else if (v->mode()==Read) {
auto s(v.record());
QUtf8String k; Val<Rec<TResult>> i;
QIdentifier k; Val<Rec<TResult>> i;
while ((i = s.item(k))) {
QJsonValue json;
if ((s = i.bind(json)))
......
......@@ -71,12 +71,12 @@ protected:
void _meta(QMetaData& meta) {
if (T==d) {
if (meta.find(qmChildren)!=meta.end()) {
childrenName = meta[qmChildren];
childrenName = QIdentifier(meta[qmChildren]);
}
if (meta.find(qmColumns)!=meta.end()) {
metaColumnNames=true;
int i=0;
Q_FOREACH(auto k,meta[qmColumns].split(',')) {
Q_FOREACH(auto k,meta[qmColumns].data().split(',')) {
m->insertColumn(i);
m->setHeaderData(i,Qt::Horizontal,k);
columnNames.insert(i,k);
......@@ -84,7 +84,7 @@ protected:
}
}
if (meta.find(qmSizes)!=meta.end()) {
Q_FOREACH(auto k,meta[qmSizes].split(',')) {
Q_FOREACH(auto k,meta[qmSizes].data().split(',')) {
int i = k.toInt();
if (0<i) sizes.append(i);
}
......@@ -205,7 +205,7 @@ protected:
if (!parent.isValid() && row==0) {
int i = m->columnCount();
m->insertColumn(i);
m->setHeaderData(i,Qt::Horizontal,n.toLatin1());
m->setHeaderData(i,Qt::Horizontal,n.latin1());
columnNames.insert(i,n.utf8());
}
}
......@@ -274,7 +274,7 @@ private:
int row=0, col=0; //!< Allows handling {null} equally to {} (but not equally to null) without requiring QModelIndex to reference rows/columns before they actually exist
// Supported QMetaData
QUtf8String childrenName;
QIdentifier childrenName;
QList<QUtf8String> columnNames;
bool metaColumnNames=false;
QVector<int> sizes;
......
......@@ -133,7 +133,7 @@ protected:
bool _record (quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level(QName(""))); return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (settings->value(key()).isNull()) { return true; } _reportError(qBindExpectedNull ); return false; }
bool _item(QUtf8String& k) { levels.last().key=QName(k) ; return true; }
bool _item(QIdentifier& k) { levels.last().key=QName(k) ; return true; }
bool _item( ) { levels.last().idx++ ; return true; }
bool _out ( ) { levels.pop(); settings->endGroup(); return true; }
......
......@@ -80,7 +80,7 @@ private:
}
else {
if (!levels.last().key.isNull())
levels.last().object[levels.last().key.toLatin1()]=v;
levels.last().object[QLatin1String(levels.last().key)]=v;
else
levels.last().array.append(v);
}
......@@ -151,7 +151,7 @@ protected:
bool _record (quint32* =nullptr) { if (current()->type()==QVariant::Map ) { levels.push(Level()); return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (current()->isNull() ) { return true; } _reportError(qBindExpectedNull ); return false; }
bool _item(QUtf8String& k) { levels.last().key=QName(k); return (levels.last().item = current(1)->toMap ().value(QString::fromLatin1(levels.last().key.latin1()), QVariant())).isValid(); }
bool _item(QIdentifier& k) { levels.last().key=QName(k); return (levels.last().item = current(1)->toMap ().value(QString::fromLatin1(levels.last().key.latin1()), QVariant())).isValid(); }
bool _item( ) { levels.last().idx++ ; return (levels.last().item = current(1)->toList().value( levels.last().idx , QVariant())).isValid(); }
bool _out ( ) { levels.pop() ; return true; }
......
......@@ -88,11 +88,11 @@ protected:
bool _out ( ) { io->writeEndElement(); return true; }
bool _item( ) { return true; }
bool _item(QName n) { name=n; return true; }
bool _item(QName n) { name=n.toIdentifier(); return true; }
void _meta(QMetaData& m) {
for (auto&& kv : m) {
if (kv.first == qmName) {
name = QName(kv.second.utf8());
name = QIdentifier(kv.second.constData()); // TODO optimize out unnecessary constData() copy
}
else {
auto found = metaData.find(kv.first);
......@@ -107,10 +107,10 @@ protected:
}
private:
void writeText(QName def, QString text) { io->writeStartElement(tag(def)); att(); io->writeCharacters(text); io->writeEndElement(); }
QLatin1String tag(QName def) { if (name.isNull()) { return def.toLatin1(); } auto n=name; name=QName(); return n.toLatin1(); }
void att() { for (auto&& a : metaData) { if (!a.second.isNull()) io->writeAttribute(a.first.toLatin1(), QString::fromUtf8(a.second)); } }
QLatin1String tag(QName def) { if (name.isNull()) { return def; } auto n=name; name=QName(); return n; }
void att() { for (auto&& a : metaData) { if (!a.second.isNull()) io->writeAttribute(QLatin1String(a.first), QString::fromUtf8(a.second)); } }
QXmlStreamWriter* io;
QMetaData metaData;
QName name;
QIdentifier name;
};
......@@ -134,8 +134,8 @@ QDataStream &operator>>(QDataStream &in, Person &p)
struct Persons : public QVector<Person> {
Cursor bind(Val<Cursor>&& value) {
return value
.meta({{qmChildren,QByteArrayLiteral("children" )}
,{qmColumns ,QByteArrayLiteral("names,height,age,phones,comments")}}) // to optimize multi-dimensional data formats
.meta({{qmChildren,QAsciiPrintable(QByteArrayLiteral("children" ))}
,{qmColumns ,QAsciiPrintable(QByteArrayLiteral("names,height,age,phones,comments"))}}) // to optimize multi-dimensional data formats
.bind(static_cast<QVector<Person>>(*this))
;
}
......@@ -161,7 +161,7 @@ struct QBind<QColor> {
v=v.meta(m);
auto dataStreamVersion = m.find(qmDataStreamVersion);
if (dataStreamVersion!=m.end()) {
if (dataStreamVersion->second.toInt()<7) { v->reportError(QName("Unsupported QDataStream.version()"));
if (dataStreamVersion->second.data().toInt()<7) { v->reportError(QName("Unsupported QDataStream.version()"));
return v.null();
}
return v.sequence().bind(qint8(c.spec())).bind(quint16(c.alpha())).bind(quint16(c.cyan())).bind(quint16(c.magenta())).bind(quint16(c.yellow())).bind(quint16(c.black()));
......@@ -1010,9 +1010,9 @@ void doGuiExample() {
QStandardItemModel matrixModel, treeModel, tableModel, customModel, flatModel;
// Using meta() to drive various custom bind
QModelWriter<>(&matrixModel,false).meta({{qmSizes ,"4,3" } }).bind(transform);
QModelWriter<>(& treeModel ).meta({{qmChildren,"children"}/*,{qmColumns ,"names,age"}*/}).bind(persons);
QModelWriter<>(& tableModel ).meta({{qmColumns , "names,age"} }).bind(persons);
QModelWriter<>(&matrixModel,false).meta({{qmSizes ,QAsciiPrintable("4,3" )} }).bind(transform);
QModelWriter<>(& treeModel ).meta({{qmChildren,QAsciiPrintable("children")}/*,{qmColumns ,"names,age"}*/}).bind(persons);
QModelWriter<>(& tableModel ).meta({{qmColumns ,QAsciiPrintable( "names,age")} }).bind(persons);
// Various possible designs for flexible custom bind
#if 0
......@@ -1035,7 +1035,7 @@ void doGuiExample() {
s = s // To keep working with the active Cursor
.record()
.item(QName("first name"))
.meta(qmColor, person.age >= 42 ? "green" : "blue")
.meta(qmColor, QAsciiPrintable(person.age >= 42 ? "green" : "blue"))
.bind(person.firstName)
.item(QName("office phone")).with([&](Val<Cursor>&& v) {
for (auto&& phone : person.phones) {
......
============| QDebug| Text| Json| Xml| Variant| Cbor| QCborStream| Data| QDataStream| QByteArray| Bindables|Bindables>Cbor|Bindables>Json|total(usecs)|variation(%)
builtin> | 7.3| 2.3| 7.2| 29.0| 7.6| 0.7| 1.5| 1.1| 0.8| 0.1| 0.1| 0.9| 7.6| 66.1| 53.6
builtin> | 7.0| 2.3| 7.6| 28.8| 7.6| 0.7| 1.5| 1.1| 0.8| 0.1| 0.1| 0.8| 7.6| 65.9| 0.4
builtin> | 7.0| 2.1| 7.2| 28.7| 7.5| 0.7| 1.5| 1.1| 0.8| 0.1| 0.1| 0.8| 7.6| 65.1| 1.1
builtin> | 6.9| 2.2| 7.2| 44.0| 6.5| 0.7| 1.6| 1.2| 0.8| 0.1| 0.1| 0.8| 7.4| 79.3| 39.8