| 1 | // Copyright (C) 2016 The Qt Company Ltd. |
| 2 | // Copyright (C) 2016 Intel Corporation. |
| 3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 4 | |
| 5 | #include <cmath> |
| 6 | #include <qlocale.h> |
| 7 | #include "qjsonwriter_p.h" |
| 8 | #include "qjson_p.h" |
| 9 | #include "private/qstringconverter_p.h" |
| 10 | #include <private/qnumeric_p.h> |
| 11 | #include <private/qcborvalue_p.h> |
| 12 | |
| 13 | QT_BEGIN_NAMESPACE |
| 14 | |
| 15 | using namespace QJsonPrivate; |
| 16 | |
| 17 | static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact); |
| 18 | static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact); |
| 19 | |
| 20 | static inline uchar hexdig(uint u) |
| 21 | { |
| 22 | return (u < 0xa ? '0' + u : 'a' + u - 0xa); |
| 23 | } |
| 24 | |
| 25 | static QByteArray escapedString(QStringView s) |
| 26 | { |
| 27 | // give it a minimum size to ensure the resize() below always adds enough space |
| 28 | QByteArray ba(qMax(a: s.size(), b: 16), Qt::Uninitialized); |
| 29 | |
| 30 | auto ba_const_start = [&]() { return reinterpret_cast<const uchar *>(ba.constData()); }; |
| 31 | uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData())); |
| 32 | const uchar *ba_end = cursor + ba.size(); |
| 33 | const char16_t *src = s.utf16(); |
| 34 | const char16_t *const end = s.utf16() + s.size(); |
| 35 | |
| 36 | while (src != end) { |
| 37 | if (cursor >= ba_end - 6) { |
| 38 | // ensure we have enough space |
| 39 | qptrdiff pos = cursor - ba_const_start(); |
| 40 | ba.resize(size: ba.size()*2); |
| 41 | cursor = reinterpret_cast<uchar *>(ba.data()) + pos; |
| 42 | ba_end = ba_const_start() + ba.size(); |
| 43 | } |
| 44 | |
| 45 | char16_t u = *src++; |
| 46 | if (u < 0x80) { |
| 47 | if (u < 0x20 || u == 0x22 || u == 0x5c) { |
| 48 | *cursor++ = '\\'; |
| 49 | switch (u) { |
| 50 | case 0x22: |
| 51 | *cursor++ = '"'; |
| 52 | break; |
| 53 | case 0x5c: |
| 54 | *cursor++ = '\\'; |
| 55 | break; |
| 56 | case 0x8: |
| 57 | *cursor++ = 'b'; |
| 58 | break; |
| 59 | case 0xc: |
| 60 | *cursor++ = 'f'; |
| 61 | break; |
| 62 | case 0xa: |
| 63 | *cursor++ = 'n'; |
| 64 | break; |
| 65 | case 0xd: |
| 66 | *cursor++ = 'r'; |
| 67 | break; |
| 68 | case 0x9: |
| 69 | *cursor++ = 't'; |
| 70 | break; |
| 71 | default: |
| 72 | *cursor++ = 'u'; |
| 73 | *cursor++ = '0'; |
| 74 | *cursor++ = '0'; |
| 75 | *cursor++ = hexdig(u: u>>4); |
| 76 | *cursor++ = hexdig(u: u & 0xf); |
| 77 | } |
| 78 | } else { |
| 79 | *cursor++ = (uchar)u; |
| 80 | } |
| 81 | } else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, dst&: cursor, src, end) < 0) { |
| 82 | // failed to get valid utf8 use JSON escape sequence |
| 83 | *cursor++ = '\\'; |
| 84 | *cursor++ = 'u'; |
| 85 | *cursor++ = hexdig(u: u>>12 & 0x0f); |
| 86 | *cursor++ = hexdig(u: u>>8 & 0x0f); |
| 87 | *cursor++ = hexdig(u: u>>4 & 0x0f); |
| 88 | *cursor++ = hexdig(u: u & 0x0f); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | ba.resize(size: cursor - ba_const_start()); |
| 93 | return ba; |
| 94 | } |
| 95 | |
| 96 | static void valueToJson(const QCborValue &v, QByteArray &json, int indent, bool compact) |
| 97 | { |
| 98 | QCborValue::Type type = v.type(); |
| 99 | switch (type) { |
| 100 | case QCborValue::True: |
| 101 | json += "true" ; |
| 102 | break; |
| 103 | case QCborValue::False: |
| 104 | json += "false" ; |
| 105 | break; |
| 106 | case QCborValue::Integer: |
| 107 | json += QByteArray::number(v.toInteger()); |
| 108 | break; |
| 109 | case QCborValue::Double: { |
| 110 | const double d = v.toDouble(); |
| 111 | if (qt_is_finite(d)) |
| 112 | json += QByteArray::number(d, format: 'g', precision: QLocale::FloatingPointShortest); |
| 113 | else |
| 114 | json += "null" ; // +INF || -INF || NaN (see RFC4627#section2.4) |
| 115 | break; |
| 116 | } |
| 117 | case QCborValue::String: |
| 118 | json += '"'; |
| 119 | json += escapedString(s: v.toString()); |
| 120 | json += '"'; |
| 121 | break; |
| 122 | case QCborValue::Array: |
| 123 | json += compact ? "[" : "[\n" ; |
| 124 | arrayContentToJson( |
| 125 | a: QJsonPrivate::Value::container(v), json, indent: indent + (compact ? 0 : 1), compact); |
| 126 | json += QByteArray(4*indent, ' '); |
| 127 | json += ']'; |
| 128 | break; |
| 129 | case QCborValue::Map: |
| 130 | json += compact ? "{" : "{\n" ; |
| 131 | objectContentToJson( |
| 132 | o: QJsonPrivate::Value::container(v), json, indent: indent + (compact ? 0 : 1), compact); |
| 133 | json += QByteArray(4*indent, ' '); |
| 134 | json += '}'; |
| 135 | break; |
| 136 | case QCborValue::Null: |
| 137 | default: |
| 138 | json += "null" ; |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact) |
| 143 | { |
| 144 | if (!a || a->elements.empty()) |
| 145 | return; |
| 146 | |
| 147 | QByteArray indentString(4*indent, ' '); |
| 148 | |
| 149 | qsizetype i = 0; |
| 150 | while (true) { |
| 151 | json += indentString; |
| 152 | valueToJson(v: a->valueAt(idx: i), json, indent, compact); |
| 153 | |
| 154 | if (++i == a->elements.size()) { |
| 155 | if (!compact) |
| 156 | json += '\n'; |
| 157 | break; |
| 158 | } |
| 159 | |
| 160 | json += compact ? "," : ",\n" ; |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | |
| 165 | static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact) |
| 166 | { |
| 167 | if (!o || o->elements.empty()) |
| 168 | return; |
| 169 | |
| 170 | QByteArray indentString(4*indent, ' '); |
| 171 | |
| 172 | qsizetype i = 0; |
| 173 | while (true) { |
| 174 | QCborValue e = o->valueAt(idx: i); |
| 175 | json += indentString; |
| 176 | json += '"'; |
| 177 | json += escapedString(s: o->valueAt(idx: i).toString()); |
| 178 | json += compact ? "\":" : "\": " ; |
| 179 | valueToJson(v: o->valueAt(idx: i + 1), json, indent, compact); |
| 180 | |
| 181 | if ((i += 2) == o->elements.size()) { |
| 182 | if (!compact) |
| 183 | json += '\n'; |
| 184 | break; |
| 185 | } |
| 186 | |
| 187 | json += compact ? "," : ",\n" ; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | void Writer::objectToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact) |
| 192 | { |
| 193 | json.reserve(asize: json.size() + (o ? (int)o->elements.size() : 16)); |
| 194 | json += compact ? "{" : "{\n" ; |
| 195 | objectContentToJson(o, json, indent: indent + (compact ? 0 : 1), compact); |
| 196 | json += QByteArray(4*indent, ' '); |
| 197 | json += compact ? "}" : "}\n" ; |
| 198 | } |
| 199 | |
| 200 | void Writer::arrayToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact) |
| 201 | { |
| 202 | json.reserve(asize: json.size() + (a ? (int)a->elements.size() : 16)); |
| 203 | json += compact ? "[" : "[\n" ; |
| 204 | arrayContentToJson(a, json, indent: indent + (compact ? 0 : 1), compact); |
| 205 | json += QByteArray(4*indent, ' '); |
| 206 | json += compact ? "]" : "]\n" ; |
| 207 | } |
| 208 | |
| 209 | QT_END_NAMESPACE |
| 210 | |