1 | // Copyright (C) 2020 Intel Corporation. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qcborstreamreader.h" |
5 | |
6 | #define CBOR_NO_ENCODER_API |
7 | #include <private/qcborcommon_p.h> |
8 | |
9 | #include <private/qnumeric_p.h> |
10 | #include <private/qstringconverter_p.h> |
11 | #include <qiodevice.h> |
12 | #include <qdebug.h> |
13 | #include <qstack.h> |
14 | #include <qvarlengtharray.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | static bool qt_cbor_decoder_can_read(void *token, size_t len); |
19 | static void qt_cbor_decoder_advance(void *token, size_t len); |
20 | static void *qt_cbor_decoder_read(void *token, void *userptr, size_t offset, size_t len); |
21 | static CborError qt_cbor_decoder_transfer_string(void *token, const void **userptr, size_t offset, size_t len); |
22 | |
23 | #define CBOR_PARSER_READER_CONTROL 1 |
24 | #define CBOR_PARSER_CAN_READ_BYTES_FUNCTION qt_cbor_decoder_can_read |
25 | #define CBOR_PARSER_ADVANCE_BYTES_FUNCTION qt_cbor_decoder_advance |
26 | #define CBOR_PARSER_TRANSFER_STRING_FUNCTION qt_cbor_decoder_transfer_string |
27 | #define CBOR_PARSER_READ_BYTES_FUNCTION qt_cbor_decoder_read |
28 | |
29 | QT_WARNING_PUSH |
30 | QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) |
31 | QT_WARNING_DISABLE_GCC("-Wimplicit-fallthrough") |
32 | |
33 | #include <cborparser.c> |
34 | |
35 | QT_WARNING_POP |
36 | |
37 | static CborError _cbor_value_dup_string(const CborValue *, void **, size_t *, CborValue *) |
38 | { |
39 | Q_UNREACHABLE_RETURN(CborErrorInternalError); |
40 | } |
41 | [[maybe_unused]] static CborError cbor_value_get_half_float_as_float(const CborValue *, float *) |
42 | { |
43 | Q_UNREACHABLE_RETURN(CborErrorInternalError); |
44 | } |
45 | |
46 | // confirm our constants match TinyCBOR's |
47 | static_assert(int(QCborStreamReader::UnsignedInteger) == CborIntegerType); |
48 | static_assert(int(QCborStreamReader::ByteString) == CborByteStringType); |
49 | static_assert(int(QCborStreamReader::TextString) == CborTextStringType); |
50 | static_assert(int(QCborStreamReader::Array) == CborArrayType); |
51 | static_assert(int(QCborStreamReader::Map) == CborMapType); |
52 | static_assert(int(QCborStreamReader::Tag) == CborTagType); |
53 | static_assert(int(QCborStreamReader::SimpleType) == CborSimpleType); |
54 | static_assert(int(QCborStreamReader::HalfFloat) == CborHalfFloatType); |
55 | static_assert(int(QCborStreamReader::Float) == CborFloatType); |
56 | static_assert(int(QCborStreamReader::Double) == CborDoubleType); |
57 | static_assert(int(QCborStreamReader::Invalid) == CborInvalidType); |
58 | |
59 | /*! |
60 | \class QCborStreamReader |
61 | \inmodule QtCore |
62 | \ingroup cbor |
63 | \ingroup qtserialization |
64 | \reentrant |
65 | \since 5.12 |
66 | |
67 | \brief The QCborStreamReader class is a simple CBOR stream decoder, operating |
68 | on either a QByteArray or QIODevice. |
69 | |
70 | This class can be used to decode a stream of CBOR content directly from |
71 | either a QByteArray or a QIODevice. CBOR is the Concise Binary Object |
72 | Representation, a very compact form of binary data encoding that is |
73 | compatible with JSON. It was created by the IETF Constrained RESTful |
74 | Environments (CoRE) WG, which has used it in many new RFCs. It is meant to |
75 | be used alongside the \l{RFC 7252}{CoAP |
76 | protocol}. |
77 | |
78 | QCborStreamReader provides a StAX-like API, similar to that of |
79 | \l{QXmlStreamReader}. Using it requires a bit of knowledge of CBOR encoding. |
80 | For a simpler API, see \l{QCborValue} and especially the decoding function |
81 | QCborValue::fromCbor(). |
82 | |
83 | Typically, one creates a QCborStreamReader by passing the source QByteArray |
84 | or QIODevice as a parameter to the constructor, then pop elements off the |
85 | stream if there were no errors in decoding. There are three kinds of CBOR |
86 | types: |
87 | |
88 | \table |
89 | \header \li Kind \li Types \li Behavior |
90 | \row \li Fixed-width \li Integers, Tags, Simple types, Floating point |
91 | \li Value is pre-parsed by QCborStreamReader, so accessor functions |
92 | are \c const. Must call next() to advance. |
93 | \row \li Strings \li Byte arrays, Text strings |
94 | \li Length (if known) is pre-parsed, but the string itself is not. |
95 | The accessor functions are not const and may allocate memory. |
96 | Once called, the accessor functions automatically advance to |
97 | the next element. |
98 | \row \li Containers \li Arrays, Maps |
99 | \li Length (if known) is pre-parsed. To access the elements, you |
100 | must call enterContainer(), read all elements, then call |
101 | leaveContainer(). That function advances to the next element. |
102 | \endtable |
103 | |
104 | So a processor function typically looks like this: |
105 | |
106 | \snippet code/src_corelib_serialization_qcborstream.cpp 24 |
107 | |
108 | \section1 CBOR support |
109 | |
110 | The following table lists the CBOR features that QCborStreamReader supports. |
111 | |
112 | \table |
113 | \header \li Feature \li Support |
114 | \row \li Unsigned numbers \li Yes (full range) |
115 | \row \li Negative numbers \li Yes (full range) |
116 | \row \li Byte strings \li Yes |
117 | \row \li Text strings \li Yes |
118 | \row \li Chunked strings \li Yes |
119 | \row \li Tags \li Yes (arbitrary) |
120 | \row \li Booleans \li Yes |
121 | \row \li Null \li Yes |
122 | \row \li Undefined \li Yes |
123 | \row \li Arbitrary simple values \li Yes |
124 | \row \li Half-precision float (16-bit) \li Yes |
125 | \row \li Single-precision float (32-bit) \li Yes |
126 | \row \li Double-precision float (64-bit) \li Yes |
127 | \row \li Infinities and NaN floating point \li Yes |
128 | \row \li Determinate-length arrays and maps \li Yes |
129 | \row \li Indeterminate-length arrays and maps \li Yes |
130 | \row \li Map key types other than strings and integers \li Yes (arbitrary) |
131 | \endtable |
132 | |
133 | \section1 Dealing with invalid or incomplete CBOR streams |
134 | |
135 | QCborStreamReader is capable of detecting corrupt input on its own. The |
136 | library it uses has been extensively tested against invalid input of any |
137 | kind and is quite able to report errors. If any is detected, |
138 | QCborStreamReader will set lastError() to a value besides |
139 | QCborError::NoError, indicating which situation was detected. |
140 | |
141 | Most errors detected by QCborStreamReader during normal item parsing are not |
142 | recoverable. The code using QCborStreamReader may opt to handle the data |
143 | that was properly decoded or it can opt to discard the entire data. |
144 | |
145 | The only recoverable error is QCborError::EndOfFile, which indicates that |
146 | more data is required in order to complete the parsing. This situation is |
147 | useful when data is being read from an asynchronous source, such as a pipe |
148 | (QProcess) or a socket (QTcpSocket, QUdpSocket, QNetworkReply, etc.). When |
149 | more data arrives, the surrounding code needs to call either addData(), if |
150 | parsing from a QByteArray, or reparse(), if it is instead reading directly |
151 | a the QIDOevice that now has more data available (see setDevice()). |
152 | |
153 | \sa QCborStreamWriter, QCborValue, QXmlStreamReader, |
154 | {Parsing and displaying CBOR data}, {Serialization Converter}, |
155 | {Saving and Loading a Game} |
156 | */ |
157 | |
158 | /*! |
159 | \enum QCborStreamReader::Type |
160 | |
161 | This enumeration contains all possible CBOR types as decoded by |
162 | QCborStreamReader. CBOR has 7 major types, plus a number of simple types |
163 | carrying no value, and floating point values. |
164 | |
165 | \value UnsignedInteger (Major type 0) Ranges from 0 to 2\sup{64} - 1 |
166 | (18,446,744,073,709,551,616) |
167 | \value NegativeInteger (Major type 1) Ranges from -1 to -2\sup{64} |
168 | (-18,446,744,073,709,551,616) |
169 | \value ByteArray (Major type 2) Arbitrary binary data. |
170 | \value ByteString An alias to ByteArray. |
171 | \value String (Major type 3) Unicode text, possibly containing NULs. |
172 | \value TextString An alias to String |
173 | \value Array (Major type 4) Array of heterogeneous items. |
174 | \value Map (Major type 5) Map/dictionary of heterogeneous items. |
175 | \value Tag (Major type 6) Numbers giving further semantic value |
176 | to generic CBOR items. See \l QCborTag for more information. |
177 | \value SimpleType (Major type 7) Types carrying no further value. Includes |
178 | booleans (true and false), null, undefined. |
179 | \value Float16 IEEE 754 half-precision floating point (\c qfloat16). |
180 | \value HalfFloat An alias to Float16. |
181 | \value Float IEEE 754 single-precision floating point (\tt float). |
182 | \value Double IEEE 754 double-precision floating point (\tt double). |
183 | \value Invalid Not a valid type, either due to parsing error or due to |
184 | reaching the end of an array or map. |
185 | */ |
186 | |
187 | /*! |
188 | \enum QCborStreamReader::StringResultCode |
189 | |
190 | This enum is returned by readString() and readByteArray() and is used to |
191 | indicate what the status of the parsing is. |
192 | |
193 | \value EndOfString The parsing for the string is complete, with no error. |
194 | \value Ok The function returned data; there was no error. |
195 | \value Error Parsing failed with an error. |
196 | */ |
197 | |
198 | /*! |
199 | \class QCborStreamReader::StringResult |
200 | \inmodule QtCore |
201 | |
202 | This class is returned by readString() and readByteArray(), with either the |
203 | contents of the string that was read or an indication that the parsing is |
204 | done or found an error. |
205 | |
206 | The contents of \l data are valid only if \l status is |
207 | \l{StringResultCode}{Ok}. Otherwise, it should be null. |
208 | */ |
209 | |
210 | /*! |
211 | \variable QCborStreamReader::StringResult::data |
212 | |
213 | Contains the actual data from the string if \l status is \c Ok. |
214 | */ |
215 | |
216 | /*! |
217 | \variable QCborStreamReader::StringResult::status |
218 | |
219 | Contains the status of the attempt of reading the string from the stream. |
220 | */ |
221 | |
222 | /*! |
223 | \fn QCborStreamReader::Type QCborStreamReader::type() const |
224 | |
225 | Returns the type of the current element. It is one of the valid types or |
226 | Invalid. |
227 | |
228 | \sa isValid(), isUnsignedInteger(), isNegativeInteger(), isInteger(), |
229 | isByteArray(), isString(), isArray(), isMap(), isTag(), isSimpleType(), |
230 | isBool(), isFalse(), isTrue(), isNull(), isUndefined(), isFloat16(), |
231 | isFloat(), isDouble() |
232 | */ |
233 | |
234 | /*! |
235 | \fn bool QCborStreamReader::isValid() const |
236 | |
237 | Returns true if the current element is valid, false otherwise. The current |
238 | element may be invalid if there was a decoding error or we've just parsed |
239 | the last element in an array or map. |
240 | |
241 | \note This function is not the opposite of isNull(). Null is a normal CBOR |
242 | type that must be handled by the application. |
243 | |
244 | \sa type(), isInvalid() |
245 | */ |
246 | |
247 | /*! |
248 | \fn bool QCborStreamReader::isInvalid() const |
249 | |
250 | Returns true if the current element is invalid, false otherwise. The current |
251 | element may be invalid if there was a decoding error or we've just parsed |
252 | the last element in an array or map. |
253 | |
254 | \note This function is not to be confused with isNull(). Null is a normal |
255 | CBOR type that must be handled by the application. |
256 | |
257 | \sa type(), isValid() |
258 | */ |
259 | |
260 | /*! |
261 | \fn bool QCborStreamReader::isUnsignedInteger() const |
262 | |
263 | Returns true if the type of the current element is an unsigned integer (that |
264 | is if type() returns QCborStreamReader::UnsignedInteger). If this function |
265 | returns true, you may call toUnsignedInteger() or toInteger() to read that value. |
266 | |
267 | \sa type(), toUnsignedInteger(), toInteger(), isInteger(), isNegativeInteger() |
268 | */ |
269 | |
270 | /*! |
271 | \fn bool QCborStreamReader::isNegativeInteger() const |
272 | |
273 | Returns true if the type of the current element is a negative integer (that |
274 | is if type() returns QCborStreamReader::NegativeInteger). If this function |
275 | returns true, you may call toNegativeInteger() or toInteger() to read that value. |
276 | |
277 | \sa type(), toNegativeInteger(), toInteger(), isInteger(), isUnsignedInteger() |
278 | */ |
279 | |
280 | /*! |
281 | \fn bool QCborStreamReader::isInteger() const |
282 | |
283 | Returns true if the type of the current element is either an unsigned |
284 | integer or a negative one (that is, if type() returns |
285 | QCborStreamReader::UnsignedInteger or QCborStreamReader::NegativeInteger). |
286 | If this function returns true, you may call toInteger() to read that |
287 | value. |
288 | |
289 | \sa type(), toInteger(), toUnsignedInteger(), toNegativeInteger(), |
290 | isUnsignedInteger(), isNegativeInteger() |
291 | */ |
292 | |
293 | /*! |
294 | \fn bool QCborStreamReader::isByteArray() const |
295 | |
296 | Returns true if the type of the current element is a byte array (that is, |
297 | if type() returns QCborStreamReader::ByteArray). If this function returns |
298 | true, you may call readByteArray() to read that data. |
299 | |
300 | \sa type(), readByteArray(), isString() |
301 | */ |
302 | |
303 | /*! |
304 | \fn bool QCborStreamReader::isString() const |
305 | |
306 | Returns true if the type of the current element is a text string (that is, |
307 | if type() returns QCborStreamReader::String). If this function returns |
308 | true, you may call readString() to read that data. |
309 | |
310 | \sa type(), readString(), isByteArray() |
311 | */ |
312 | |
313 | /*! |
314 | \fn bool QCborStreamReader::isArray() const |
315 | |
316 | Returns true if the type of the current element is an array (that is, |
317 | if type() returns QCborStreamReader::Array). If this function returns |
318 | true, you may call enterContainer() to begin parsing that container. |
319 | |
320 | When the current element is an array, you may also call isLengthKnown() to |
321 | find out if the array's size is explicit in the CBOR stream. If it is, that |
322 | size can be obtained by calling length(). |
323 | |
324 | The following example pre-allocates a QVariantList given the array's size |
325 | for more efficient decoding: |
326 | |
327 | \snippet code/src_corelib_serialization_qcborstream.cpp 25 |
328 | |
329 | \note The code above does not validate that the length is a sensible value. |
330 | If the input stream reports that the length is 1 billion elements, the above |
331 | function will try to allocate some 16 GB or more of RAM, which can lead to a |
332 | crash. |
333 | |
334 | \sa type(), isMap(), isLengthKnown(), length(), enterContainer(), leaveContainer() |
335 | */ |
336 | |
337 | /*! |
338 | \fn bool QCborStreamReader::isMap() const |
339 | |
340 | Returns true if the type of the current element is a map (that is, if type() |
341 | returns QCborStreamReader::Map). If this function returns true, you may call |
342 | enterContainer() to begin parsing that container. |
343 | |
344 | When the current element is a map, you may also call isLengthKnown() to |
345 | find out if the map's size is explicit in the CBOR stream. If it is, that |
346 | size can be obtained by calling length(). |
347 | |
348 | The following example pre-allocates a QVariantMap given the map's size |
349 | for more efficient decoding: |
350 | |
351 | \snippet code/src_corelib_serialization_qcborstream.cpp 26 |
352 | |
353 | The example above uses a function called \c readElementAsString to read the |
354 | map's keys and obtain a string. That is because CBOR maps may contain any |
355 | type as keys, not just strings. User code needs to either perform this |
356 | conversion, reject non-string keys, or instead use a different container |
357 | besides \l QVariantMap and \l QVariantHash. For example, if the map is |
358 | expected to contain integer keys, which is recommended as it reduces stream |
359 | size and parsing, the correct container would be \c{\l{QMap}<int, QVariant>} |
360 | or \c{\l{QHash}<int, QVariant>}. |
361 | |
362 | \note The code above does not validate that the length is a sensible value. |
363 | If the input stream reports that the length is 1 billion elements, the above |
364 | function will try to allocate some 24 GB or more of RAM, which can lead to a |
365 | crash. |
366 | |
367 | \sa type(), isArray(), isLengthKnown(), length(), enterContainer(), leaveContainer() |
368 | */ |
369 | |
370 | /*! |
371 | \fn bool QCborStreamReader::isTag() const |
372 | |
373 | Returns true if the type of the current element is a CBOR tag (that is, |
374 | if type() returns QCborStreamReader::Tag). If this function returns |
375 | true, you may call toTag() to read that data. |
376 | |
377 | \sa type(), toTag() |
378 | */ |
379 | |
380 | /*! |
381 | \fn bool QCborStreamReader::isFloat16() const |
382 | |
383 | Returns true if the type of the current element is an IEEE 754 |
384 | half-precision floating point (that is, if type() returns |
385 | QCborStreamReader::Float16). If this function returns true, you may call |
386 | toFloat16() to read that data. |
387 | |
388 | \sa type(), toFloat16(), isFloat(), isDouble() |
389 | */ |
390 | |
391 | /*! |
392 | \fn bool QCborStreamReader::isFloat() const |
393 | |
394 | Returns true if the type of the current element is an IEEE 754 |
395 | single-precision floating point (that is, if type() returns |
396 | QCborStreamReader::Float). If this function returns true, you may call |
397 | toFloat() to read that data. |
398 | |
399 | \sa type(), toFloat(), isFloat16(), isDouble() |
400 | */ |
401 | |
402 | /*! |
403 | \fn bool QCborStreamReader::isDouble() const |
404 | |
405 | Returns true if the type of the current element is an IEEE 754 |
406 | double-precision floating point (that is, if type() returns |
407 | QCborStreamReader::Double). If this function returns true, you may call |
408 | toDouble() to read that data. |
409 | |
410 | \sa type(), toDouble(), isFloat16(), isFloat() |
411 | */ |
412 | |
413 | /*! |
414 | \fn bool QCborStreamReader::isSimpleType() const |
415 | |
416 | Returns true if the type of the current element is any CBOR simple type, |
417 | including a boolean value (true and false) as well as null and undefined. To |
418 | find out which simple type this is, call toSimpleType(). Alternatively, to |
419 | test for one specific simple type, call the overload that takes a |
420 | QCborSimpleType parameter. |
421 | |
422 | CBOR simple types are types that do not carry extra value. There are 255 |
423 | possibilities, but there are currently only four values that have defined |
424 | meaning. Code is not expected to cope with unknown simple types and may |
425 | simply discard the stream as invalid if it finds an unknown one. |
426 | |
427 | \sa QCborSimpleType, type(), isSimpleType(QCborSimpleType), toSimpleType() |
428 | */ |
429 | |
430 | /*! |
431 | \fn bool QCborStreamReader::isSimpleType(QCborSimpleType st) const |
432 | |
433 | Returns true if the type of the current element is the simple type \a st, |
434 | false otherwise. If this function returns true, then toSimpleType() will |
435 | return \a st. |
436 | |
437 | CBOR simple types are types that do not carry extra value. There are 255 |
438 | possibilities, but there are currently only four values that have defined |
439 | meaning. Code is not expected to cope with unknown simple types and may |
440 | simply discard the stream as invalid if it finds an unknown one. |
441 | |
442 | \sa QCborSimpleType, type(), isSimpleType(), toSimpleType() |
443 | */ |
444 | |
445 | /*! |
446 | \fn bool QCborStreamReader::isFalse() const |
447 | |
448 | Returns true if the current element is the \c false value, false if it is |
449 | anything else. |
450 | |
451 | \sa type(), isTrue(), isBool(), toBool(), isSimpleType(), toSimpleType() |
452 | */ |
453 | |
454 | /*! |
455 | \fn bool QCborStreamReader::isTrue() const |
456 | |
457 | Returns true if the current element is the \c true value, false if it is |
458 | anything else. |
459 | |
460 | \sa type(), isFalse(), isBool(), toBool(), isSimpleType(), toSimpleType() |
461 | */ |
462 | |
463 | /*! |
464 | \fn bool QCborStreamReader::isBool() const |
465 | |
466 | Returns true if the current element is a boolean value (\c true or \c |
467 | false), false if it is anything else. If this function returns true, you may |
468 | call toBool() to retrieve the value of the boolean. You may also call |
469 | toSimpleType() and compare to either QCborSimpleValue::True or |
470 | QCborSimpleValue::False. |
471 | |
472 | \sa type(), isFalse(), isTrue(), toBool(), isSimpleType(), toSimpleType() |
473 | */ |
474 | |
475 | /*! |
476 | \fn bool QCborStreamReader::isNull() const |
477 | |
478 | Returns true if the current element is the \c null value, false if it is |
479 | anything else. Null values may be used to indicate the absence of some |
480 | optional data. |
481 | |
482 | \note This function is not the opposite of isValid(). A Null value is a |
483 | valid CBOR value. |
484 | |
485 | \sa type(), isSimpleType(), toSimpleType() |
486 | */ |
487 | |
488 | /*! |
489 | \fn bool QCborStreamReader::isUndefined() const |
490 | |
491 | Returns true if the current element is the \c undefined value, false if it |
492 | is anything else. Undefined values may be encoded to indicate that some |
493 | conversion failed or was not possible when creating the stream. |
494 | QCborStreamReader never performs any replacement and this function will only |
495 | return true if the stream contains an explicit undefined value. |
496 | |
497 | \sa type(), isSimpleType(), toSimpleType() |
498 | */ |
499 | |
500 | /*! |
501 | \fn bool QCborStreamReader::isContainer() const |
502 | |
503 | Returns true if the current element is a container (that is, an array or a |
504 | map), false if it is anything else. If the current element is a container, |
505 | the isLengthKnown() function may be used to find out if the container's size |
506 | is explicit in the stream and, if so, length() can be used to get that size. |
507 | |
508 | More importantly, for a container, the enterContainer() function is |
509 | available to begin iterating through the elements contained therein. |
510 | |
511 | \sa type(), isArray(), isMap(), isLengthKnown(), length(), enterContainer(), |
512 | leaveContainer(), containerDepth() |
513 | */ |
514 | |
515 | class QCborStreamReaderPrivate |
516 | { |
517 | public: |
518 | enum { |
519 | // 9 bytes is the maximum size for any integer, floating point or |
520 | // length in CBOR. |
521 | MaxCborIndividualSize = 9, |
522 | IdealIoBufferSize = 256 |
523 | }; |
524 | |
525 | QIODevice *device; |
526 | QByteArray buffer; |
527 | QStack<CborValue> containerStack; |
528 | |
529 | CborParser parser; |
530 | CborValue currentElement; |
531 | QCborError lastError = {}; |
532 | |
533 | QByteArray::size_type bufferStart = 0; |
534 | bool corrupt = false; |
535 | |
536 | QCborStreamReaderPrivate(const QByteArray &data) |
537 | : device(nullptr), buffer(data) |
538 | { |
539 | initDecoder(); |
540 | } |
541 | |
542 | QCborStreamReaderPrivate(QIODevice *device) |
543 | { |
544 | setDevice(device); |
545 | } |
546 | |
547 | ~QCborStreamReaderPrivate() |
548 | { |
549 | } |
550 | |
551 | void setDevice(QIODevice *dev) |
552 | { |
553 | buffer.clear(); |
554 | device = dev; |
555 | initDecoder(); |
556 | } |
557 | |
558 | void initDecoder() |
559 | { |
560 | containerStack.clear(); |
561 | bufferStart = 0; |
562 | if (device) { |
563 | buffer.clear(); |
564 | buffer.reserve(asize: IdealIoBufferSize); // sets the CapacityReserved flag |
565 | } |
566 | |
567 | preread(); |
568 | if (CborError err = cbor_parser_init_reader(ops: nullptr, parser: &parser, it: ¤tElement, token: this)) |
569 | handleError(err); |
570 | else |
571 | lastError = { .c: QCborError::NoError }; |
572 | } |
573 | |
574 | char *bufferPtr() |
575 | { |
576 | Q_ASSERT(buffer.isDetached()); |
577 | return const_cast<char *>(buffer.constData()) + bufferStart; |
578 | } |
579 | |
580 | void preread() |
581 | { |
582 | if (device && buffer.size() - bufferStart < MaxCborIndividualSize) { |
583 | // load more, but only if there's more to be read |
584 | qint64 avail = device->bytesAvailable(); |
585 | Q_ASSERT(avail >= buffer.size()); |
586 | if (avail == buffer.size()) |
587 | return; |
588 | |
589 | if (bufferStart) |
590 | device->skip(maxSize: bufferStart); // skip what we've already parsed |
591 | |
592 | if (buffer.size() != IdealIoBufferSize) |
593 | buffer.resize(size: IdealIoBufferSize); |
594 | |
595 | bufferStart = 0; |
596 | qint64 read = device->peek(data: bufferPtr(), maxlen: IdealIoBufferSize); |
597 | if (read < 0) |
598 | buffer.clear(); |
599 | else if (read != IdealIoBufferSize) |
600 | buffer.truncate(pos: read); |
601 | } |
602 | } |
603 | |
604 | void handleError(CborError err) noexcept |
605 | { |
606 | Q_ASSERT(err); |
607 | |
608 | // is the error fatal? |
609 | if (err != CborErrorUnexpectedEOF) |
610 | corrupt = true; |
611 | |
612 | lastError = QCborError { .c: QCborError::Code(int(err)) }; |
613 | } |
614 | |
615 | struct ReadStringChunk { |
616 | union { |
617 | char *ptr; |
618 | QByteArray *array; |
619 | QString *string; |
620 | }; |
621 | enum Type { ByteArray = -1, String = -3, Utf8String = -5 }; |
622 | qsizetype maxlen_or_type; |
623 | |
624 | ReadStringChunk(char *ptr, qsizetype maxlen) : ptr(ptr), maxlen_or_type(maxlen) {} |
625 | ReadStringChunk(QByteArray *array, Type type = ByteArray) : array(array), maxlen_or_type(type) {} |
626 | ReadStringChunk(QString *str) : string(str), maxlen_or_type(String) {} |
627 | bool isString() const { return maxlen_or_type == String; } |
628 | bool isUtf8String() const { return maxlen_or_type == Utf8String; } |
629 | bool isByteArray() const { return maxlen_or_type == ByteArray; } |
630 | bool isPlainPointer() const { return maxlen_or_type >= 0; } |
631 | }; |
632 | |
633 | static QCborStreamReader::StringResultCode appendStringChunk(QCborStreamReader &reader, QByteArray *data); |
634 | bool readFullString(ReadStringChunk params); |
635 | QCborStreamReader::StringResult<qsizetype> readStringChunk(ReadStringChunk params); |
636 | qsizetype readStringChunk_byte(ReadStringChunk params, qsizetype len); |
637 | qsizetype readStringChunk_unicode(ReadStringChunk params, qsizetype utf8len); |
638 | qsizetype readStringChunk_utf8(ReadStringChunk params, qsizetype utf8len); |
639 | bool ensureStringIteration(); |
640 | }; |
641 | |
642 | void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error) |
643 | { |
644 | d->handleError(err: CborError(error.c)); |
645 | } |
646 | |
647 | static inline bool qt_cbor_decoder_can_read(void *token, size_t len) |
648 | { |
649 | Q_ASSERT(len <= QCborStreamReaderPrivate::MaxCborIndividualSize); |
650 | auto self = static_cast<QCborStreamReaderPrivate *>(token); |
651 | |
652 | qint64 avail = self->buffer.size() - self->bufferStart; |
653 | return len <= quint64(avail); |
654 | } |
655 | |
656 | static void qt_cbor_decoder_advance(void *token, size_t len) |
657 | { |
658 | Q_ASSERT(len <= QCborStreamReaderPrivate::MaxCborIndividualSize); |
659 | auto self = static_cast<QCborStreamReaderPrivate *>(token); |
660 | Q_ASSERT(len <= size_t(self->buffer.size() - self->bufferStart)); |
661 | |
662 | self->bufferStart += int(len); |
663 | self->preread(); |
664 | } |
665 | |
666 | static void *qt_cbor_decoder_read(void *token, void *userptr, size_t offset, size_t len) |
667 | { |
668 | Q_ASSERT(len == 1 || len == 2 || len == 4 || len == 8); |
669 | Q_ASSERT(offset == 0 || offset == 1); |
670 | auto self = static_cast<const QCborStreamReaderPrivate *>(token); |
671 | |
672 | // we must have pre-read the data |
673 | Q_ASSERT(len + offset <= size_t(self->buffer.size() - self->bufferStart)); |
674 | return memcpy(dest: userptr, src: self->buffer.constData() + self->bufferStart + offset, n: len); |
675 | } |
676 | |
677 | static CborError qt_cbor_decoder_transfer_string(void *token, const void **userptr, size_t offset, size_t len) |
678 | { |
679 | auto self = static_cast<QCborStreamReaderPrivate *>(token); |
680 | Q_ASSERT(offset <= size_t(self->buffer.size())); |
681 | static_assert(sizeof(size_t) >= sizeof(QByteArray::size_type)); |
682 | static_assert(sizeof(size_t) == sizeof(qsizetype)); |
683 | |
684 | // check that we will have enough data from the QIODevice before we advance |
685 | // (otherwise, we'd lose the length information) |
686 | qsizetype total; |
687 | if (len > size_t(std::numeric_limits<QByteArray::size_type>::max()) |
688 | || qAddOverflow<qsizetype>(v1: offset, v2: len, r: &total)) |
689 | return CborErrorDataTooLarge; |
690 | |
691 | // our string transfer is just saving the offset to the userptr |
692 | *userptr = reinterpret_cast<void *>(offset); |
693 | |
694 | qint64 avail = (self->device ? self->device->bytesAvailable() : self->buffer.size()) - |
695 | self->bufferStart; |
696 | return total > avail ? CborErrorUnexpectedEOF : CborNoError; |
697 | } |
698 | |
699 | bool QCborStreamReaderPrivate::ensureStringIteration() |
700 | { |
701 | if (currentElement.flags & CborIteratorFlag_IteratingStringChunks) |
702 | return true; |
703 | |
704 | CborError err = cbor_value_begin_string_iteration(value: ¤tElement); |
705 | if (!err) |
706 | return true; |
707 | handleError(err); |
708 | return false; |
709 | } |
710 | |
711 | /*! |
712 | \internal |
713 | */ |
714 | inline void QCborStreamReader::preparse() |
715 | { |
716 | if (lastError() == QCborError::NoError) { |
717 | type_ = cbor_value_get_type(value: &d->currentElement); |
718 | |
719 | if (type_ == CborInvalidType) { |
720 | // We may have reached the end. |
721 | if (d->device && d->containerStack.isEmpty()) { |
722 | d->buffer.clear(); |
723 | if (d->bufferStart) |
724 | d->device->skip(maxSize: d->bufferStart); |
725 | d->bufferStart = 0; |
726 | } |
727 | } else { |
728 | d->lastError = {}; |
729 | // Undo the type mapping that TinyCBOR does (we have an explicit type |
730 | // for negative integer and we don't have separate types for Boolean, |
731 | // Null and Undefined). |
732 | if (type_ == CborBooleanType || type_ == CborNullType || type_ == CborUndefinedType) { |
733 | type_ = CborSimpleType; |
734 | value64 = quint8(d->buffer.at(i: d->bufferStart)) - CborSimpleType; |
735 | } else { |
736 | // Using internal TinyCBOR API! |
737 | value64 = _cbor_value_extract_int64_helper(value: &d->currentElement); |
738 | |
739 | if (cbor_value_is_negative_integer(value: &d->currentElement)) |
740 | type_ = quint8(QCborStreamReader::NegativeInteger); |
741 | } |
742 | } |
743 | } else { |
744 | type_ = Invalid; |
745 | } |
746 | } |
747 | |
748 | /*! |
749 | Creates a QCborStreamReader object with no source data. After construction, |
750 | QCborStreamReader will report an error parsing. |
751 | |
752 | You can add more data by calling addData() or by setting a different source |
753 | device using setDevice(). |
754 | |
755 | \sa addData(), isValid() |
756 | */ |
757 | QCborStreamReader::QCborStreamReader() |
758 | : d(new QCborStreamReaderPrivate({})), type_(Invalid) |
759 | { |
760 | } |
761 | |
762 | /*! |
763 | \overload |
764 | |
765 | Creates a QCborStreamReader object with \a len bytes of data starting at \a |
766 | data. The pointer must remain valid until QCborStreamReader is destroyed. |
767 | */ |
768 | QCborStreamReader::QCborStreamReader(const char *data, qsizetype len) |
769 | : QCborStreamReader(QByteArray::fromRawData(data, size: len)) |
770 | { |
771 | } |
772 | |
773 | /*! |
774 | \overload |
775 | |
776 | Creates a QCborStreamReader object with \a len bytes of data starting at \a |
777 | data. The pointer must remain valid until QCborStreamReader is destroyed. |
778 | */ |
779 | QCborStreamReader::QCborStreamReader(const quint8 *data, qsizetype len) |
780 | : QCborStreamReader(QByteArray::fromRawData(data: reinterpret_cast<const char *>(data), size: len)) |
781 | { |
782 | } |
783 | |
784 | /*! |
785 | \overload |
786 | |
787 | Creates a QCborStreamReader object that will parse the CBOR stream found in |
788 | \a data. |
789 | */ |
790 | QCborStreamReader::QCborStreamReader(const QByteArray &data) |
791 | : d(new QCborStreamReaderPrivate(data)) |
792 | { |
793 | preparse(); |
794 | } |
795 | |
796 | /*! |
797 | \overload |
798 | |
799 | Creates a QCborStreamReader object that will parse the CBOR stream found by |
800 | reading from \a device. QCborStreamReader does not take ownership of \a |
801 | device, so it must remain valid until this object is destroyed. |
802 | */ |
803 | QCborStreamReader::QCborStreamReader(QIODevice *device) |
804 | : d(new QCborStreamReaderPrivate(device)) |
805 | { |
806 | preparse(); |
807 | } |
808 | |
809 | /*! |
810 | Destroys this QCborStreamReader object and frees any associated resources. |
811 | */ |
812 | QCborStreamReader::~QCborStreamReader() |
813 | { |
814 | } |
815 | |
816 | /*! |
817 | Sets the source of data to \a device, resetting the decoder to its initial |
818 | state. |
819 | */ |
820 | void QCborStreamReader::setDevice(QIODevice *device) |
821 | { |
822 | d->setDevice(device); |
823 | preparse(); |
824 | } |
825 | |
826 | /*! |
827 | Returns the QIODevice that was set with either setDevice() or the |
828 | QCborStreamReader constructor. If this object was reading from a QByteArray, |
829 | this function returns nullptr instead. |
830 | */ |
831 | QIODevice *QCborStreamReader::device() const |
832 | { |
833 | return d->device; |
834 | } |
835 | |
836 | /*! |
837 | Adds \a data to the CBOR stream and reparses the current element. This |
838 | function is useful if the end of the data was previously reached while |
839 | processing the stream, but now more data is available. |
840 | */ |
841 | void QCborStreamReader::addData(const QByteArray &data) |
842 | { |
843 | addData(data: data.constData(), len: data.size()); |
844 | } |
845 | |
846 | /*! |
847 | \fn void QCborStreamReader::addData(const quint8 *data, qsizetype len) |
848 | \overload |
849 | |
850 | Adds \a len bytes of data starting at \a data to the CBOR stream and |
851 | reparses the current element. This function is useful if the end of the data |
852 | was previously reached while processing the stream, but now more data is |
853 | available. |
854 | */ |
855 | |
856 | /*! |
857 | \overload |
858 | |
859 | Adds \a len bytes of data starting at \a data to the CBOR stream and |
860 | reparses the current element. This function is useful if the end of the data |
861 | was previously reached while processing the stream, but now more data is |
862 | available. |
863 | */ |
864 | void QCborStreamReader::addData(const char *data, qsizetype len) |
865 | { |
866 | if (!d->device) { |
867 | if (len > 0) |
868 | d->buffer.append(s: data, len); |
869 | reparse(); |
870 | } else { |
871 | qWarning(msg: "QCborStreamReader: addData() with device()"); |
872 | } |
873 | } |
874 | |
875 | /*! |
876 | Reparses the current element. This function must be called when more data |
877 | becomes available in the source QIODevice after parsing failed due to |
878 | reaching the end of the input data before the end of the CBOR stream. |
879 | |
880 | When reading from QByteArray(), the addData() function automatically calls |
881 | this function. Calling it when the reading had not failed is a no-op. |
882 | */ |
883 | void QCborStreamReader::reparse() |
884 | { |
885 | d->lastError = {}; |
886 | d->preread(); |
887 | if (CborError err = cbor_value_reparse(it: &d->currentElement)) |
888 | d->handleError(err); |
889 | else |
890 | preparse(); |
891 | } |
892 | |
893 | /*! |
894 | Clears the decoder state and resets the input source data to an empty byte |
895 | array. After this function is called, QCborStreamReader will be indicating |
896 | an error parsing. |
897 | |
898 | Call addData() to add more data to be parsed. |
899 | |
900 | \sa reset(), setDevice() |
901 | */ |
902 | void QCborStreamReader::clear() |
903 | { |
904 | setDevice(nullptr); |
905 | } |
906 | |
907 | /*! |
908 | Resets the source back to the beginning and clears the decoder state. If the |
909 | source data was a QByteArray, QCborStreamReader will restart from the |
910 | beginning of the array. |
911 | |
912 | If the source data is a QIODevice, this function will call |
913 | QIODevice::reset(), which will seek to byte position 0. If the CBOR stream |
914 | is not found at the beginning of the device (e.g., beginning of a file), |
915 | then this function will likely do the wrong thing. Instead, position the |
916 | QIODevice to the right offset and call setDevice(). |
917 | |
918 | \sa clear(), setDevice() |
919 | */ |
920 | void QCborStreamReader::reset() |
921 | { |
922 | if (d->device) |
923 | d->device->reset(); |
924 | d->lastError = {}; |
925 | d->initDecoder(); |
926 | preparse(); |
927 | } |
928 | |
929 | /*! |
930 | Returns the last error in decoding the stream, if any. If no error |
931 | was encountered, this returns an QCborError::NoError. |
932 | |
933 | \sa isValid() |
934 | */ |
935 | QCborError QCborStreamReader::lastError() const |
936 | { |
937 | return d->lastError; |
938 | } |
939 | |
940 | /*! |
941 | Returns the offset in the input stream of the item currently being decoded. |
942 | The current offset is the number of decoded bytes so far only if the source |
943 | data is a QByteArray or it is a QIODevice that was positioned at its |
944 | beginning when decoding started. |
945 | |
946 | \sa reset(), clear(), device() |
947 | */ |
948 | qint64 QCborStreamReader::currentOffset() const |
949 | { |
950 | return (d->device ? d->device->pos() : 0) + d->bufferStart; |
951 | } |
952 | |
953 | /*! |
954 | Returns the number of containers that this stream has entered with |
955 | enterContainer() but not yet left. |
956 | |
957 | \sa enterContainer(), leaveContainer() |
958 | */ |
959 | int QCborStreamReader::containerDepth() const |
960 | { |
961 | return d->containerStack.size(); |
962 | } |
963 | |
964 | /*! |
965 | Returns either QCborStreamReader::Array or QCborStreamReader::Map, |
966 | indicating whether the container that contains the current item was an array |
967 | or map, respectively. If we're currently parsing the root element, this |
968 | function returns QCborStreamReader::Invalid. |
969 | |
970 | \sa containerDepth(), enterContainer() |
971 | */ |
972 | QCborStreamReader::Type QCborStreamReader::parentContainerType() const |
973 | { |
974 | if (d->containerStack.isEmpty()) |
975 | return Invalid; |
976 | return Type(cbor_value_get_type(value: &std::as_const(t&: d->containerStack).top())); |
977 | } |
978 | |
979 | /*! |
980 | Returns true if there are more items to be decoded in the current container |
981 | or false of we've reached its end. If we're parsing the root element, |
982 | hasNext() returning false indicates the parsing is complete; otherwise, if |
983 | the container depth is non-zero, then the outer code needs to call |
984 | leaveContainer(). |
985 | |
986 | \sa parentContainerType(), containerDepth(), leaveContainer() |
987 | */ |
988 | bool QCborStreamReader::hasNext() const noexcept |
989 | { |
990 | return cbor_value_is_valid(value: &d->currentElement) && |
991 | !cbor_value_at_end(it: &d->currentElement); |
992 | } |
993 | |
994 | /*! |
995 | Advance the CBOR stream decoding one element. You should usually call this |
996 | function when parsing fixed-width basic elements (that is, integers, simple |
997 | values, tags and floating point values). But this function can be called |
998 | when the current item is a string, array or map too and it will skip over |
999 | that entire element, including all contained elements. |
1000 | |
1001 | This function returns true if advancing was successful, false otherwise. It |
1002 | may fail if the stream is corrupt, incomplete or if the nesting level of |
1003 | arrays and maps exceeds \a maxRecursion. Calling this function when |
1004 | hasNext() has returned false is also an error. If this function returns |
1005 | false, lastError() will return the error code detailing what the failure |
1006 | was. |
1007 | |
1008 | \sa lastError(), isValid(), hasNext() |
1009 | */ |
1010 | bool QCborStreamReader::next(int maxRecursion) |
1011 | { |
1012 | if (lastError() != QCborError::NoError) |
1013 | return false; |
1014 | |
1015 | if (!hasNext()) { |
1016 | d->handleError(err: CborErrorAdvancePastEOF); |
1017 | } else if (maxRecursion < 0) { |
1018 | d->handleError(err: CborErrorNestingTooDeep); |
1019 | } else if (isContainer()) { |
1020 | // iterate over each element |
1021 | enterContainer(); |
1022 | while (lastError() == QCborError::NoError && hasNext()) |
1023 | next(maxRecursion: maxRecursion - 1); |
1024 | if (lastError() == QCborError::NoError) |
1025 | leaveContainer(); |
1026 | } else if (isByteArray()) { |
1027 | char c; |
1028 | StringResult<qsizetype> r; |
1029 | do { |
1030 | r = readStringChunk(ptr: &c, maxlen: 1); |
1031 | } while (r.status == Ok); |
1032 | } else if (isString()) { |
1033 | // we need to use actual readString so we get UTF-8 validation |
1034 | StringResult<QString> r; |
1035 | do { |
1036 | r = readString(); |
1037 | } while (r.status == Ok); |
1038 | } else { |
1039 | // fixed types |
1040 | CborError err = cbor_value_advance_fixed(it: &d->currentElement); |
1041 | if (err) |
1042 | d->handleError(err); |
1043 | } |
1044 | |
1045 | preparse(); |
1046 | return d->lastError == QCborError::NoError; |
1047 | } |
1048 | |
1049 | /*! |
1050 | Returns true if the length of the current array, map, byte array or string |
1051 | is known (explicit in the CBOR stream), false otherwise. This function |
1052 | should only be called if the element is one of those. |
1053 | |
1054 | If the length is known, it may be obtained by calling length(). |
1055 | |
1056 | If the length of a map or an array is not known, it is implied by the number |
1057 | of elements present in the stream. QCborStreamReader has no API to calculate |
1058 | the length in that condition. |
1059 | |
1060 | Strings and byte arrays may also have indeterminate length (that is, they |
1061 | may be transmitted in multiple chunks). Those cannot currently be created |
1062 | with QCborStreamWriter, but they could be with other encoders, so |
1063 | QCborStreamReader supports them. |
1064 | |
1065 | \sa length(), QCborStreamWriter::startArray(), QCborStreamWriter::startMap() |
1066 | */ |
1067 | bool QCborStreamReader::isLengthKnown() const noexcept |
1068 | { |
1069 | return cbor_value_is_length_known(value: &d->currentElement); |
1070 | } |
1071 | |
1072 | /*! |
1073 | Returns the length of the string or byte array, or the number of items in an |
1074 | array or the number, of item pairs in a map, if known. This function must |
1075 | not be called if the length is unknown (that is, if isLengthKnown() returned |
1076 | false). It is an error to do that and it will cause QCborStreamReader to |
1077 | stop parsing the input stream. |
1078 | |
1079 | \sa isLengthKnown(), QCborStreamWriter::startArray(), QCborStreamWriter::startMap() |
1080 | */ |
1081 | quint64 QCborStreamReader::length() const |
1082 | { |
1083 | CborError err; |
1084 | switch (type()) { |
1085 | case String: |
1086 | case ByteArray: |
1087 | case Map: |
1088 | case Array: |
1089 | if (isLengthKnown()) |
1090 | return value64; |
1091 | err = CborErrorUnknownLength; |
1092 | break; |
1093 | |
1094 | default: |
1095 | err = CborErrorIllegalType; |
1096 | break; |
1097 | } |
1098 | |
1099 | d->handleError(err); |
1100 | return quint64(-1); |
1101 | } |
1102 | |
1103 | /*! |
1104 | \fn bool QCborStreamReader::enterContainer() |
1105 | |
1106 | Enters the array or map that is the current item and prepares for iterating |
1107 | the elements contained in the container. Returns true if entering the |
1108 | container succeeded, false otherwise (usually, a parsing error). Each call |
1109 | to enterContainer() must be paired with a call to leaveContainer(). |
1110 | |
1111 | This function may only be called if the current item is an array or a map |
1112 | (that is, if isArray(), isMap() or isContainer() is true). Calling it in any |
1113 | other condition is an error. |
1114 | |
1115 | \sa leaveContainer(), isContainer(), isArray(), isMap() |
1116 | */ |
1117 | bool QCborStreamReader::_enterContainer_helper() |
1118 | { |
1119 | d->containerStack.push(t: d->currentElement); |
1120 | CborError err = cbor_value_enter_container(it: &d->containerStack.top(), recursed: &d->currentElement); |
1121 | if (!err) { |
1122 | preparse(); |
1123 | return true; |
1124 | } |
1125 | d->handleError(err); |
1126 | return false; |
1127 | } |
1128 | |
1129 | /*! |
1130 | Leaves the array or map whose items were being processed and positions the |
1131 | decoder at the next item after the end of the container. Returns true if |
1132 | leaving the container succeeded, false otherwise (usually, a parsing error). |
1133 | Each call to enterContainer() must be paired with a call to |
1134 | leaveContainer(). |
1135 | |
1136 | This function may only be called if hasNext() has returned false and |
1137 | containerDepth() is not zero. Calling it in any other condition is an error. |
1138 | |
1139 | \sa enterContainer(), parentContainerType(), containerDepth() |
1140 | */ |
1141 | bool QCborStreamReader::leaveContainer() |
1142 | { |
1143 | if (d->containerStack.isEmpty()) { |
1144 | qWarning(msg: "QCborStreamReader::leaveContainer: trying to leave top-level element"); |
1145 | return false; |
1146 | } |
1147 | if (d->corrupt) |
1148 | return false; |
1149 | |
1150 | CborValue container = d->containerStack.pop(); |
1151 | CborError err = cbor_value_leave_container(it: &container, recursed: &d->currentElement); |
1152 | d->currentElement = container; |
1153 | if (err) { |
1154 | d->handleError(err); |
1155 | return false; |
1156 | } |
1157 | |
1158 | preparse(); |
1159 | return true; |
1160 | } |
1161 | |
1162 | /*! |
1163 | \fn bool QCborStreamReader::toBool() const |
1164 | |
1165 | Returns the boolean value of the current element. |
1166 | |
1167 | This function does not perform any type conversions, including from integer. |
1168 | Therefore, it may only be called if isTrue(), isFalse() or isBool() returned |
1169 | true; calling it in any other condition is an error. |
1170 | |
1171 | \sa isBool(), isTrue(), isFalse(), toInteger() |
1172 | */ |
1173 | |
1174 | /*! |
1175 | \fn QCborTag QCborStreamReader::toTag() const |
1176 | |
1177 | Returns the tag value of the current element. |
1178 | |
1179 | This function does not perform any type conversions, including from integer. |
1180 | Therefore, it may only be called if isTag() is true; calling it in any other |
1181 | condition is an error. |
1182 | |
1183 | Tags are 64-bit numbers attached to generic CBOR types that give them |
1184 | further meaning. For a list of known tags, see the \l QCborKnownTags |
1185 | enumeration. |
1186 | |
1187 | \sa isTag(), toInteger(), QCborKnownTags |
1188 | */ |
1189 | |
1190 | /*! |
1191 | \fn quint64 QCborStreamReader::toUnsignedInteger() const |
1192 | |
1193 | Returns the unsigned integer value of the current element. |
1194 | |
1195 | This function does not perform any type conversions, including from boolean |
1196 | or CBOR tag. Therefore, it may only be called if isUnsignedInteger() is |
1197 | true; calling it in any other condition is an error. |
1198 | |
1199 | This function may be used to obtain numbers beyond the range of the return |
1200 | type of toInteger(). |
1201 | |
1202 | \sa type(), toInteger(), isUnsignedInteger(), isNegativeInteger() |
1203 | */ |
1204 | |
1205 | /*! |
1206 | \fn QCborNegativeValue QCborStreamReader::toNegativeInteger() const |
1207 | |
1208 | Returns the negative integer value of the current element. |
1209 | QCborNegativeValue is a 64-bit unsigned integer containing the absolute |
1210 | value of the negative number that was stored in the CBOR stream. |
1211 | Additionally, QCborNegativeValue(0) represents the number -2\sup{64}. |
1212 | |
1213 | This function does not perform any type conversions, including from boolean |
1214 | or CBOR tag. Therefore, it may only be called if isNegativeInteger() is |
1215 | true; calling it in any other condition is an error. |
1216 | |
1217 | This function may be used to obtain numbers beyond the range of the return |
1218 | type of toInteger(). However, use of negative numbers smaller than -2\sup{63} |
1219 | is extremely discouraged. |
1220 | |
1221 | \sa type(), toInteger(), isNegativeInteger(), isUnsignedInteger() |
1222 | */ |
1223 | |
1224 | /*! |
1225 | \fn qint64 QCborStreamReader::toInteger() const |
1226 | |
1227 | Returns the integer value of the current element, be it negative, positive |
1228 | or zero. If the value is larger than 2\sup{63} - 1 or smaller than |
1229 | -2\sup{63}, the returned value will overflow and will have an incorrect |
1230 | sign. If handling those values is required, use toUnsignedInteger() or |
1231 | toNegativeInteger() instead. |
1232 | |
1233 | This function does not perform any type conversions, including from boolean |
1234 | or CBOR tag. Therefore, it may only be called if isInteger() is true; |
1235 | calling it in any other condition is an error. |
1236 | |
1237 | \sa isInteger(), toUnsignedInteger(), toNegativeInteger() |
1238 | */ |
1239 | |
1240 | /*! |
1241 | \fn QCborSimpleType QCborStreamReader::toSimpleType() const |
1242 | |
1243 | Returns value of the current simple type. |
1244 | |
1245 | This function does not perform any type conversions, including from integer. |
1246 | Therefore, it may only be called if isSimpleType() is true; calling it in |
1247 | any other condition is an error. |
1248 | |
1249 | \sa isSimpleType(), isTrue(), isFalse(), isBool(), isNull(), isUndefined() |
1250 | */ |
1251 | |
1252 | /*! |
1253 | \fn qfloat16 QCborStreamReader::toFloat16() const |
1254 | |
1255 | Returns the 16-bit half-precision floating point value of the current element. |
1256 | |
1257 | This function does not perform any type conversions, including from other |
1258 | floating point types or from integer values. Therefore, it may only be |
1259 | called if isFloat16() is true; calling it in any other condition is an |
1260 | error. |
1261 | |
1262 | \sa isFloat16(), toFloat(), toDouble() |
1263 | */ |
1264 | |
1265 | /*! |
1266 | \fn float QCborStreamReader::toFloat() const |
1267 | |
1268 | Returns the 32-bit single-precision floating point value of the current |
1269 | element. |
1270 | |
1271 | This function does not perform any type conversions, including from other |
1272 | floating point types or from integer values. Therefore, it may only be |
1273 | called if isFloat() is true; calling it in any other condition is an error. |
1274 | |
1275 | \sa isFloat(), toFloat16(), toDouble() |
1276 | */ |
1277 | |
1278 | /*! |
1279 | \fn double QCborStreamReader::toDouble() const |
1280 | |
1281 | Returns the 64-bit double-precision floating point value of the current |
1282 | element. |
1283 | |
1284 | This function does not perform any type conversions, including from other |
1285 | floating point types or from integer values. Therefore, it may only be |
1286 | called if isDouble() is true; calling it in any other condition is an error. |
1287 | |
1288 | \sa isDouble(), toFloat16(), toFloat() |
1289 | */ |
1290 | |
1291 | /*! |
1292 | \fn QCborStreamReader::StringResult<QString> QCborStreamReader::readString() |
1293 | |
1294 | Decodes one string chunk from the CBOR string and returns it. This function |
1295 | is used for both regular and chunked string contents, so the caller must |
1296 | always loop around calling this function, even if isLengthKnown() |
1297 | is true. The typical use of this function is as follows: |
1298 | |
1299 | \snippet code/src_corelib_serialization_qcborstream.cpp 27 |
1300 | |
1301 | The readAllString() function implements the above loop and some extra checks. |
1302 | |
1303 | //! [string-no-type-conversions] |
1304 | This function does not perform any type conversions, including from integers |
1305 | or from byte arrays. Therefore, it may only be called if isString() returned |
1306 | true; calling it in any other condition is an error. |
1307 | //! [string-no-type-conversions] |
1308 | |
1309 | \sa readAllString(), readByteArray(), isString(), readStringChunk() |
1310 | */ |
1311 | QCborStreamReader::StringResult<QString> QCborStreamReader::_readString_helper() |
1312 | { |
1313 | QCborStreamReader::StringResult<QString> result; |
1314 | auto r = d->readStringChunk(params: &result.data); |
1315 | result.status = r.status; |
1316 | if (r.status == Error) { |
1317 | result.data.clear(); |
1318 | } else { |
1319 | Q_ASSERT(r.data == result.data.size()); |
1320 | if (r.status == EndOfString && lastError() == QCborError::NoError) |
1321 | preparse(); |
1322 | } |
1323 | |
1324 | return result; |
1325 | } |
1326 | |
1327 | /*! |
1328 | \fn QCborStreamReader::StringResult<QByteArray> QCborStreamReader::readUtf8String() |
1329 | \since 6.7 |
1330 | |
1331 | Decodes one string chunk from the CBOR string and returns it. This function |
1332 | is used for both regular and chunked string contents, so the caller must |
1333 | always loop around calling this function, even if isLengthKnown() is true. |
1334 | The typical use of this function is as for readString() in the following: |
1335 | |
1336 | \snippet code/src_corelib_serialization_qcborstream.cpp 27 |
1337 | |
1338 | The readAllUtf8String() function implements the above loop and some extra checks. |
1339 | |
1340 | \include qcborstreamreader.cpp string-no-type-conversions |
1341 | |
1342 | \sa readAllString(), readByteArray(), isString(), readStringChunk() |
1343 | */ |
1344 | QCborStreamReader::StringResult<QByteArray> QCborStreamReader::_readUtf8String_helper() |
1345 | { |
1346 | using P = QCborStreamReaderPrivate::ReadStringChunk; |
1347 | QCborStreamReader::StringResult<QByteArray> result; |
1348 | auto r = d->readStringChunk(params: P{ &result.data, P::Utf8String }); |
1349 | result.status = r.status; |
1350 | if (r.status == Error) { |
1351 | result.data.clear(); |
1352 | } else { |
1353 | Q_ASSERT(r.data == result.data.size()); |
1354 | if (r.status == EndOfString && lastError() == QCborError::NoError) |
1355 | preparse(); |
1356 | } |
1357 | |
1358 | return result; |
1359 | } |
1360 | |
1361 | /*! |
1362 | \fn QCborStreamReader::StringResult<QByteArray> QCborStreamReader::readByteArray() |
1363 | |
1364 | Decodes one byte array chunk from the CBOR string and returns it. This |
1365 | function is used for both regular and chunked contents, so the caller must |
1366 | always loop around calling this function, even if isLengthKnown() |
1367 | is true. The typical use of this function is as follows: |
1368 | |
1369 | \snippet code/src_corelib_serialization_qcborstream.cpp 28 |
1370 | |
1371 | The readAllByteArray() function implements the above loop and some extra checks. |
1372 | |
1373 | //! [bytearray-no-type-conversions] |
1374 | This function does not perform any type conversions, including from integers |
1375 | or from strings. Therefore, it may only be called if isByteArray() is true; |
1376 | calling it in any other condition is an error. |
1377 | //! [bytearray-no-type-conversions] |
1378 | |
1379 | \sa readAllByteArray(), readString(), isByteArray(), readStringChunk() |
1380 | */ |
1381 | QCborStreamReader::StringResult<QByteArray> QCborStreamReader::_readByteArray_helper() |
1382 | { |
1383 | QCborStreamReader::StringResult<QByteArray> result; |
1384 | auto r = d->readStringChunk(params: &result.data); |
1385 | result.status = r.status; |
1386 | if (r.status == Error) { |
1387 | result.data.clear(); |
1388 | } else { |
1389 | Q_ASSERT(r.data == result.data.size()); |
1390 | if (r.status == EndOfString && lastError() == QCborError::NoError) |
1391 | preparse(); |
1392 | } |
1393 | |
1394 | return result; |
1395 | } |
1396 | |
1397 | /*! |
1398 | \fn qsizetype QCborStreamReader::currentStringChunkSize() const |
1399 | |
1400 | Returns the size of the current text or byte string chunk. If the CBOR |
1401 | stream contains a non-chunked string (that is, if isLengthKnown() returns |
1402 | \c true), this function returns the size of the entire string, the same as |
1403 | length(). |
1404 | |
1405 | This function is useful to pre-allocate the buffer whose pointer can be passed |
1406 | to readStringChunk() later. |
1407 | |
1408 | \sa readString(), readByteArray(), readStringChunk() |
1409 | */ |
1410 | qsizetype QCborStreamReader::_currentStringChunkSize() const |
1411 | { |
1412 | if (!d->ensureStringIteration()) |
1413 | return -1; |
1414 | |
1415 | size_t len; |
1416 | CborError err = cbor_value_get_string_chunk_size(value: &d->currentElement, len: &len); |
1417 | if (err == CborErrorNoMoreStringChunks) |
1418 | return 0; // not a real error |
1419 | else if (err) |
1420 | d->handleError(err); |
1421 | else if (qsizetype(len) < 0) |
1422 | d->handleError(err: CborErrorDataTooLarge); |
1423 | else |
1424 | return qsizetype(len); |
1425 | return -1; |
1426 | } |
1427 | |
1428 | bool QCborStreamReaderPrivate::readFullString(ReadStringChunk params) |
1429 | { |
1430 | auto r = readStringChunk(params); |
1431 | while (r.status == QCborStreamReader::Ok) { |
1432 | // keep appending |
1433 | r = readStringChunk(params); |
1434 | } |
1435 | |
1436 | bool ok = r.status == QCborStreamReader::EndOfString; |
1437 | Q_ASSERT(ok == !lastError); |
1438 | return ok; |
1439 | } |
1440 | |
1441 | /*! |
1442 | \fn QCborStreamReader::readAllString() |
1443 | \since 6.7 |
1444 | |
1445 | Decodes the current text string and returns it. If the string is chunked, |
1446 | this function will iterate over all chunks and concatenate them. If an |
1447 | error happens, this function returns a default-constructed QString(), but |
1448 | that may not be distinguishable from certain empty text strings. Instead, |
1449 | check lastError() to determine if an error has happened. |
1450 | |
1451 | \include qcborstreamreader.cpp string-no-type-conversions |
1452 | |
1453 | //! [note-not-restartable] |
1454 | \note This function cannot be resumed. That is, this function should not |
1455 | be used in contexts where the CBOR data may still be received, for example |
1456 | from a socket or pipe. It should only be used when the full data has |
1457 | already been received and is available in the input QByteArray or |
1458 | QIODevice. |
1459 | //! [note-not-restartable] |
1460 | |
1461 | \sa readString(), readStringChunk(), isString(), readAllByteArray() |
1462 | */ |
1463 | /*! |
1464 | \fn QCborStreamReader::readAndAppendToString(QString &dst) |
1465 | \since 6.7 |
1466 | |
1467 | Decodes the current text string and appends to \a dst. If the string is |
1468 | chunked, this function will iterate over all chunks and concatenate them. |
1469 | If an error happens during decoding, other chunks that could be decoded |
1470 | successfully may have been written to \a dst nonetheless. Returns \c true |
1471 | if the decoding happened without errors, \c false otherwise. |
1472 | |
1473 | \include qcborstreamreader.cpp string-no-type-conversions |
1474 | |
1475 | \include qcborstreamreader.cpp note-not-restartable |
1476 | |
1477 | \sa readString(), readStringChunk(), isString(), readAndAppendToByteArray() |
1478 | */ |
1479 | bool QCborStreamReader::_readAndAppendToString_helper(QString &dst) |
1480 | { |
1481 | bool ok = d->readFullString(params: &dst); |
1482 | if (ok) |
1483 | preparse(); |
1484 | return ok; |
1485 | } |
1486 | |
1487 | /*! |
1488 | \fn QCborStreamReader::readAllUtf8String() |
1489 | \since 6.7 |
1490 | |
1491 | Decodes the current text string and returns it. If the string is chunked, |
1492 | this function will iterate over all chunks and concatenate them. If an |
1493 | error happens, this function returns a default-constructed QString(), but |
1494 | that may not be distinguishable from certain empty text strings. Instead, |
1495 | check lastError() to determine if an error has happened. |
1496 | |
1497 | \include qcborstreamreader.cpp string-no-type-conversions |
1498 | |
1499 | \include qcborstreamreader.cpp note-not-restartable |
1500 | |
1501 | \sa readString(), readStringChunk(), isString(), readAllByteArray() |
1502 | */ |
1503 | /*! |
1504 | \fn QCborStreamReader::readAndAppendToUtf8String(QByteArray &dst) |
1505 | \since 6.7 |
1506 | |
1507 | Decodes the current text string and appends to \a dst. If the string is |
1508 | chunked, this function will iterate over all chunks and concatenate them. |
1509 | If an error happens during decoding, other chunks that could be decoded |
1510 | successfully may have been written to \a dst nonetheless. Returns \c true |
1511 | if the decoding happened without errors, \c false otherwise. |
1512 | |
1513 | \include qcborstreamreader.cpp string-no-type-conversions |
1514 | |
1515 | \include qcborstreamreader.cpp note-not-restartable |
1516 | |
1517 | \sa readString(), readStringChunk(), isString(), readAndAppendToByteArray() |
1518 | */ |
1519 | bool QCborStreamReader::_readAndAppendToUtf8String_helper(QByteArray &dst) |
1520 | { |
1521 | using P = QCborStreamReaderPrivate::ReadStringChunk; |
1522 | bool ok = d->readFullString(params: { &dst, P::Utf8String }); |
1523 | if (ok) |
1524 | preparse(); |
1525 | return ok; |
1526 | } |
1527 | |
1528 | /*! |
1529 | \fn QCborStreamReader::readAllByteArray() |
1530 | \since 6.7 |
1531 | |
1532 | Decodes the current byte string and returns it. If the string is chunked, |
1533 | this function will iterate over all chunks and concatenate them. If an |
1534 | error happens, this function returns a default-constructed QByteArray(), |
1535 | but that may not be distinguishable from certain empty byte strings. |
1536 | Instead, check lastError() to determine if an error has happened. |
1537 | |
1538 | \include qcborstreamreader.cpp bytearray-no-type-conversions |
1539 | |
1540 | \include qcborstreamreader.cpp note-not-restartable |
1541 | |
1542 | \sa readByteArray(), readStringChunk(), isByteArray(), readAllString() |
1543 | */ |
1544 | |
1545 | /*! |
1546 | \fn QCborStreamReader::readAndAppendToByteArray(QByteArray &dst) |
1547 | \since 6.7 |
1548 | |
1549 | Decodes the current byte string and appends to \a dst. If the string is |
1550 | chunked, this function will iterate over all chunks and concatenate them. |
1551 | If an error happens during decoding, other chunks that could be decoded |
1552 | successfully may have been written to \a dst nonetheless. Returns \c true |
1553 | if the decoding happened without errors, \c false otherwise. |
1554 | |
1555 | \include qcborstreamreader.cpp bytearray-no-type-conversions |
1556 | |
1557 | \include qcborstreamreader.cpp note-not-restartable |
1558 | |
1559 | \sa readByteArray(), readStringChunk(), isByteArray(), readAndAppendToString() |
1560 | */ |
1561 | bool QCborStreamReader::_readAndAppendToByteArray_helper(QByteArray &dst) |
1562 | { |
1563 | bool ok = d->readFullString(params: &dst); |
1564 | if (ok) |
1565 | preparse(); |
1566 | return ok; |
1567 | } |
1568 | |
1569 | /*! |
1570 | Reads the current string chunk into the buffer pointed to by \a ptr, whose |
1571 | size is \a maxlen. This function returns a \l StringResult object, with the |
1572 | number of bytes copied into \a ptr saved in the \c \l StringResult::data |
1573 | member. The \c \l StringResult::status member indicates whether there was |
1574 | an error reading the string, whether data was copied or whether this was |
1575 | the last chunk. |
1576 | |
1577 | This function can be called for both \l String and \l ByteArray types. |
1578 | For the latter, this function will read the same data that readByteArray() |
1579 | would have returned. For strings, it returns the UTF-8 equivalent of the \l |
1580 | QString that would have been returned. |
1581 | |
1582 | This function is usually used alongside currentStringChunkSize() in a loop. |
1583 | For example: |
1584 | |
1585 | \snippet code/src_corelib_serialization_qcborstream.cpp 29 |
1586 | |
1587 | Unlike readByteArray() and readString(), this function is not limited by |
1588 | implementation limits of QByteArray and QString. |
1589 | |
1590 | \note This function does not perform verification that the UTF-8 contents |
1591 | are properly formatted. That means this function does not produce the |
1592 | QCborError::InvalidUtf8String error, even when readString() does. |
1593 | |
1594 | \sa currentStringChunkSize(), readString(), readByteArray(), |
1595 | isString(), isByteArray() |
1596 | */ |
1597 | QCborStreamReader::StringResult<qsizetype> |
1598 | QCborStreamReader::readStringChunk(char *ptr, qsizetype maxlen) |
1599 | { |
1600 | auto r = d->readStringChunk(params: {ptr, maxlen}); |
1601 | if (r.status == EndOfString && lastError() == QCborError::NoError) |
1602 | preparse(); |
1603 | return r; |
1604 | } |
1605 | |
1606 | // used by qcborvalue.cpp |
1607 | QCborStreamReader::StringResultCode qt_cbor_append_string_chunk(QCborStreamReader &reader, QByteArray *data) |
1608 | { |
1609 | return QCborStreamReaderPrivate::appendStringChunk(reader, data); |
1610 | } |
1611 | |
1612 | inline QCborStreamReader::StringResultCode |
1613 | QCborStreamReaderPrivate::appendStringChunk(QCborStreamReader &reader, QByteArray *data) |
1614 | { |
1615 | auto status = reader.d->readStringChunk(params: data).status; |
1616 | if (status == QCborStreamReader::EndOfString && reader.lastError() == QCborError::NoError) |
1617 | reader.preparse(); |
1618 | return status; |
1619 | } |
1620 | |
1621 | Q_NEVER_INLINE QCborStreamReader::StringResult<qsizetype> |
1622 | QCborStreamReaderPrivate::readStringChunk(ReadStringChunk params) |
1623 | { |
1624 | CborError err; |
1625 | size_t len; |
1626 | const void *content = nullptr; |
1627 | QCborStreamReader::StringResult<qsizetype> result; |
1628 | result.data = 0; |
1629 | result.status = QCborStreamReader::Error; |
1630 | |
1631 | lastError = {}; |
1632 | if (!ensureStringIteration()) |
1633 | return result; |
1634 | |
1635 | // Note: in the current implementation, the call into TinyCBOR below only |
1636 | // succeeds if we *already* have all the data in memory. That's obvious for |
1637 | // the case of direct memory (no QIODevice), whereas for QIODevices |
1638 | // qt_cbor_decoder_transfer_string() enforces that |
1639 | // QIODevice::bytesAvailable() be bigger than the amount we're about to |
1640 | // read. |
1641 | // |
1642 | // This is an important security gate: if the CBOR stream is corrupt or |
1643 | // malicious, and has an impossibly large string size, we only go past it |
1644 | // if the transfer to the destination buffer will succeed (modulo QIODevice |
1645 | // I/O failures). |
1646 | |
1647 | #if 1 |
1648 | // Using internal TinyCBOR API! |
1649 | err = _cbor_value_get_string_chunk(value: ¤tElement, bufferptr: &content, len: &len, next: ¤tElement); |
1650 | #else |
1651 | // the above is effectively the same as: |
1652 | if (cbor_value_is_byte_string(¤tElement)) |
1653 | err = cbor_value_get_byte_string_chunk(¤tElement, reinterpret_cast<const uint8_t **>(&content), |
1654 | &len, ¤tElement); |
1655 | else |
1656 | err = cbor_value_get_text_string_chunk(¤tElement, reinterpret_cast<const char **>(&content), |
1657 | &len, ¤tElement); |
1658 | #endif |
1659 | |
1660 | // Range check: using implementation-defined behavior in converting an |
1661 | // unsigned value out of range of the destination signed type (same as |
1662 | // "len > size_t(std::numeric_limits<qsizetype>::max())", but generates |
1663 | // better code with ICC and MSVC). |
1664 | if (!err && qsizetype(len) < 0) |
1665 | err = CborErrorDataTooLarge; |
1666 | |
1667 | if (err) { |
1668 | if (err == CborErrorNoMoreStringChunks) { |
1669 | preread(); |
1670 | err = cbor_value_finish_string_iteration(value: ¤tElement); |
1671 | result.status = QCborStreamReader::EndOfString; |
1672 | } |
1673 | if (err) |
1674 | handleError(err); |
1675 | // caller musts call preparse() |
1676 | return result; |
1677 | } |
1678 | |
1679 | qptrdiff offset = qptrdiff(content); |
1680 | bufferStart += offset; |
1681 | if (device) { |
1682 | // This first skip can't fail because we've already read this many bytes. |
1683 | device->skip(maxSize: bufferStart); |
1684 | } |
1685 | |
1686 | if (params.isString()) { |
1687 | // readString() |
1688 | result.data = readStringChunk_unicode(params, utf8len: qsizetype(len)); |
1689 | } else if (params.isUtf8String()) { |
1690 | result.data = readStringChunk_utf8(params, utf8len: qsizetype(len)); |
1691 | } else { |
1692 | // readByteArray() or readStringChunk() |
1693 | result.data = readStringChunk_byte(params, len: qsizetype(len)); |
1694 | } |
1695 | |
1696 | if (result.data < 0) |
1697 | return result; // error |
1698 | |
1699 | // adjust the buffers after we're done reading the string |
1700 | bufferStart += len; |
1701 | if (device) { |
1702 | qsizetype remainingInBuffer = buffer.size() - bufferStart; |
1703 | |
1704 | if (remainingInBuffer <= 0) { |
1705 | // We've read from the QIODevice more than what was in the buffer. |
1706 | buffer.truncate(pos: 0); |
1707 | } else { |
1708 | // There's still data buffered, but we need to move it around. |
1709 | char *ptr = buffer.data(); |
1710 | memmove(dest: ptr, src: ptr + bufferStart, n: remainingInBuffer); |
1711 | buffer.truncate(pos: remainingInBuffer); |
1712 | } |
1713 | |
1714 | bufferStart = 0; |
1715 | } |
1716 | |
1717 | preread(); |
1718 | result.status = QCborStreamReader::Ok; |
1719 | return result; |
1720 | } |
1721 | |
1722 | inline qsizetype |
1723 | QCborStreamReaderPrivate::readStringChunk_byte(ReadStringChunk params, qsizetype len) |
1724 | { |
1725 | qint64 actuallyRead; |
1726 | qsizetype toRead = qsizetype(len); |
1727 | qsizetype left = 0; // bytes from the chunk not copied to the user buffer, to discard |
1728 | char *ptr = nullptr; |
1729 | |
1730 | if (params.isPlainPointer()) { |
1731 | left = toRead - params.maxlen_or_type; |
1732 | if (left < 0) |
1733 | left = 0; // buffer bigger than string |
1734 | else |
1735 | toRead = params.maxlen_or_type; // buffer smaller than string |
1736 | ptr = params.ptr; |
1737 | } else if (!params.isString()) { |
1738 | // See note above on having ensured there is enough incoming data. |
1739 | auto oldSize = params.array->size(); |
1740 | auto newSize = oldSize; |
1741 | if (qAddOverflow<decltype(newSize)>(v1: oldSize, v2: toRead, r: &newSize)) { |
1742 | handleError(err: CborErrorDataTooLarge); |
1743 | return -1; |
1744 | } |
1745 | QT_TRY { |
1746 | params.array->resize(size: newSize); |
1747 | } QT_CATCH (const std::bad_alloc &) { |
1748 | // the distinction between DataTooLarge and OOM is mostly for |
1749 | // compatibility with Qt 5; in Qt 6, we could consider everything |
1750 | // to be OOM. |
1751 | handleError(err: newSize > QByteArray::maxSize() ? CborErrorDataTooLarge: CborErrorOutOfMemory); |
1752 | return -1; |
1753 | } |
1754 | |
1755 | ptr = const_cast<char *>(params.array->constData()) + oldSize; |
1756 | } |
1757 | |
1758 | if (device) { |
1759 | actuallyRead = device->read(data: ptr, maxlen: toRead); |
1760 | |
1761 | if (actuallyRead != toRead) { |
1762 | actuallyRead = -1; |
1763 | } else if (left) { |
1764 | qint64 skipped = device->skip(maxSize: left); |
1765 | if (skipped != left) |
1766 | actuallyRead = -1; |
1767 | } |
1768 | |
1769 | if (actuallyRead < 0) { |
1770 | handleError(err: CborErrorIO); |
1771 | return -1; |
1772 | } |
1773 | } else { |
1774 | actuallyRead = toRead; |
1775 | memcpy(dest: ptr, src: buffer.constData() + bufferStart, n: toRead); |
1776 | } |
1777 | |
1778 | return actuallyRead; |
1779 | } |
1780 | |
1781 | inline qsizetype |
1782 | QCborStreamReaderPrivate::readStringChunk_unicode(ReadStringChunk params, qsizetype utf8len) |
1783 | { |
1784 | Q_ASSERT(params.isString()); |
1785 | |
1786 | // See QUtf8::convertToUnicode() a detailed explanation of why this |
1787 | // conversion uses the same number of words or less. |
1788 | qsizetype currentSize = params.string->size(); |
1789 | size_t newSize = size_t(utf8len) + size_t(currentSize); // can't overflow |
1790 | if (utf8len > QString::maxSize() || qsizetype(newSize) < 0) { |
1791 | handleError(err: CborErrorDataTooLarge); |
1792 | return -1; |
1793 | } |
1794 | QT_TRY { |
1795 | params.string->resize(size: qsizetype(newSize)); |
1796 | } QT_CATCH (const std::bad_alloc &) { |
1797 | handleError(err: CborErrorOutOfMemory); |
1798 | return -1; |
1799 | } |
1800 | |
1801 | QChar *begin = const_cast<QChar *>(params.string->constData()); |
1802 | QChar *ptr = begin + currentSize; |
1803 | QStringConverter::State cs(QStringConverter::Flag::Stateless); |
1804 | if (device == nullptr) { |
1805 | // Easy case: we can decode straight from the buffer we already have |
1806 | ptr = QUtf8::convertToUnicode(out: ptr, in: { buffer.constData() + bufferStart, utf8len }, state: &cs); |
1807 | } else { |
1808 | // read in chunks, to avoid creating large, intermediate buffers |
1809 | constexpr qsizetype StringChunkSize = 16384; |
1810 | qsizetype chunkSize = qMin(a: StringChunkSize, b: utf8len); |
1811 | QVarLengthArray<char> chunk(chunkSize); |
1812 | |
1813 | cs = { QStringConverter::Flag::ConvertInitialBom }; |
1814 | while (utf8len > 0 && cs.invalidChars == 0) { |
1815 | qsizetype toRead = qMin(a: chunkSize, b: utf8len); |
1816 | qint64 actuallyRead = device->read(data: chunk.data(), maxlen: toRead); |
1817 | if (actuallyRead == toRead) |
1818 | ptr = QUtf8::convertToUnicode(out: ptr, in: { chunk.data(), toRead }, state: &cs); |
1819 | |
1820 | if (actuallyRead != toRead) { |
1821 | handleError(err: CborErrorIO); |
1822 | return -1; |
1823 | } |
1824 | utf8len -= toRead; |
1825 | } |
1826 | } |
1827 | |
1828 | if (cs.invalidChars != 0 || cs.remainingChars != 0) { |
1829 | handleError(err: CborErrorInvalidUtf8TextString); |
1830 | return -1; |
1831 | } |
1832 | |
1833 | qsizetype size = ptr - begin; |
1834 | params.string->truncate(pos: ptr - begin); |
1835 | return size - currentSize; // how many bytes we added |
1836 | } |
1837 | |
1838 | inline qsizetype |
1839 | QCborStreamReaderPrivate::readStringChunk_utf8(ReadStringChunk params, qsizetype utf8len) |
1840 | { |
1841 | qsizetype result = readStringChunk_byte(params, len: utf8len); |
1842 | if (result < 0) |
1843 | return result; |
1844 | |
1845 | // validate the UTF-8 content we've just read |
1846 | QByteArrayView chunk = *params.array; |
1847 | chunk = chunk.last(n: result); |
1848 | if (QtPrivate::isValidUtf8(s: chunk)) |
1849 | return result; |
1850 | |
1851 | handleError(err: CborErrorInvalidUtf8TextString); |
1852 | return -1; |
1853 | } |
1854 | |
1855 | QT_END_NAMESPACE |
1856 | |
1857 | #include "moc_qcborstreamreader.cpp" |
1858 |
Definitions
- _cbor_value_dup_string
- cbor_value_get_half_float_as_float
- QCborStreamReaderPrivate
- QCborStreamReaderPrivate
- QCborStreamReaderPrivate
- ~QCborStreamReaderPrivate
- setDevice
- initDecoder
- bufferPtr
- preread
- handleError
- ReadStringChunk
- Type
- ReadStringChunk
- ReadStringChunk
- ReadStringChunk
- isString
- isUtf8String
- isByteArray
- isPlainPointer
- qt_cbor_stream_set_error
- qt_cbor_decoder_can_read
- qt_cbor_decoder_advance
- qt_cbor_decoder_read
- qt_cbor_decoder_transfer_string
- ensureStringIteration
- preparse
- QCborStreamReader
- QCborStreamReader
- QCborStreamReader
- QCborStreamReader
- QCborStreamReader
- ~QCborStreamReader
- setDevice
- device
- addData
- addData
- reparse
- clear
- reset
- lastError
- currentOffset
- containerDepth
- parentContainerType
- hasNext
- next
- isLengthKnown
- length
- _enterContainer_helper
- leaveContainer
- _readString_helper
- _readUtf8String_helper
- _readByteArray_helper
- _currentStringChunkSize
- readFullString
- _readAndAppendToString_helper
- _readAndAppendToUtf8String_helper
- _readAndAppendToByteArray_helper
- readStringChunk
- qt_cbor_append_string_chunk
- appendStringChunk
- readStringChunk
- readStringChunk_byte
- readStringChunk_unicode
Start learning QML with our Intro Training
Find out more