Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

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

Simplified all readers using QReader improving performance

Added QUtf8String : public QByteArray
parent 35ac4acd
This diff is collapsed.
......@@ -127,12 +127,14 @@ protected:
bool _null ( ) { return putSimpleValue(cbor::Null ); }
bool _bind ( QByteArray& s) { return putInteger (quint64(s.size()) ,cbor::ByteStringType) && (Q_LIKELY(io->write(s)) || *s=='\0'); }
bool _bind ( const char* s) { return putInteger (strlen(s) ,cbor::TextStringType) && (Q_LIKELY(io->write(s)) || *s=='\0'); }
bool _bind ( QString& s) { 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 ( QString& s) { QUtf8String u(s.toUtf8());
return putInteger (quint64(u.size()) ,cbor::TextStringType) && (Q_LIKELY(io->write(u)) || u.isEmpty()); }
//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 ( 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); }
......@@ -180,89 +182,72 @@ private:
#include <QtCore/qcborstream.h>
#include <QtCore/qstack.h>
class QCborReaderImpl;
class QCborReader : public QBaseReader<QCborReader, QCborReaderImpl>
class QCborReader : public IReader
{
Q_PROTECTED_COPY(QCborReader)
Q_DISABLE_COPY(QCborReader)
public:
Q_ENABLE_MOVE_DEFAULT(QCborReader)
QCborReader(QCborStreamReader* io);
private:
friend class QBaseReader<QCborReader, QCborReaderImpl>;
friend class QCborReaderImpl; // uses method below
QCborReader(QCborReaderImpl* outer) : QBaseReader(outer, false) {}
};
class QCborReaderImpl
{
Q_DISABLE_COPY(QCborReaderImpl)
QCborReader(QCborStreamReader* io) : io(io) { Q_ASSERT(io); }
QCborStreamReader* io;
bool isChoice = false;
public:
QCborReaderImpl(QCborStreamReader* io) : io(io) { Q_ASSERT(io); }
struct Error { const char* error; qint64 index; template<class T> T bind(Val<T> value) { QUtf8String utf8(error); utf8.append(' ').append(QUtf8String::number(index)); return value.bind(utf8.constData()); } };
struct Error { const char* error; qint64 index; QCborError cborError; template<class T> T bind(Val<T> value) { QUtf8String utf8(error); utf8.append(' ').append(QUtf8String::number(index)); return value.bind(utf8.constData()); } };
QVector<Error> errors;
protected:
friend class QScopedChoice<Val<QCborReader>>;
void setChoice(bool v) { isChoice=v; }
friend class QBaseReader<QCborReader, QCborReaderImpl>;
void reportError(const char* error) { if (!isChoice) errors.append(Error{ error, io->currentOffset() }); }
// Shortcuts
/**/ Val<QReader<IReader>> value ( ) { return QReader<IReader>(this).value ( ); }
/**/ Val<QReader<IReader>> meta ( QMetaData&& m) { return QReader<IReader>(this).meta ( m); }
/**/ Val<QReader<IReader>> meta ( QMetaData& m) { return QReader<IReader>(this).meta ( m); }
/**/ Seq<QReader<IReader>> sequence(quint32* s=nullptr) { return QReader<IReader>(this).sequence ( s); }
template<typename T> QReader<IReader> bind ( T&& t) { return QReader<IReader>(this).bind(std::forward<T>(t)); }
protected:
template<class T_> friend class Val; // calls methods below
bool _sequence(quint32* =nullptr) { if (io->isArray ()) { io->enterContainer(); return true; } reportError(qBindExpectedSequence); return false; }
bool _record (quint32* =nullptr) { if (io->isMap ()) { io->enterContainer(); return true; } reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (io->isNull ()) { return true; } reportError(qBindExpectedNull ); return false; }
bool _bind ( QString& s) { if (io->isString()) {
s.clear();
auto r = io->readString();
while (r.status == QCborStreamReader::Ok) {
s.append(r.data);
r = io->readString();
}
if (r.status == QCborStreamReader::Error) {
if (!s.isEmpty()) {
reportError(qBindIgnoredBytes);
s.clear();
}
return false;
}
return true;
} else { reportError(qBindExpectedBytes); return false; } }
bool _bind ( QByteArray& s) { if (io->isByteArray()) {
s.clear();
auto r = io->readByteArray();
while (r.status == QCborStreamReader::Ok) {
s.append(r.data);
r = io->readByteArray();
}
if (r.status == QCborStreamReader::Error) {
if (!s.isEmpty()) {
reportError(qBindIgnoredBytes);
s.clear();
}
return false;
}
return true;
} else { reportError(qBindExpectedBytes); return false; } }
bool _bind ( bool& b) { if (io->isBool()) {
b=io->toBool();
return true;
} else { reportError(qBindExpectedBoolean); return false; } }
bool _sequence(quint32* =nullptr) { skipTag(); if (io->isArray () && io->enterContainer()) { return true; } _reportError(qBindExpectedSequence); return false; }
bool _record (quint32* =nullptr) { skipTag(); if (io->isMap () && io->enterContainer()) { return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { skipTag(); if (io->isNull () && io->next ()) { return true; } _reportError(qBindExpectedNull ); return false; }
bool _bind ( QString& s) { skipTag(); if (io->isString()) {
s.clear();
auto r = io->readString();
while (r.status == QCborStreamReader::Ok) {
s.append(r.data);
r = io->readString();
}
if (r.status == QCborStreamReader::Error) {
if (!s.isEmpty()) {
_reportError(qBindIgnoredBytes);
s.clear();
}
return false;
}
return true;
} else { _reportError(qBindExpectedBytes); return false; } }
bool _bind ( QByteArray& s) { skipTag(); if (io->isByteArray()) {
s.clear();
auto r = io->readByteArray();
while (r.status == QCborStreamReader::Ok) {
s.append(r.data);
r = io->readByteArray();
}
if (r.status == QCborStreamReader::Error) {
if (!s.isEmpty()) {
_reportError(qBindIgnoredBytes);
s.clear();
}
return false;
}
return true;
} else { _reportError(qBindExpectedBytes); return false; } }
bool _bind ( bool& b) { skipTag(); if (io->isBool()) {
b=io->toBool();
return io->next();
} else { _reportError(qBindExpectedBoolean); return false; } }
bool _bind ( float& n) { double d; qlonglong i; bool b; auto r=getNumber(d,i,b); if (r) n=float(d); return r; }
bool _bind ( double& n) { double d; qlonglong i; bool b; auto r=getNumber(d,i,b); if (r) n= d ; return r; }
// This dispatch would be more simple with C++17 constexpr if
template<typename T> bool _bind(T& t, typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type* =nullptr) {
double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r && !isNegative) t=i; return r; }
template<typename T> bool _bind(T& t, typename std::enable_if<std::is_integral<T>::value && std:: is_signed<T>::value>::type* =nullptr) {
double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r ) t=i; return r; }
bool _bind ( ) { // TODO use io->next() ?
bool _bind ( qulonglong& t) { double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r) t=i; return r; }
bool _bind ( qlonglong& t) { double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r) t=i; return r; }
bool _bind ( ) { skipTag(); // TODO use io->next() ?
isChoice=true; QString s; QByteArray t; double d; bool b;
((_sequence() && _out())
||_null()
......@@ -281,61 +266,60 @@ protected:
// TODO cache key:value pairs coming out of order wrt to T items() calls
bool _item(QUtf8String& k) { if (!io->hasNext()) {
return false;
}
QString s;
if (!_bind(s)) {
return false;
}
k = s.toUtf8();
return true; }
bool _item( ) { if (!io->hasNext()) {
return false;
}
return true; }
bool _out ( ) { while (io->hasNext()) {
_bind();
}
return io->leaveContainer(); }
}
QString s;
if (!_bind(s)) {
return false;
}
k = s.toUtf8();
return true; }
bool _item( ) { if (!io->hasNext()) {
return false;
}
return true; }
bool _out ( ) { while (io->hasNext()) {
_bind();
}
return io->leaveContainer(); }
bool _isOk() { return io /* TODO && io->lastError()==QCborError::NoError*/; }
void _setChoice(bool b) { isChoice = b; }
void _reportError(const char* e) { if (!isChoice) errors.append(Error{ e, io->currentOffset(), io->lastError() }); }
private:
bool getNumber(double& d, qlonglong&i, bool& isNegative) {
void skipTag() { if (io->isTag()) { io->toTag(); io->next(); } }
bool getNumber(double& d, qlonglong&i, bool& isNegative) { skipTag();
if (io->isFloat()) {
d = double(io->toFloat());
i = qlonglong(d);
return true;
return io->next();
}
if (io->isDouble()) {
d = io->toDouble();
i = qlonglong(d);
return true;
return io->next();
}
if (io->isFloat16()) {
d = double(io->toFloat16());
i = qlonglong(d);
return true;
return io->next();
}
if (io->isNegativeInteger()) {
i = - io->toInteger(); // TODO toNegativeInteger to get full range ?
i = io->toInteger(); // TODO toNegativeInteger to get full range ?
isNegative = true;
return true;
return io->next();
}
if (io->isInteger()) {
i = io->toInteger();
isNegative = false;
return true;
return io->next();
}
reportError(qBindExpectedDecimal); return false;
_reportError(qBindExpectedDecimal); return false;
}
QCborStreamReader* io;
bool isChoice = false;
};
QCborReader::QCborReader(QCborStreamReader* io) : QBaseReader(new QCborReaderImpl(io), true) {}
template<> struct BindSupport<QCborReaderImpl, void> : BindNative {};
template<> struct BindSupport<QCborReaderImpl, QString&> : BindNative {};
template<> struct BindSupport<QCborReaderImpl,QByteArray&> : BindNative {};
template<> struct BindSupport<QCborReaderImpl, bool&> : BindNative {};
template<> struct BindSupport<QCborReaderImpl, float&> : BindNative {};
template<> struct BindSupport<QCborReaderImpl, double&> : BindNative {};
template<typename T> struct BindSupport<QCborReaderImpl,T&,typename std::enable_if<std::is_integral<T>::value>::type> : BindNative {};
// //////////////////////////////////////////////////////////////////////////
// QBind<QCborValue,T> support
......
......@@ -58,7 +58,7 @@ class QDataWriter
, public QBaseWriter<QDataWriter> // allows using QDataStream << for small numeric types and more performance by removing an indirection level
{
protected:
QDataWriter(const QDataWriter &o) : io(o.io) {}
QDataWriter(const QDataWriter &o) : QBaseWriter(), io(o.io) {}
QDataWriter &operator=(const QDataWriter &) = delete;
public:
Q_ENABLE_MOVE(QDataWriter, std::swap(io, o.io); )
......@@ -74,9 +74,9 @@ 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* s) { QByteArray ba = QByteArray::fromRawData(s, int(qstrlen(s))); return _bind(ba); }
template<typename T>
bool _bind ( T& t) { *io << t; return true; } // Use QVariant to explicit the parts that may be unknown to the reader
bool _bind ( const char* s) { QByteArray ba = QByteArray::fromRawData(s, int(qstrlen(s))); return _bind(ba); }
bool _isOk() { return io && io->status()==QDataStream::Ok; }
......@@ -89,12 +89,13 @@ protected:
private:
QDataStream* io;
};
template<> struct BindSupport<QDataWriter,const char*> : BindNative {};
//template<> struct BindSupport<QDataWriter,const char*> : BindNative {};
template<> struct BindSupport<QDataWriter, QString&> : BindNative {};
template<> struct BindSupport<QDataWriter,QByteArray&> : BindNative {};
template<> struct BindSupport<QDataWriter, bool&> : BindNative {};
template<> struct BindSupport<QDataWriter, float&> : BindNative {};
template<> struct BindSupport<QDataWriter, double&> : BindNative {};
// TODO BindNative smallest numeric types
template<> struct BindSupport<QDataWriter, qint32&> : BindNative {};
template<> struct BindSupport<QDataWriter, quint32&> : BindNative {};
template<> struct BindSupport<QDataWriter, qlonglong&> : BindNative {};
......@@ -103,7 +104,6 @@ template<typename T> struct BindSupport<QDataWriter,T&,typename std::enable_if<
QtPrivate::IsAssociativeContainer<typename std::remove_reference<typename std::remove_cv<T>::type>::type>::Value // Not handled correctly by _record()._item(k) which makes the keys implicit
>::type
> : BindNative {};
// TODO BindNative smallest numeric types
// TODO BindNative all types for which T stream operators are defined : QMetaTypeId<typename std::remove_reference<typename std::remove_cv<T>>>::Defined ?
// --------------------------------------------------------------------------
......
......@@ -95,23 +95,11 @@ private:
// --------------------------------------------------------------------------
class QJsonVisitorImpl;
class QJsonVisitor : public QBaseReader<QJsonVisitor, QJsonVisitorImpl>
class QJsonVisitor : public IReader
{
Q_PROTECTED_COPY(QJsonVisitor)
Q_DISABLE_COPY(QJsonVisitor)
public:
Q_ENABLE_MOVE_DEFAULT(QJsonVisitor)
QJsonVisitor(const QJsonValue* v);
private:
friend class QBaseReader<QJsonVisitor, QJsonVisitorImpl>;
friend class QJsonVisitorImpl; // uses method below
QJsonVisitor(QJsonVisitorImpl* outer) : QBaseReader(outer, false) {}
};
class QJsonVisitorImpl
{
Q_DISABLE_COPY(QJsonVisitorImpl)
public:
QJsonVisitorImpl(const QJsonValue* v) : value(v) { Q_ASSERT(v); }
QJsonVisitor(const QJsonValue* v) : json(v) { Q_ASSERT(v); }
struct Error { const char* error; QUtf8String path; template<class T> T bind(Val<T> value) { return value.bind(QUtf8String(error)+' '+path); } };
QVector<Error> errors;
......@@ -119,53 +107,43 @@ public:
QUtf8String currentPath() {
QUtf8String path;
Q_FOREACH(Step s, steps) {
if (s.key) {
path.append('{').append(s.key);
}
else {
char index[std::numeric_limits<int>::digits10+1];
itoa(s.idx, index, 10);
path.append('[').append(index);
}
if (s.key) { path.append('{').append(s.key); }
else { path.append('[').append(QUtf8String::number(s.idx)); }
}
return path;
}
protected:
void setChoice(bool v) { isChoice=v; }
friend class QBaseReader<QJsonVisitor, QJsonVisitorImpl>;
void reportError(const char* error) { if (!isChoice) errors.append(Error{ error, currentPath() }); }
template<class T_> friend class Val; // calls methods below
bool _sequence(quint32* s=nullptr) { Q_UNUSED(s); if (current()->isArray ()) { steps.push(Step()); return true; } reportError(qBindExpectedSequence); return false; }
bool _record (quint32* s=nullptr) { Q_UNUSED(s); if (current()->isObject()) { steps.push(Step()); return true; } reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (current()->isNull ()) { return true; } reportError(qBindExpectedNull ); 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 ( double& v) { if (current()->isDouble()) { v = current()->toDouble(); return true; } reportError(qBindExpectedDecimal ); return false; }
template<typename T>
bool _bind (T& t, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr) { double d; if (_bind(d)) { t=d; return true; } else return false; }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
// Shortcuts
/**/ Val<QReader<IReader>> value ( ) { return QReader<IReader>(this).value ( ); }
/**/ Seq<QReader<IReader>> sequence(quint32* s=nullptr) { return QReader<IReader>(this).sequence ( s); }
template<typename T> QReader<IReader> bind ( T&& t) { return QReader<IReader>(this).bind(std::forward<T>(t)); }
protected:
bool _sequence(quint32* s=nullptr) { Q_UNUSED(s); if (current()->isArray ()) { steps.push(Step()); return true; } _reportError(qBindExpectedSequence); return false; }
bool _record (quint32* s=nullptr) { Q_UNUSED(s); if (current()->isObject()) { steps.push(Step()); return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (current()->isNull ()) { return true; } _reportError(qBindExpectedNull ); return false; }
bool _bind ( const char* u) { if (current()->isString() && current()->toString().toUtf8()==u) return true; _reportError(qBindExpectedLiteral ); 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 ( float& v) { if (current()->isDouble()) { v = current()->toDouble(); return true; } _reportError(qBindExpectedDecimal ); return false; }
bool _bind ( double& v) { if (current()->isDouble()) { v = current()->toDouble(); return true; } _reportError(qBindExpectedDecimal ); return false; }
bool _bind ( qulonglong& t) { double d; if (_bind(d)) { t=d; return true; } else return false; }
bool _bind ( qlonglong& t) { double d; if (_bind(d)) { t=d; return true; } else return false; }
bool _item(QUtf8String& k) { steps.last().key=k; return !(steps.last().item = current(1)->toObject().value(QString::fromUtf8(steps.last().key))).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 json; }
void _setChoice(bool v) { isChoice=v; }
void _reportError(const char* e) { if (!isChoice) errors.append(Error{ e, currentPath() }); }
private:
const QJsonValue* current(unsigned outer=0) const { return unsigned(steps.size())-outer <= 0 ? value : &(steps[unsigned(steps.size())-outer-1].item); }
const QJsonValue* current(unsigned outer=0) const { return unsigned(steps.size())-outer <= 0 ? json : &(steps[unsigned(steps.size())-outer-1].item); }
const QJsonValue* value;
const QJsonValue* json;
struct Step { const char* key=nullptr; int idx=-1; QJsonValue item; Step() = default; };
QStack<Step> steps = QStack<Step>();
bool isChoice = false;
};
QJsonVisitor::QJsonVisitor(const QJsonValue* v) : QBaseReader(new QJsonVisitorImpl(v), true) {}
template<> struct BindSupport<QJsonVisitorImpl, QString&> : BindNative {};
template<> struct BindSupport<QJsonVisitorImpl, bool&> : BindNative {};
template<> struct BindSupport<QJsonVisitorImpl, double&> : BindNative {};
template<typename T> struct BindSupport<QJsonVisitorImpl,T&,typename std::enable_if<std::is_integral<T>::value>::type> : BindNative {};
// --------------------------------------------------------------------------
......@@ -232,55 +210,40 @@ private:
// --------------------------------------------------------------------------
class QJsonReaderImpl;
class QJsonReader : public QBaseReader<QJsonReader, QJsonReaderImpl>
class QJsonReader : public IReader
{
Q_PROTECTED_COPY(QJsonReader)
Q_DISABLE_COPY(QJsonReader)
public:
Q_ENABLE_MOVE_DEFAULT(QJsonReader)
QJsonReader(QIODevice* io);
private:
friend class QBaseReader<QJsonReader, QJsonReaderImpl>;
friend class QJsonReaderImpl; // uses method below
QJsonReader(QJsonReaderImpl* outer) : QBaseReader(outer, false) {}
};
class QJsonReaderImpl
{
Q_DISABLE_COPY(QJsonReaderImpl)
QJsonReader(QIODevice* io) : io(io) { Q_ASSERT(io); }
struct Step { int index; const char* end; Step(int i=-1, const char* e=nullptr) : index(i), end(e) {} };
QIODevice* io;
int line = 0, column = 0, index = -1;
QStack<Step> levels = QStack<Step>(); //!< dynamic context required to implement item() and report meaningful errors
bool isChoice = false;
public:
QJsonReaderImpl(QIODevice* io) : io(io) { Q_ASSERT(io); }
struct Error { const char* error; int line; int column; int index; template<class T> T bind(Val<T> value) { QUtf8String utf8(error); utf8.append(' ').append(QUtf8String::number(line)).append(':').append(QUtf8String::number(column)); return value.bind(utf8.constData()); } };
QVector<Error> errors;
protected:
friend class QScopedChoice<Val<QJsonReader>>;
void setChoice(bool v) { isChoice=v; }
friend class QBaseReader<QJsonReader, QJsonReaderImpl>;
void reportError(const char* error) { if (!isChoice) errors.append(Error{ error, line, column, index }); }
template<class T_> friend class Val; // calls methods below
bool _sequence(quint32* =nullptr) { if (get('[', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"]")); return true; } reportError(qBindExpectedSequence); return false; }
bool _record (quint32* =nullptr) { if (get('{', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"}")); return true; } reportError(qBindExpectedRecord ); return false; }
// Shortcuts
/**/ Val<QReader<IReader>> value ( ) { return QReader<IReader>(this).value ( ); }
/**/ Seq<QReader<IReader>> sequence(quint32* s=nullptr) { return QReader<IReader>(this).sequence ( s); }
template<typename T> QReader<IReader> bind ( T&& t) { return QReader<IReader>(this).bind(std::forward<T>(t)); }
protected:
bool _sequence(quint32* =nullptr) { if (get('[', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"]")); return true; } _reportError(qBindExpectedSequence); return false; }
bool _record (quint32* =nullptr) { if (get('{', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"}")); return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (get('n', "[{\"ntf-0123456789.") &&
get('u', "[{\"" ) &&
get('l', "[{\"" ) &&
get('l', "[{\"" )) {
return true;
} else { reportError(qBindExpectedNull); return false; } }
bool _bind ( QByteArray& s) { // FIXME Use QString instead of QByteArray
if ( get('"', "[{\"ntf-0123456789.")) { s.clear(); char c;
while ((c=getCharInString()) != '\0' ) { s.append(c); }
} else { _reportError(qBindExpectedNull); return false; } }
bool _bind ( const char* l) { QUtf8String u;
if ( get('"', "[{\"ntf-0123456789.")) { u.clear(); char c;
while ((c=getCharInString()) != '\0' ) { u.append(c); }
return get('"') && u==l;
} else { _reportError(qBindExpectedText); return false; } }
bool _bind ( QString& s) { QUtf8String u;
if ( get('"', "[{\"ntf-0123456789.")) { u.clear(); char c;
while ((c=getCharInString()) != '\0' ) { u.append(c); }
s=QString::fromUtf8(u); // TODO Support user-defined QTextCodec
return get('"');
} else { reportError(qBindExpectedText); return false; } }
} else { _reportError(qBindExpectedText); return false; } }
bool _bind ( bool& b) { if (get('t', "[{\"ntf-0123456789.") &&
get('r', "[{\"" ) &&
get('u', "[{\"" ) &&
......@@ -293,28 +256,24 @@ protected:
get('s', "[{\"" ) &&
get('e', "[{\"" )) { b=false;
return true;
} else { reportError(qBindExpectedBoolean); return false; } }
} else { _reportError(qBindExpectedBoolean); return false; } }
bool _bind ( float& n) { double d; qlonglong i; bool b; auto r=getNumber(d,i,b); if (r) n=d; return r; }
bool _bind ( double& n) { double d; qlonglong i; bool b; auto r=getNumber(d,i,b); if (r) n=d; return r; }
// This dispatch would be more simple with C++17 constexpr if
template<typename T> bool _bind(T& t, typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type* =nullptr) {
double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r && !isNegative) t=i; return r; }
template<typename T> bool _bind(T& t, typename std::enable_if<std::is_integral<T>::value && std:: is_signed<T>::value>::type* =nullptr) {
double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r) t=(isNegative?-i:i); return r; }
bool _bind ( ) { isChoice=true; QByteArray t; double d; bool b; // FIXME Use QString instead of QByteArray
( (_sequence() && _out())
|| _null()
|| _bind(b)
|| _bind(d)
|| _bind(t) // including any other?
);
isChoice=false;
return true;
}
bool _bind ( qulonglong& t) { double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r && !isNegative) t=i; return r; }
bool _bind ( qlonglong& t) { double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r) t=(isNegative?-i:i); return r; }
bool _bind ( ) { isChoice=true; QString t; double d; bool b; // FIXME Use QString instead of QByteArray
( (_sequence() && _out())
|| _null()
|| _bind(b)
|| _bind(d)
|| _bind(t) // including any other?
);
isChoice=false;
return true;
}
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
......@@ -325,7 +284,10 @@ protected:
return false;
}
level.index++;
return _bind(k) && get(':',level.end); }
QString s;
if (!_bind(s)) return false;
k=s.toUtf8();
return get(':',level.end); }
bool _item( ) { Step& level = levels.last();
if (-1 < level.index && !get(',',level.end)) {
return false;
......@@ -338,12 +300,15 @@ protected:
}
return get(*level.end, "}"); }
bool _isOk() { return io; }
void _setChoice(bool v) { isChoice=v; }
void _reportError(const char* e) { if (!isChoice) errors.append(Error{ e, line, column, index }); }
private:
bool getNumber(double& d, qlonglong&i, bool& isNegative) {
isNegative = get('-', "[{\"0123456789.");
int digit;
if ((digit = getDigit()) < 0) {
reportError(qBindExpectedDecimal);