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
......
This diff is collapsed.
......@@ -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) {
......
......@@ -57,11 +57,11 @@ Bindable>Cbor |bf647479706502666e756d6265726b2b34342031323334353637ff
Bindable>Json |{"type":2,"number":"+44 1234567"}
Person<>Json |================================================================================
Json>P |(Person)[ names:[ John Doe] height:1.75 age:-1 phones:[] comments: children:[]] | [ Ignored character 0:1 Ignored character 0:15 Ignored character 0:23 Ignored character 0:40 Ignored character 0:42 Ignored character 0:58 Ignored character 0:76 Ignored character 0:77]
P>Json |{"names":["John","Doe"],"height":1.7500000000000002,"age":-1,"phones":[],"comments":"","children":[]}
Json>P |(Person)[ names:[ John Doe] height:1.75 age:-1 phones:[] comments: children:[]] | [ Ignored character 0:72 Ignored character 0:73 Expected record 0:73]
P>JsonValue |{"age":-1,"children":[],"comments":"","height":1.7500000000000004,"names":["John","Doe"],"phones":[]}
P>Json |{"names":["John","Doe"],"height":1.75,"age":-1,"phones":[],"comments":"","children":[]}
Json>P |(Person)[ names:[ John Doe] height:1.75 age:-1 phones:[] comments: children:[]] | [ Ignored character 0:58 Ignored character 0:59 Expected record 0:59]
P>JsonValue |{"age":-1,"children":[],"comments":"","height":1.75,"names":["John","Doe"],"phones":[]}
JsonValue>P |(Person)[ names:[ John Doe] height:1.75 age:-1 phones:[] comments: children:[]] | []
Json>JsonValue|{"age":-1,"height":1.7500000000000004,"names":["John","Doe"],"phones":["comments","children"]} | [ Ignored character 0:72 Ignored character 0:73 Ignored character 0:84 Ignored character 0:85 Ignored character 0:86 Ignored character 0:98 Ignored character 0:99]
Json>JsonValue|{"age":-1,"height":1.75,"names":["John","Doe"],"phones":["comments","children"]} | [ Ignored character 0:58 Ignored character 0:59 Ignored character 0:70 Ignored character 0:71 Ignored character 0:72 Ignored character 0:84 Ignored character 0:85]
Json>Cbor |bf656e616d65739f644a6f686e63446f65ff66686569676874fa3fe0000063616765206670686f6e65739f68636f6d6d656e7473686368696c6472656effff
Person<>Cbor |================================================================================
Cbor>P |(Person)[ names:[ John Doe] height:1.75 age:-1 phones:[] comments: children:[]] | []
......
Markdown is supported
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