1/****************************************************************************
2**
3** Copyright (C) 2018 Intel Corporation.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include <QCborStreamReader>
52#include <QCommandLineParser>
53#include <QCommandLineOption>
54#include <QCoreApplication>
55#include <QFile>
56#include <QLocale>
57#include <QStack>
58
59#include <locale.h>
60#include <math.h>
61#include <stdarg.h>
62#include <stdio.h>
63
64/*
65 * To regenerate:
66 * curl -O https://www.iana.org/assignments/cbor-tags/cbor-tags.xml
67 * xsltproc tag-transform.xslt cbor-tags.xml
68 */
69
70// GENERATED CODE
71struct CborTagDescription
72{
73 QCborTag tag;
74 const char *description; // with space and parentheses
75};
76
77// CBOR Tags
78static const CborTagDescription tagDescriptions[] = {
79 // from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
80 { QCborTag(0), " (Standard date/time string; see Section 2.4.1 [RFC7049])" },
81 { QCborTag(1), " (Epoch-based date/time; see Section 2.4.1 [RFC7049])" },
82 { QCborTag(2), " (Positive bignum; see Section 2.4.2 [RFC7049])" },
83 { QCborTag(3), " (Negative bignum; see Section 2.4.2 [RFC7049])" },
84 { QCborTag(4), " (Decimal fraction; see Section 2.4.3 [RFC7049])" },
85 { QCborTag(5), " (Bigfloat; see Section 2.4.3 [RFC7049])" },
86 { QCborTag(16), " (COSE Single Recipient Encrypted Data Object [RFC8152])" },
87 { QCborTag(17), " (COSE Mac w/o Recipients Object [RFC8152])" },
88 { QCborTag(18), " (COSE Single Signer Data Object [RFC8152])" },
89 { QCborTag(21), " (Expected conversion to base64url encoding; see Section 2.4.4.2 [RFC7049])" },
90 { QCborTag(22), " (Expected conversion to base64 encoding; see Section 2.4.4.2 [RFC7049])" },
91 { QCborTag(23), " (Expected conversion to base16 encoding; see Section 2.4.4.2 [RFC7049])" },
92 { QCborTag(24), " (Encoded CBOR data item; see Section 2.4.4.1 [RFC7049])" },
93 { QCborTag(25), " (reference the nth previously seen string)" },
94 { QCborTag(26), " (Serialised Perl object with classname and constructor arguments)" },
95 { QCborTag(27), " (Serialised language-independent object with type name and constructor arguments)" },
96 { QCborTag(28), " (mark value as (potentially) shared)" },
97 { QCborTag(29), " (reference nth marked value)" },
98 { QCborTag(30), " (Rational number)" },
99 { QCborTag(32), " (URI; see Section 2.4.4.3 [RFC7049])" },
100 { QCborTag(33), " (base64url; see Section 2.4.4.3 [RFC7049])" },
101 { QCborTag(34), " (base64; see Section 2.4.4.3 [RFC7049])" },
102 { QCborTag(35), " (Regular expression; see Section 2.4.4.3 [RFC7049])" },
103 { QCborTag(36), " (MIME message; see Section 2.4.4.3 [RFC7049])" },
104 { QCborTag(37), " (Binary UUID ( section 4.1.2))" },
105 { QCborTag(38), " (Language-tagged string)" },
106 { QCborTag(39), " (Identifier)" },
107 { QCborTag(61), " (CBOR Web Token (CWT))" },
108 { QCborTag(96), " (COSE Encrypted Data Object [RFC8152])" },
109 { QCborTag(97), " (COSE MACed Data Object [RFC8152])" },
110 { QCborTag(98), " (COSE Signed Data Object [RFC8152])" },
111 { QCborTag(256), " (mark value as having string references)" },
112 { QCborTag(257), " (Binary MIME message)" },
113 { QCborTag(258), " (Mathematical finite set)" },
114 { QCborTag(260), " (Network Address (IPv4 or IPv6 or MAC Address))" },
115 { QCborTag(264), " (Decimal fraction with arbitrary exponent)" },
116 { QCborTag(265), " (Bigfloat with arbitrary exponent)" },
117 { QCborTag(1001), " (extended time)" },
118 { QCborTag(1002), " (duration)" },
119 { QCborTag(1003), " (period)" },
120 { QCborTag(22098), " (hint that indicates an additional level of indirection)" },
121 { QCborTag(55799), " (Self-describe CBOR; see Section 2.4.5 [RFC7049])" },
122 { QCborTag(15309736), " (RAINS Message)" },
123 { QCborTag(-1), nullptr }
124};
125// END GENERATED CODE
126
127enum {
128 // See RFC 7049 section 2.
129 SmallValueBitLength = 5,
130 SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */
131 Value8Bit = 24,
132 Value16Bit = 25,
133 Value32Bit = 26,
134 Value64Bit = 27
135};
136
137struct CborDumper
138{
139 enum DumpOption {
140 ShowCompact = 0x01,
141 ShowWidthIndicators = 0x02,
142 ShowAnnotated = 0x04
143 };
144 Q_DECLARE_FLAGS(DumpOptions, DumpOption)
145
146 CborDumper(QFile *f, DumpOptions opts_);
147 QCborError dump();
148
149private:
150 void dumpOne(int nestingLevel);
151 void dumpOneDetailed(int nestingLevel);
152
153 void printByteArray(const QByteArray &ba);
154 void printWidthIndicator(quint64 value, char space = '\0');
155 void printStringWidthIndicator(quint64 value);
156
157 QCborStreamReader reader;
158 QByteArray data;
159 QStack<quint8> byteArrayEncoding;
160 qint64 offset = 0;
161 DumpOptions opts;
162};
163Q_DECLARE_OPERATORS_FOR_FLAGS(CborDumper::DumpOptions)
164
165static int cborNumberSize(quint64 value)
166{
167 int normalSize = 1;
168 if (value > std::numeric_limits<quint32>::max())
169 normalSize += 8;
170 else if (value > std::numeric_limits<quint16>::max())
171 normalSize += 4;
172 else if (value > std::numeric_limits<quint8>::max())
173 normalSize += 2;
174 else if (value >= Value8Bit)
175 normalSize += 1;
176 return normalSize;
177}
178
179CborDumper::CborDumper(QFile *f, DumpOptions opts_)
180 : opts(opts_)
181{
182 // try to mmap the file, this is faster
183 char *ptr = reinterpret_cast<char *>(f->map(0, f->size(), QFile::MapPrivateOption));
184 if (ptr) {
185 // worked
186 data = QByteArray::fromRawData(ptr, f->size());
187 reader.addData(data);
188 } else if ((opts & ShowAnnotated) || f->isSequential()) {
189 // details requires full contents, so allocate memory
190 data = f->readAll();
191 reader.addData(data);
192 } else {
193 // just use the QIODevice
194 reader.setDevice(f);
195 }
196}
197
198QCborError CborDumper::dump()
199{
200 byteArrayEncoding << quint8(QCborKnownTags::ExpectedBase16);
201 if (!reader.lastError()) {
202 if (opts & ShowAnnotated)
203 dumpOneDetailed(0);
204 else
205 dumpOne(0);
206 }
207
208 QCborError err = reader.lastError();
209 offset = reader.currentOffset();
210 if (err) {
211 fflush(stdout);
212 fprintf(stderr, "cbordump: decoding failed at %lld: %s\n",
213 offset, qPrintable(err.toString()));
214 if (!data.isEmpty())
215 fprintf(stderr, " bytes at %lld: %s\n", offset,
216 data.mid(offset, 9).toHex(' ').constData());
217 } else {
218 if (!opts.testFlag(ShowAnnotated))
219 printf("\n");
220 if (offset < data.size() || (reader.device() && reader.device()->bytesAvailable()))
221 fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n");
222 }
223
224 return err;
225}
226
227template <typename T> static inline bool canConvertTo(double v)
228{
229 // The [conv.fpint] (7.10 Floating-integral conversions) section of the
230 // standard says only exact conversions are guaranteed. Converting
231 // integrals to floating-point with loss of precision has implementation-
232 // defined behavior whether the next higher or next lower is returned;
233 // converting FP to integral is UB if it can't be represented.;
234 static_assert(std::numeric_limits<T>::is_integer);
235
236 double supremum = ldexp(1, std::numeric_limits<T>::digits);
237 if (v >= supremum)
238 return false;
239
240 if (v < std::numeric_limits<T>::min()) // either zero or a power of two, so it's exact
241 return false;
242
243 // we're in range
244 return v == floor(v);
245}
246
247static QString fpToString(double v, const char *suffix)
248{
249 if (qIsInf(v))
250 return v < 0 ? QStringLiteral("-inf") : QStringLiteral("inf");
251 if (qIsNaN(v))
252 return QStringLiteral("nan");
253 if (canConvertTo<qint64>(v))
254 return QString::number(qint64(v)) + ".0" + suffix;
255 if (canConvertTo<quint64>(v))
256 return QString::number(quint64(v)) + ".0" + suffix;
257
258 QString s = QString::number(v, 'g', QLocale::FloatingPointShortest);
259 if (!s.contains('.') && !s.contains('e'))
260 s += '.';
261 s += suffix;
262 return s;
263};
264
265void CborDumper::dumpOne(int nestingLevel)
266{
267 QString indent(1, QLatin1Char(' '));
268 QString indented = indent;
269 if (!opts.testFlag(ShowCompact)) {
270 indent = QLatin1Char('\n') + QString(4 * nestingLevel, QLatin1Char(' '));
271 indented = QLatin1Char('\n') + QString(4 + 4 * nestingLevel, QLatin1Char(' '));
272 }
273
274 switch (reader.type()) {
275 case QCborStreamReader::UnsignedInteger: {
276 quint64 u = reader.toUnsignedInteger();
277 printf("%llu", u);
278 reader.next();
279 printWidthIndicator(u);
280 return;
281 }
282
283 case QCborStreamReader::NegativeInteger: {
284 quint64 n = quint64(reader.toNegativeInteger());
285 if (n == 0) // -2^64 (wrapped around)
286 printf("-18446744073709551616");
287 else
288 printf("-%llu", n);
289 reader.next();
290 printWidthIndicator(n);
291 return;
292 }
293
294 case QCborStreamReader::ByteArray:
295 case QCborStreamReader::String: {
296 bool isLengthKnown = reader.isLengthKnown();
297 if (!isLengthKnown) {
298 printf("(_ ");
299 ++offset;
300 }
301
302 QString comma;
303 if (reader.isByteArray()) {
304 auto r = reader.readByteArray();
305 while (r.status == QCborStreamReader::Ok) {
306 printf("%s", qPrintable(comma));
307 printByteArray(r.data);
308 printStringWidthIndicator(r.data.size());
309
310 r = reader.readByteArray();
311 comma = QLatin1Char(',') + indented;
312 }
313 } else {
314 auto r = reader.readString();
315 while (r.status == QCborStreamReader::Ok) {
316 printf("%s\"%s\"", qPrintable(comma), qPrintable(r.data));
317 printStringWidthIndicator(r.data.toUtf8().size());
318
319 r = reader.readString();
320 comma = QLatin1Char(',') + indented;
321 }
322 }
323
324 if (!isLengthKnown && !reader.lastError())
325 printf(")");
326 break;
327 }
328
329 case QCborStreamReader::Array:
330 case QCborStreamReader::Map: {
331 const char *delimiters = (reader.isArray() ? "[]" : "{}");
332 printf("%c", delimiters[0]);
333
334 if (reader.isLengthKnown()) {
335 quint64 len = reader.length();
336 reader.enterContainer();
337 printWidthIndicator(len, ' ');
338 } else {
339 reader.enterContainer();
340 offset = reader.currentOffset();
341 printf("_ ");
342 }
343
344 const char *comma = "";
345 while (!reader.lastError() && reader.hasNext()) {
346 printf("%s%s", comma, qPrintable(indented));
347 comma = ",";
348 dumpOne(nestingLevel + 1);
349
350 if (reader.parentContainerType() != QCborStreamReader::Map)
351 continue;
352 if (reader.lastError())
353 break;
354 printf(": ");
355 dumpOne(nestingLevel + 1);
356 }
357
358 if (!reader.lastError()) {
359 reader.leaveContainer();
360 printf("%s%c", qPrintable(indent), delimiters[1]);
361 }
362 break;
363 }
364
365 case QCborStreamReader::Tag: {
366 QCborTag tag = reader.toTag();
367 printf("%llu", quint64(tag));
368
369 if (tag == QCborKnownTags::ExpectedBase16 || tag == QCborKnownTags::ExpectedBase64
370 || tag == QCborKnownTags::ExpectedBase64url)
371 byteArrayEncoding.push(quint8(tag));
372
373 if (reader.next()) {
374 printWidthIndicator(quint64(tag));
375 printf("(");
376 dumpOne(nestingLevel); // same level!
377 printf(")");
378 }
379
380 if (tag == QCborKnownTags::ExpectedBase16 || tag == QCborKnownTags::ExpectedBase64
381 || tag == QCborKnownTags::ExpectedBase64url)
382 byteArrayEncoding.pop();
383 break;
384 }
385
386 case QCborStreamReader::SimpleType:
387 switch (reader.toSimpleType()) {
388 case QCborSimpleType::False:
389 printf("false");
390 break;
391 case QCborSimpleType::True:
392 printf("true");
393 break;
394 case QCborSimpleType::Null:
395 printf("null");
396 break;
397 case QCborSimpleType::Undefined:
398 printf("undefined");
399 break;
400 default:
401 printf("simple(%u)", quint8(reader.toSimpleType()));
402 break;
403 }
404 reader.next();
405 break;
406
407 case QCborStreamReader::Float16:
408 printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16")));
409 reader.next();
410 break;
411 case QCborStreamReader::Float:
412 printf("%s", qPrintable(fpToString(reader.toFloat(), "f")));
413 reader.next();
414 break;
415 case QCborStreamReader::Double:
416 printf("%s", qPrintable(fpToString(reader.toDouble(), "")));
417 reader.next();
418 break;
419 case QCborStreamReader::Invalid:
420 return;
421 }
422
423 offset = reader.currentOffset();
424}
425
426void CborDumper::dumpOneDetailed(int nestingLevel)
427{
428 auto tagDescription = [](QCborTag tag) {
429 for (auto entry : tagDescriptions) {
430 if (entry.tag == tag)
431 return entry.description;
432 if (entry.tag > tag)
433 break;
434 }
435 return "";
436 };
437 auto printOverlong = [](int actualSize, quint64 value) {
438 if (cborNumberSize(value) != actualSize)
439 printf(" (overlong)");
440 };
441 auto print = [=](const char *descr, const char *fmt, ...) {
442 qint64 prevOffset = offset;
443 offset = reader.currentOffset();
444 if (prevOffset == offset)
445 return;
446
447 QByteArray bytes = data.mid(prevOffset, offset - prevOffset);
448 QByteArray indent(nestingLevel * 2, ' ');
449 printf("%-50s # %s ", (indent + bytes.toHex(' ')).constData(), descr);
450
451 va_list va;
452 va_start(va, fmt);
453 vprintf(fmt, va);
454 va_end(va);
455
456 if (strstr(fmt, "%ll")) {
457 // Only works because all callers below that use %ll, use it as the
458 // first arg
459 va_start(va, fmt);
460 quint64 value = va_arg(va, quint64);
461 va_end(va);
462 printOverlong(bytes.size(), value);
463 }
464
465 puts("");
466 };
467
468 auto printFp = [=](const char *descr, double d) {
469 QString s = fpToString(d, "");
470 if (s.size() <= 6)
471 return print(descr, "%s", qPrintable(s));
472 return print(descr, "%a", d);
473 };
474
475 auto printString = [=](const char *descr) {
476 QByteArray indent(nestingLevel * 2, ' ');
477 const char *chunkStr = (reader.isLengthKnown() ? "" : "chunk ");
478 int width = 48 - indent.size();
479 int bytesPerLine = qMax(width / 3, 5);
480
481 qsizetype size = reader.currentStringChunkSize();
482 if (size < 0)
483 return; // error
484 if (size >= std::numeric_limits<int>::max()) {
485 fprintf(stderr, "String length too big, %lli\n", qint64(size));
486 exit(EXIT_FAILURE);
487 }
488
489 // if asking for the current string chunk changes the offset, then it
490 // was chunked
491 print(descr, "(indeterminate length)");
492
493 QByteArray bytes(size, Qt::Uninitialized);
494 auto r = reader.readStringChunk(bytes.data(), bytes.size());
495 while (r.status == QCborStreamReader::Ok) {
496 // We'll have to decode the length's width directly from CBOR...
497 const char *lenstart = data.constData() + offset;
498 const char *lenend = lenstart + 1;
499 quint8 additionalInformation = (*lenstart & SmallValueMask);
500
501 // Decode this number directly from CBOR (see RFC 7049 section 2)
502 if (additionalInformation >= Value8Bit) {
503 if (additionalInformation == Value8Bit)
504 lenend += 1;
505 else if (additionalInformation == Value16Bit)
506 lenend += 2;
507 else if (additionalInformation == Value32Bit)
508 lenend += 4;
509 else
510 lenend += 8;
511 }
512
513 {
514 QByteArray lenbytes = QByteArray::fromRawData(lenstart, lenend - lenstart);
515 printf("%-50s # %s %slength %llu",
516 (indent + lenbytes.toHex(' ')).constData(), descr, chunkStr, quint64(size));
517 printOverlong(lenbytes.size(), size);
518 puts("");
519 }
520
521 offset = reader.currentOffset();
522
523 for (int i = 0; i < r.data; i += bytesPerLine) {
524 QByteArray section = bytes.mid(i, bytesPerLine);
525 printf(" %s%s", indent.constData(), section.toHex(' ').constData());
526
527 // print the decode
528 QByteArray spaces(width > 0 ? width - section.size() * 3 + 1: 0, ' ');
529 printf("%s # \"", spaces.constData());
530 auto ptr = reinterpret_cast<const uchar *>(section.constData());
531 for (int j = 0; j < section.size(); ++j)
532 printf("%c", ptr[j] >= 0x80 || ptr[j] < 0x20 ? '.' : ptr[j]);
533
534 puts("\"");
535 }
536
537 // get the next chunk
538 size = reader.currentStringChunkSize();
539 if (size < 0)
540 return; // error
541 if (size >= std::numeric_limits<int>::max()) {
542 fprintf(stderr, "String length too big, %lli\n", qint64(size));
543 exit(EXIT_FAILURE);
544 }
545 bytes.resize(size);
546 r = reader.readStringChunk(bytes.data(), bytes.size());
547 }
548 };
549
550 if (reader.lastError())
551 return;
552
553 switch (reader.type()) {
554 case QCborStreamReader::UnsignedInteger: {
555 quint64 u = reader.toUnsignedInteger();
556 reader.next();
557 if (u < 65536 || (u % 100000) == 0)
558 print("Unsigned integer", "%llu", u);
559 else
560 print("Unsigned integer", "0x%llx", u);
561 return;
562 }
563
564 case QCborStreamReader::NegativeInteger: {
565 quint64 n = quint64(reader.toNegativeInteger());
566 reader.next();
567 print("Negative integer", n == 0 ? "-18446744073709551616" : "-%llu", n);
568 return;
569 }
570
571 case QCborStreamReader::ByteArray:
572 case QCborStreamReader::String: {
573 bool isLengthKnown = reader.isLengthKnown();
574 const char *descr = (reader.isString() ? "Text string" : "Byte string");
575 if (!isLengthKnown)
576 ++nestingLevel;
577
578 printString(descr);
579 if (reader.lastError())
580 return;
581
582 if (!isLengthKnown) {
583 --nestingLevel;
584 print("Break", "");
585 }
586 break;
587 }
588
589 case QCborStreamReader::Array:
590 case QCborStreamReader::Map: {
591 const char *descr = (reader.isArray() ? "Array" : "Map");
592 if (reader.isLengthKnown()) {
593 quint64 len = reader.length();
594 reader.enterContainer();
595 print(descr, "length %llu", len);
596 } else {
597 reader.enterContainer();
598 print(descr, "(indeterminate length)");
599 }
600
601 while (!reader.lastError() && reader.hasNext())
602 dumpOneDetailed(nestingLevel + 1);
603
604 if (!reader.lastError()) {
605 reader.leaveContainer();
606 print("Break", "");
607 }
608 break;
609 }
610
611 case QCborStreamReader::Tag: {
612 QCborTag tag = reader.toTag();
613 reader.next();
614 print("Tag", "%llu%s", quint64(tag), tagDescription(tag));
615 dumpOneDetailed(nestingLevel + 1);
616 break;
617 }
618
619 case QCborStreamReader::SimpleType: {
620 QCborSimpleType st = reader.toSimpleType();
621 reader.next();
622 switch (st) {
623 case QCborSimpleType::False:
624 print("Simple Type", "false");
625 break;
626 case QCborSimpleType::True:
627 print("Simple Type", "true");
628 break;
629 case QCborSimpleType::Null:
630 print("Simple Type", "null");
631 break;
632 case QCborSimpleType::Undefined:
633 print("Simple Type", "undefined");
634 break;
635 default:
636 print("Simple Type", "%u", quint8(st));
637 break;
638 }
639 break;
640 }
641
642 case QCborStreamReader::Float16: {
643 double d = reader.toFloat16();
644 reader.next();
645 printFp("Float16", d);
646 break;
647 }
648 case QCborStreamReader::Float: {
649 double d = reader.toFloat();
650 reader.next();
651 printFp("Float", d);
652 break;
653 }
654 case QCborStreamReader::Double: {
655 double d = reader.toDouble();
656 reader.next();
657 printFp("Double", d);
658 break;
659 }
660 case QCborStreamReader::Invalid:
661 return;
662 }
663
664 offset = reader.currentOffset();
665}
666
667void CborDumper::printByteArray(const QByteArray &ba)
668{
669 switch (byteArrayEncoding.top()) {
670 default:
671 printf("h'%s'", ba.toHex(' ').constData());
672 break;
673
674 case quint8(QCborKnownTags::ExpectedBase64):
675 printf("b64'%s'", ba.toBase64().constData());
676 break;
677
678 case quint8(QCborKnownTags::ExpectedBase64url):
679 printf("b64'%s'", ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals).constData());
680 break;
681 }
682}
683
684void printIndicator(quint64 value, qint64 previousOffset, qint64 offset, char space)
685{
686 int normalSize = cborNumberSize(value);
687 int actualSize = offset - previousOffset;
688
689 if (actualSize != normalSize) {
690 Q_ASSERT(actualSize > 1);
691 actualSize -= 2;
692 printf("_%d", qPopulationCount(uint(actualSize)));
693 if (space)
694 printf("%c", space);
695 }
696}
697
698void CborDumper::printWidthIndicator(quint64 value, char space)
699{
700 qint64 previousOffset = offset;
701 offset = reader.currentOffset();
702 if (opts & ShowWidthIndicators)
703 printIndicator(value, previousOffset, offset, space);
704}
705
706void CborDumper::printStringWidthIndicator(quint64 value)
707{
708 qint64 previousOffset = offset;
709 offset = reader.currentOffset();
710 if (opts & ShowWidthIndicators)
711 printIndicator(value, previousOffset, offset - uint(value), '\0');
712}
713
714int main(int argc, char *argv[])
715{
716 QCoreApplication app(argc, argv);
717 setlocale(LC_ALL, "C");
718
719 QCommandLineParser parser;
720 parser.setApplicationDescription(QStringLiteral("CBOR Dumper tool"));
721 parser.addHelpOption();
722
723 QCommandLineOption compact({QStringLiteral("c"), QStringLiteral("compact")},
724 QStringLiteral("Use compact form (no line breaks)"));
725 parser.addOption(compact);
726
727 QCommandLineOption showIndicators({QStringLiteral("i"), QStringLiteral("indicators")},
728 QStringLiteral("Show indicators for width of lengths and integrals"));
729 parser.addOption(showIndicators);
730
731 QCommandLineOption verbose({QStringLiteral("a"), QStringLiteral("annotated")},
732 QStringLiteral("Show bytes and annotated decoding"));
733 parser.addOption(verbose);
734
735 parser.addPositionalArgument(QStringLiteral("[source]"),
736 QStringLiteral("CBOR file to read from"));
737
738 parser.process(app);
739
740 CborDumper::DumpOptions opts;
741 if (parser.isSet(compact))
742 opts |= CborDumper::ShowCompact;
743 if (parser.isSet(showIndicators))
744 opts |= CborDumper::ShowWidthIndicators;
745 if (parser.isSet(verbose))
746 opts |= CborDumper::ShowAnnotated;
747
748 QStringList files = parser.positionalArguments();
749 if (files.isEmpty())
750 files << "-";
751 for (const QString &file : qAsConst(files)) {
752 QFile f(file);
753 if (file == "-" ? f.open(stdin, QIODevice::ReadOnly) : f.open(QIODevice::ReadOnly)) {
754 if (files.size() > 1)
755 printf("/ From \"%s\" /\n", qPrintable(file));
756
757 CborDumper dumper(&f, opts);
758 QCborError err = dumper.dump();
759 if (err)
760 return EXIT_FAILURE;
761 }
762 }
763
764 return EXIT_SUCCESS;
765}
766