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/

QCbor_impl.h 34.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/****************************************************************************
 * **
 * ** Copyright (C) 2017 MinMaxMedical.
 * ** All rights reserved.
 * ** Contact: MinMaxMedical <InCAS@MinMaxMedical.com>
 * **
 * ** This file is part of the modmedLog module.
 * **
 * ** $QT_BEGIN_LICENSE:BSD$
 * ** You may use this file under the terms of the BSD license as follows:
 * **
 * ** "Redistribution and use in source and binary forms, with or without
 * ** modification, are permitted provided that the following conditions are
 * ** met:
 * **   * Redistributions of source code must retain the above copyright
 * **     notice, this list of conditions and the following disclaimer.
 * **   * Redistributions in binary form must reproduce the above copyright
 * **     notice, this list of conditions and the following disclaimer in
 * **     the documentation and/or other materials provided with the
 * **     distribution.
 * **   * Neither the name of MinMaxMedical S.A.S. and its Subsidiary(-ies) nor
 * **     the names of its contributors may be used to endorse or promote
 * **     products derived from this software without specific prior written
 * **     permission.
 * **
 * ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 * ** $QT_END_LICENSE$
 * **
 * ****************************************************************************/
#pragma once

#include <type_traits>
43

44
45
46
#include <QtCore/qiodevice.h>
#include <QtCore/qendian.h>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
47
#include "QValue.h"
48
49
50
51
52
53

namespace cbor {

//! CBOR Major types
//! Encoded in the high 3 bits of the descriptor byte
//! \see http://tools.ietf.org/html/rfc7049#section-2.1
54
55
56
57
58
59
60
61
62
enum MajorType : char {
    UnsignedIntType     =  0,
    NegativeIntType     =  1,
    ByteStringType      =  2,
    TextStringType      =  3,
    ArrayType           =  4,
    MapType             =  5,
    TagType             =  6,
    SimpleValueType     =  7
63
64
65
66
};

//! CBOR simple and floating point types
//! Encoded in the low 5 bits of the descriptor byte when the Major Type is 7.
67
68
69
70
71
enum SimpleValue : char {
//  Unassigned          =  0-19
    False               = 20,
    True                = 21,
    Null                = 22,
72
    Undefined           = 23,
73
74
75
76
77
78
//  NextByteValue       = 24,
//  Next16BitFloat      = 25,
    Next32BitFloat      = 26,
    Next64BitFloat      = 27,
//  Unassigned          = 28-30
    Break               = 31
79
80
};

81
82
83
84
85
86
enum MajorValue : char {
//     SimpleUnsignedInt=  0-23
     NextByteUnsignedInt= 24,
    Next16BitUnsignedInt= 25,
    Next32BitUnsignedInt= 26,
    Next64BitUnsignedInt= 27,
87

88
    IndefiniteLength    = 31, // along with ArrayType or MapType
89
90
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
91
92
93
94
enum {
    Tag          = 192,
    DateTime     = 0,
    CborDocument = 55799,
95
    MimeMessage  = 36,
96
97
98
99
100
};

} // cbor

// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
101
// QTransmogrifier<T,QCbor*> support
102

103
class QCborWriter : public QAbstractValueWriter // TODO Support CBOR tags for QAbstractValue BindNative types and multi-dimensional meta() (e.g. support qmColumns with http://cbor.schmorp.de/stringref, qmSizes with https://datatracker.ietf.org/doc/draft-ietf-cbor-array-tags/?include_text=1 )
104
{
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
105
    Q_DISABLE_COPY(QCborWriter)
106
public:
107
   ~QCborWriter() { while (!levels.empty()) tryOut(); }
108
    QCborWriter(QByteArray* io) : io(io) { Q_ASSERT(io); }
109

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
110
    // Shortcuts
111
112
113
    /**/                 QValue       value   (                  ) { return QCur(this).value(); }
    /**/                 QSequence    sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
    template<typename T> QCur bind    (             T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
114
protected:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
115
    bool isValid() const noexcept { return io; }
116

117
    bool trySequence(quint32* rows=nullptr) { levels.push_back(rows);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
118
119
                                              if (Q_LIKELY(rows)) { putInteger   (*rows                 , cbor::ArrayType); }
                                              else                { putMajorValue(cbor::IndefiniteLength, cbor::ArrayType); } return true; }
120
    bool tryRecord  (quint32* cols=nullptr) { levels.push_back(cols);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
121
122
                                              if (Q_LIKELY(cols)) { putInteger   (*cols                 , cbor::MapType  ); }
                                              else                { putMajorValue(cbor::IndefiniteLength, cbor::MapType  ); } return true; }
123
124
    bool tryAny     (                     ) { putSimpleValue(cbor::Undefined); return true; }
    bool tryNull    (                     ) { putSimpleValue(cbor::Null     ); return true; }
125
126
    bool tryBind    (      QUtf8DataView u) { putInteger    (quint64(u.size()), cbor::TextStringType); io->append(u.data(), u.size()); return true; }
    bool tryBind    (          QString&& s) { QUtf8Data u(s.toUtf8());
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
127
128
129
130
131
132
133
                                              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) &&
                                              //                putInteger    (sizeof(mimeHeader)+
                                              //                               utf16.size()          ,cbor::ByteStringType) && io->write(mimeHeader, sizeof(mimeHeader))
                                              //                                                                            && (Q_LIKELY(io->write(utf16)) || s[0]==QChar('\0')); }
134
135
136
137
    bool tryBind    (       QByteArray&& s) { putInteger    (quint64(s.size()), cbor::ByteStringType); io->append(s); return true; }
    bool tryBind    (             bool&& b) { putSimpleValue(b ? cbor::True : cbor::False);                           return true; }
    bool tryBind    (            float&& n) { putSimpleValue(cbor::Next32BitFloat); putBigEndian(n);                  return true; }
    bool tryBind    (           double&& n) { putSimpleValue(cbor::Next64BitFloat); putBigEndian(n);                  return true; }
138

139
140
    bool tryBind    (       quint64&& t) {            putInteger(quint64(   t), cbor::UnsignedIntType);               return true; }
    bool tryBind    (        qint64&& t) { if (t<0) { putInteger(quint64(-1-t), cbor::NegativeIntType); } // see https://tools.ietf.org/html/rfc7049#section-2.1
141
142
                                         else     { putInteger(quint64(   t), cbor::UnsignedIntType); }             return true; }

143
144
145
146
    bool tryItem(QIdentifier&       n) { putInteger(quint64(n.size()), cbor::TextStringType); io->append(n.utf8()          ); return true; }
    bool tryItem(QIdentifierLiteral n) { putInteger(quint64(n.size()), cbor::TextStringType); io->append(n.utf8(), n.size()); return true; }
    bool tryItem(                    ) { return true; }
    bool tryOut (                    ) { bool definite = levels.back(); levels.pop_back(); if (!definite) putSimpleValue(cbor::Break); return true; }
147
private:
148
149
150
    inline void putMajorValue (cbor:: MajorValue v, cbor::MajorType majorType) { io->append(v|char (            majorType << 5)); }
    inline void putSimpleValue(cbor::SimpleValue v                           ) { io->append(v|char (cbor::SimpleValueType << 5)); }
    inline void putInteger    (          quint64 n, cbor::MajorType majorType)
151
152
    {
        // See https://tools.ietf.org/html/rfc7049#section-2.2 if more performance is needed
153
154
155
156
157
        if (n <    24       ) { putMajorValue(cbor::MajorValue(n)       , majorType)                          ; } else
        if (n <= 0xff       ) { putMajorValue(cbor::NextByteUnsignedInt , majorType); putBigEndian(quint8 (n)); } else
        if (n <= 0xffff     ) { putMajorValue(cbor::Next16BitUnsignedInt, majorType); putBigEndian(quint16(n)); } else
        if (n <= 0xffffffffL) { putMajorValue(cbor::Next32BitUnsignedInt, majorType); putBigEndian(quint32(n)); } else
                              { putMajorValue(cbor::Next64BitUnsignedInt, majorType); putBigEndian(quint64(n)); }
158
    }
159
    template<typename T> inline void putBigEndian(T&& t) {
160
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
161
        io->append(reinterpret_cast<char *>(&t), sizeof(t));
162
163
#else
        using TValue = typename std::remove_reference<T>::type;
164
165
166
167
        if (sizeof(TValue)==1) {                                                                          io->append(                               char(t     )); } else
        if (sizeof(TValue)==2) { union { TValue t; quint16 bytes; } u = { t }; u.bytes = qbswap(u.bytes); io->append(reinterpret_cast<char *>(&u),sizeof(TValue)); } else
        if (sizeof(TValue)==4) { union { TValue t; quint32 bytes; } u = { t }; u.bytes = qbswap(u.bytes); io->append(reinterpret_cast<char *>(&u),sizeof(TValue)); } else
        if (sizeof(TValue)==8) { union { TValue t; quint64 bytes; } u = { t }; u.bytes = qbswap(u.bytes); io->append(reinterpret_cast<char *>(&u),sizeof(TValue)); }
168
#endif
169
    }
170

171
    QByteArray* io;
172
    std::vector<bool> levels; //!< minimal dynamic context to ensure well-formedness in case QCur is abandoned: true indicates Definite levels with prefixed size
173
};
174

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
175
176
// --------------------------------------------------------------------------

177
178
179
#include <QtCore/qcborvalue.h>
#include <QtCore/qcborarray.h>
#include <QtCore/qcbormap.h>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
180
181
#include <QtCore/qstack.h>

182
class QCborBuilder : public QAbstractValueWriter
183
184
185
186
187
188
{
    Q_DISABLE_COPY(QCborBuilder)
public:
    QCborBuilder(QCborValue* v) : cbor(v) { Q_ASSERT(v); }

    // Shortcuts
189
190
191
    /**/                 QValue       value   (                  ) { return QCur(this).value(); }
    /**/                 QSequence    sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
    template<typename T> QCur bind    (             T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
192
protected:
193
194
195
196
    bool trySequence(quint32* =nullptr) { levels.push(Step(                   )); return true; }
    bool tryRecord  (quint32* =nullptr) { levels.push(Step(qBindExpectedItem  )); return true; }
    bool tryAny     (                 ) { set(QCborValue(QCborValue::Undefined)); return true; }
    bool tryNull    (                 ) { set(QCborValue(nullptr              )); return true; }
197

198
    bool tryBind    (  QUtf8DataView u) { set(QCborValue(QString::fromUtf8(u.data()))); return true; }
199

200
201
202
    bool tryBind    (         bool&& b) { set(QCborValue(       b )); return true; }
    bool tryBind    (        float&& d) { set(QCborValue(double(d))); return true; }
    bool tryBind    (       double&& d) { set(QCborValue(       d )); return true; }
203
    bool tryBind    (      quint64&& n) { if (quint64(std::numeric_limits<qint64>::max())<n) { handleError(qBindExpectedSmallerNumber); return false; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
204
                                          set(QCborValue(qint64(n))); return true; }
205
206
    bool tryBind    (       qint64&& n) { set(QCborValue(       n )); return true; }
    bool tryBind    (   QByteArray&& s) { set(QCborValue(       s )); return true; }
207

208
209
210
    bool tryItem(QIdentifier& n) { levels.last().key=n            ; return true; }
    bool tryItem(              ) { levels.last().key=QIdentifier(); return true; }
    bool tryOut (              ) { auto level = levels.pop(); set(!level.key.isNull() ? QCborValue(level.object) : QCborValue(level.array)); return true; }
211
212
213
214
215
216
217
private:
    void set(const QCborValue& v) {
        if (levels.isEmpty()) {
            *cbor = v;
        }
        else {
            if (!levels.last().key.isNull())
218
                levels.last().object[levels.last().key.latin1()]=v;
219
220
221
222
223
224
            else
                levels.last().array.append(v);
        }
    }

    QCborValue* cbor;
225
    struct Step { QIdentifier key; /* TODO union */ QCborMap object; QCborArray array; Step(QIdentifier k=QIdentifier()) : key(k) {} };
226
227
228
229
230
    QStack<Step> levels = QStack<Step>(); //!< minimal dynamic context to implement out() and ensure actual building in case QCborBuilder is abandoned
};

// --------------------------------------------------------------------------

231
class QCborVisitor : public QAbstractValueReader
232
233
234
235
{
    Q_DISABLE_COPY(QCborVisitor)
public:
    QCborVisitor(QCborValue* v) : cbor(v) { Q_ASSERT(v); }
236
    void reset(QCborValue* v) { cbor=v; Q_ASSERT(v); steps.resize(0); }
237

238
    QAsciiData currentPath() {
239
        QByteArray path;
240
        Q_FOREACH(Step s, steps) {
241
242
            if (!s.key.isNull()) { path.append('{').append(                   s.key.utf8() ); }
            else                 { path.append('[').append(QByteArray::number(s.idx       )); }
243
        }
244
        return QAsciiData(path);
245
246
247
    }

    // Shortcuts
248
249
250
    /**/                 QValue       value   (                  ) { return QCur(this).value(); }
    /**/                 QSequence    sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
    template<typename T> QCur bind    (             T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
251
protected:
252
253
254
255
    bool trySequence(quint32* =nullptr) {            if (current().isArray    ()) {          steps.push(Step()); return true; } handleError(qBindExpectedSequence); return false; }
    bool tryRecord  (quint32* =nullptr) {            if (current().isMap      ()) {          steps.push(Step()); return true; } handleError(qBindExpectedRecord  ); return false; }
    bool tryNull    (                 ) {            if (current().isNull     ()) {                              return true; } handleError(qBindExpectedNull    ); return false; }
    bool tryBind    (     QUtf8Data& u) { QString s; if (tryBind(s)             ) { u = s        .toUtf8     (); return true; }                                     return false; }
256
257
258
    bool tryBind    (       QString& v) {            if (current().isString   ()) { v = current().toString   (); return true; } handleError(qBindExpectedText    ); return false; }
    bool tryBind    (          bool& v) {            if (current().isBool     ()) { v = current().toBool     (); return true; } handleError(qBindExpectedBoolean ); return false; }
    bool tryBind    (        qint64& t) {            if (current().isInteger  ()) { t = current().toInteger  (); return true; } handleError(qBindExpectedInteger ); return false; }
259
260
    bool tryBind    (       quint64& t) {  qint64 i; if (tryBind(i)             ) { t = quint64(i)             ; return true; }                                     return false; }
    bool tryBind    (         float& v) {  double d; if (tryBind(d)             ) { v = float(d)               ; return true; }                                     return false; }
261
262
    bool tryBind    (        double& v) {            if (current().isDouble   ()) { v = current().toDouble   (); return true; } handleError(qBindExpectedDecimal ); return false; }
    bool tryBind    (    QByteArray& v) { QString s; if (current().isByteArray()) { v = current().toByteArray(); return true; } handleError(qBindExpectedBytes   ); return false; }
263
264
265
266

    bool tryItem(QIdentifierLiteral u) { steps.last().key=u; return !(steps.last().item = current(1).toMap().value(steps.last().key.latin1())).isUndefined(); }
    bool tryItem(QIdentifier&       k) { steps.last().key=k; return !(steps.last().item = current(1).toMap().value(steps.last().key.latin1())).isUndefined(); }
    bool tryItem(                    ) { steps.last().idx++; return !(steps.last().item = current(1).toArray(). at(steps.last().idx         )).isUndefined(); }
267
268
269
270
271
    bool tryOut (                    ) { steps.pop()       ; return true; }

    bool tryAny() { return true; }
    bool isValid() const noexcept { return cbor; }
    bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
272
private:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
273
    const QCborValue& current(int outer=0) const { Q_ASSERT(0<=outer); return steps.size()-outer <= 0 ? *cbor : steps[steps.size()-outer-1].item; }
274
275

    QCborValue* cbor;
276
    struct Step { QIdentifier key; int idx=-1; QCborValue item; Step() = default; };
277
278
279
280
281
282
283
284
    QStack<Step> steps = QStack<Step>();
};

// --------------------------------------------------------------------------

#include <QtCore/qcborstream.h>
#include <QtCore/qpair.h>

285
class QCborReader : public QAbstractValueReader, public QCborStreamReader
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
286
{
287
    Q_DISABLE_COPY(QCborReader)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
288
public:
289
    QCborReader(QIODevice* io) : QCborStreamReader(io), cacheVisitor(&cachedValue) { Q_ASSERT(io); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
290

291
    // Shortcuts
292
293
294
    /**/                 QValue        value   (                  ) { return QCur(this).value(); }
    /**/                 QSequence     sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
    template<typename T> QCur  bind    (             T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
295
protected:
296
    bool trySequence(quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->trySequence(s); }
297
                                           skipTag(); if (isArray () && enterContainer()) { levels.push(Level()); return true; } handleError(qBindExpectedSequence); return false; }
298
    bool tryRecord  (quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->tryRecord(s); }
299
                                           skipTag(); if (isMap   () && enterContainer()) { levels.push(Level()); return true; } handleError(qBindExpectedRecord  ); return false; }
300
301
    bool tryAny     (                  ) { if (caching) { return caching->tryAny() && cacheOut(); }
                                           skipTag(); return next(); }
302
    bool tryNull    (                  ) { if (caching) { return caching->tryNull() && cacheOut(); }
303
                                           skipTag(); if (isNull() && next()) { return true; } handleError(qBindExpectedNull); return false; }
304
    bool tryBind    (     QUtf8Data& u) { QString s; if (tryBind(s)) { u = s.toUtf8(); return true; } return false; }
305
    bool tryBind    (       QString& s) { if (caching) { return caching->tryBind(s) && cacheOut(); }
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
                                          skipTag(); if (isString()) {
                                                         s.resize(0);
                                                         auto r = readString();
                                                         while (r.status == QCborStreamReader::Ok) {
                                                             s.append(r.data);
                                                             r = readString();
                                                         }
                                                         if (r.status == QCborStreamReader::Error) {
                                                             if (!s.isEmpty()) {
                                                                 s.resize(0);
                                                             }
                                                             return handleError(qBindIgnoredBytes);
                                                         }
                                                         return true;
                                                     } else { handleError(qBindExpectedText); return false; } }
321
    bool tryBind    (    QByteArray& s) { if (caching) { return caching->tryBind(s) && cacheOut(); }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
                                          skipTag(); if (isByteArray()) {
                                                         s.resize(0);
                                                         auto r = readByteArray();
                                                         while (r.status == QCborStreamReader::Ok) {
                                                             s.append(r.data);
                                                             r = readByteArray();
                                                         }
                                                         if (r.status == QCborStreamReader::Error) {
                                                             if (!s.isEmpty()) {
                                                                 s.resize(0);
                                                             }
                                                             return handleError(qBindIgnoredBytes);
                                                         }
                                                         return true;
                                                     } else { handleError(qBindExpectedBytes); return false; } }
337
    bool tryBind    (          bool& b) { if (caching) { return caching->tryBind(b) && cacheOut(); }
338
339
340
341
                                          skipTag(); if (isBool()) {
                                                         b=toBool();
                                                         return next();
                                                     } else { handleError(qBindExpectedBoolean); return false; } }
342
    bool tryBind    (        double& n) { if (caching) { return caching->tryBind(n) && cacheOut(); }
343
                                          return getNumber(n); }
344
    bool tryBind    (         float& n) { double d;
345
346
347
348
                                          if (!tryBind(d)) { return false; }
                                          if (d<double(std::numeric_limits<float>::min()) ||
                                                double(std::numeric_limits<float>::max()) <d) { handleError(qBindExpectedSmallerNumber); return false; }
                                          n=float(d); return true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
349

350
    bool tryBind    (       quint64& t) { if (caching) { return caching->tryBind(t) && cacheOut(); }
351
352
353
354
                                          quint64 i; bool neg;
                                          if (!getInteger(i,neg)) { handleError(qBindExpectedInteger        ); return false; }
                                          if (              neg ) { handleError(qBindExpectedPositiveInteger); return false; }
                                          t=i; return true; }
355
    bool tryBind    (        qint64& t) { if (caching) { return caching->tryBind(t) && cacheOut(); }
356
357
                                          quint64 i; bool neg;
                                          if (!getInteger(i,neg)) { handleError(qBindExpectedInteger        ); return false; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
358
359
                                          if (              neg ) { if (-quint64(std::numeric_limits<qint64>::max())-1<i) { handleError(qBindExpectedSmallerNumber); return false; } t=-qint64(i); return true; }
                                          else                    { if ( quint64(std::numeric_limits<qint64>::max())  <i) { handleError(qBindExpectedSmallerNumber); return false; } t= qint64(i); return true; }
360
                                          }
361

362
    bool tryItem(       QIdentifierLiteral u) { if (caching) { return caching->tryItem(u); }
363
                                 QIdentifier s;
364
                                 while (true) {
365
                                     if (levels.last().cachedItems.          contains(QIdentifier(u))) { // must be checked before we consume tryItem(s)
366
                                         cachedValue = levels.last().cachedItems.take(QIdentifier(u));
367
368
369
370
371
372
373
374
375
376
                                         Q_ASSERT(!cacheLevel);
                                         if (!device()->isSequential()) {
                                             cachingAfter = device()->pos();
                                             device()->seek(cachedValue.toInteger());
                                             if (!cacheReader) {
                                                 cacheReader.reset(new QCborReader(device()));
                                             }
                                             else {
                                                cacheReader->setDevice(device());
                                             }
377
                                             caching = cacheReader.get(); // let outer QCur drive QCborReader depending on bound T
378
379
380
                                         }
                                         else {
                                             cacheVisitor.reset(&cachedValue);
381
                                             caching = &cacheVisitor; // let outer QCur drive QCborReader depending on bound T
382
383
384
                                         }
                                         return true;
                                     }
385
                                     else if (!tryItem(s)) { // record() end reached
386
387
                                         return false;
                                     }
388
                                     else if (u  ==  s ) {
389
390
391
392
393
394
395
396
397
398
                                         return true ;
                                     }
                                     else {
                                         if (!device()->isSequential()) {
                                             levels.last().cachedItems.insert(s, QCborValue(device()->pos()));
                                             next();
                                         }
                                         else {
                                             levels.last().cachedItems.insert(s, QCborValue::fromCbor(*this));
                                         }
399
                                         continue; // until !tryItem(s) or u==s
400
401
                                     }
                                 }}
402
    bool tryItem(QIdentifier& k) { if (caching) { return caching->tryItem(k); }
403
                                 if (!hasNext()) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
404
                                    return false;
405
406
                                 }
                                 QString s;
407
                                 if (!tryBind(s) || s.isEmpty()) {
408
409
                                     return false;
                                 }
410
                                 k = QIdentifier(s.toLatin1());
411
                                 return true; }
412
    bool tryItem(              ) { if (caching) { return caching->tryItem(); }
413
                                 if (!hasNext()) {
414
415
416
                                     return false;
                                 }
                                 return true; }
417
418
419
420
421
    bool tryOut (              ) { if (caching) { bool out = caching->tryOut(); if (out) { --cacheLevel; cacheOut(); } return out; }
                                 levels.pop();
                                 while (hasNext()) { tryAny(); }
                                 return leaveContainer(); }

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
422
    bool isValid() const noexcept { return const_cast<QCborReader*>(this)->lastError()==QCborError::NoError; }
423
    bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString("at index:%1 ").arg(currentOffset()).append(context)) : false; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
424
private:
425
    void skipTag() { if (isTag()) next(); }
426
    bool getNumber(double& d) { skipTag();
427
428
429
        if (isFloat16()) { d = double(toFloat16()); return next(); }
        if (isFloat  ()) { d = double(toFloat  ()); return next(); }
        if (isDouble ()) { d =        toDouble () ; return next(); }
430
        handleError(qBindExpectedDecimal); return false;
431
    }
432
433
434
    bool getInteger(quint64&i, bool& isNegative) { skipTag();
        if (isNegativeInteger()) { i = quint64(toNegativeInteger()); isNegative = true ; return next(); }
        if (isUnsignedInteger()) { i =         toUnsignedInteger() ; isNegative = false; return next(); }
435
        handleError(qBindExpectedInteger); return false;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
436
    }
437

438
439
    bool cacheOut() { if (!cacheLevel) {
                          caching = nullptr;
440
441
442
443
444
445
446
                          if (!device()->isSequential()) {
                              device()->seek(cachingAfter);
                          }
                      }
                      return true; }

    // Read/Write caching of out-of-order item keys
447
    struct Level { QMap<QIdentifier,QCborValue> cachedItems; };
448
    QStack<Level> levels = QStack<Level>(); //!< dynamic context required to cache out-of-order item(QIdentifierLiteral)
449
450
451
    quint8        cacheLevel  = 0;
    QCborValue    cachedValue    ; //!< Only used AFTER --cacheLevel==1
    qint64        cachingAfter= 0; //!< Only used AFTER --cacheLevel==1
452
    QAbstractValue*        caching = nullptr; //!< Only used when cacheLevel>=1 && key!=expectedKey
453
454
    QCborVisitor  cacheVisitor; //!< Only used as caching
    QScopedPointer<QCborReader> cacheReader; //!< Only used as caching
455
456
457
};

// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
458
// QTransmogrifier<QCbor*,_> support
459
460
461
462
463

#include <QtCore/qjsonvalue.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonobject.h>

464
template<>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
465
struct QTransmogrifier<QCborValue> {
466
    static QValueEnd zap(QValue&& v, QCborValue&& j) {
467
468
469
470
471
472
473
        if (v->mode()==Write) {
            if (j.isMap      ()) return v.bind(j.toMap      ());
            if (j.isArray    ()) return v.bind(j.toArray    ());
            if (j.isBool     ()) return v.bind(j.toBool     ());
            if (j.isInteger  ()) return v.bind(j.toInteger  ());
            if (j.isDouble   ()) return v.bind(j.toDouble   ());
            if (j.isString   ()) return v.bind(j.toString   ());
474
475
476
477
478
            if (j.isByteArray()) return v.bind(j.toByteArray());
            if (j.isNull     ()) return v.null();
            return (j.isUndefined() || v->handleError(qBindUnexpectedValue)) ? v.any() : QValueEnd();
        }
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
479
    }
480
    static QValueEnd zap(QValue&& v, QCborValue& j) {
481
        if (v->mode()==Write) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
482
            return zap(std::move(v),std::move(j));
483
484
485
486
487
        }
        else if (v->mode()==Read) {
            QValueEnd r;
            auto suspended = v->setErrorHandler();
            QCborArray  a; if ((r = v.bind(a))) { j =                   a  ; v->setErrorHandler(suspended); return r; }
488
489
490
491
            QCborMap    o; if ((r = v.bind(o))) { j =                   o  ; v->setErrorHandler(suspended); return r; }
            QString     t; if ((r = v.bind(t))) { j = QCborValue(       t ); v->setErrorHandler(suspended); return r; }
            bool        b; if ((r = v.bind(b))) { j = QCborValue(       b ); v->setErrorHandler(suspended); return r; }
            qint64      i; if ((r = v.bind(i))) { j = QCborValue(       i ); v->setErrorHandler(suspended); return r; }
492
            quint64     u; if ((r = v.bind(u))) { Q_ASSERT(quint64(std::numeric_limits<qint64>::max())<u); // or tryBind<qint64>() would have succeeded
493
494
495
                                                  j = QCborValue(double(u)); v->setErrorHandler(suspended); return r; }
            double      d; if ((r = v.bind(d))) { j = QCborValue(       d ); v->setErrorHandler(suspended); return r; }
            QByteArray  y; if ((r = v.bind(y))) { j = QCborValue(       y ); v->setErrorHandler(suspended); return r; }
496
497
498
499
500
            /**/           if ((r = v.null( ))) { j = QCborValue( nullptr ); v->setErrorHandler(suspended); return r; }
            v->setErrorHandler(suspended);
            if ((r = v.any())) { j = QCborValue(); }
            return r;
        }
501
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
502
503
504
    }
};

505
template<>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
506
struct QTransmogrifier<QCborArray> {
507
    static QValueEnd zap(QValue&& v, QCborArray&& j) {
508
509
510
511
512
513
514
515
        if (v->mode()==Write) {
            quint32 size=quint32(j.size());
            auto s(v.sequence(&size));
            for (QCborValue item : j) {
                s = s.bind(item);
            }
            return s;
        }
516
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
517
    }
518
    static QValueEnd zap(QValue&& v, QCborArray& j) {
519
        if (v->mode()==Write) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
520
            return zap(std::move(v),std::move(j));
521
522
523
        }
        else if (v->mode()==Read) {
            auto s(v.sequence());
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
524
            QVal<QSequence> i;
525
526
527
528
529
530
531
            while ((i = s.item())) {
                QCborValue json;
                if ((s = i.bind(json)))
                    j.append(json);
            }
            return s;
        }
532
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
533
534
535
    }
};

536
template<>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
537
struct QTransmogrifier<QCborMap> {
538
    static QValueEnd zap(QValue&& v, QCborMap&& j) {
539
540
        if (v->mode()==Write) {
            quint32 size=quint32(j.size());
541
            auto r(v.record(&size));
542
            for (QCborValue key : j.keys()) {
543
544
545
546
547
                auto k = key.toString();
                if (!k.isEmpty()) {
                    auto id = QIdentifier(k.toLatin1());
                    r = r.item(id).bind(QCborValue(j[key]));
                }
548
            }
549
            return r;
550
        }
551
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
552
    }
553
    static QValueEnd zap(QValue&& v, QCborMap& j) {
554
        if (v->mode()==Write) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
555
            return zap(std::move(v),std::move(j));
556
557
558
        }
        else if (v->mode()==Read) {
            auto s(v.record());
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
559
            QIdentifier k; QVal<QRecord> i;
560
561
562
            while ((i = s.item(k))) {
                QCborValue json;
                if ((s = i.bind(json)))
563
                    j.insert(k.latin1(),json);
564
565
566
            }
            return s;
        }
567
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
568
569
    }
};