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