diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2011-12-05 16:21:55 +0100 |
---|---|---|
committer | Simons Kevin <kevin.simons@nokia.com> | 2011-12-06 06:14:17 +0100 |
commit | da1bb989f409431181fc5e7b58fde2af4986869f (patch) | |
tree | 26756ca88b129e6402bfdb2c90cc1585cfc08c9a | |
parent | 121b80db58a421ffcfda751babc0909bab42a422 (diff) |
Properly escape strings when converting to text
Properly escape character sequences that need
to be escaped in JSON.
Change-Id: Ia2e3c53447df5b82330701232c868049b108eac6
Reviewed-by: Simons Kevin <kevin.simons@nokia.com>
-rw-r--r-- | src/qbinaryjsonobject.cpp | 132 | ||||
-rw-r--r-- | tests/auto/tst_qtbinaryjson.cpp | 8 |
2 files changed, 135 insertions, 5 deletions
diff --git a/src/qbinaryjsonobject.cpp b/src/qbinaryjsonobject.cpp index 7086517..4243116 100644 --- a/src/qbinaryjsonobject.cpp +++ b/src/qbinaryjsonobject.cpp @@ -87,6 +87,134 @@ QVariantMap JsonObject::toVariantMap() const static void objectContentToJson(const Object *o, QByteArray &json, int indent); static void arrayContentToJson(const Array *a, QByteArray &json, int indent); +// some code from qutfcodec.cpp, inlined here for performance reasons +// to allow fast escaping of strings +static inline bool isUnicodeNonCharacter(uint ucs4) +{ + // Unicode has a couple of "non-characters" that one can use internally, + // but are not allowed to be used for text interchange. + // + // Those are the last two entries each Unicode Plane (U+FFFE, U+FFFF, + // U+1FFFE, U+1FFFF, etc.) as well as the entries between U+FDD0 and + // U+FDEF (inclusive) + + return (ucs4 & 0xfffe) == 0xfffe + || (ucs4 - 0xfdd0U) < 16; +} + +static inline uchar hexdig(uint u) +{ + return (u < 0xa ? '0' + u : 'a' + u - 0xa); +} + +static QByteArray escapedString(const QString &s) +{ + const uchar replacement = '?'; + QByteArray ba(s.length(), Qt::Uninitialized); + + uchar *cursor = (uchar *)ba.data(); + const uchar *ba_end = cursor + ba.length(); + + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + + int surrogate_high = -1; + + while (ch < end) { + if (cursor >= ba_end - 6) { + // ensure we have enough space + int pos = cursor - (uchar *)ba.constData(); + ba.resize(ba.size()*2); + cursor = (uchar *)ba.data() + pos; + ba_end = (const uchar *)ba.constData() + ba.length(); + } + + uint u = ch->unicode(); + if (surrogate_high >= 0) { + if (ch->isLowSurrogate()) { + u = QChar::surrogateToUcs4(surrogate_high, u); + surrogate_high = -1; + } else { + // high surrogate without low + *cursor = replacement; + ++ch; + surrogate_high = -1; + continue; + } + } else if (ch->isLowSurrogate()) { + // low surrogate without high + *cursor = replacement; + ++ch; + continue; + } else if (ch->isHighSurrogate()) { + surrogate_high = u; + ++ch; + continue; + } + + if (u < 0x80) { + if (u < 0x20 || u == 0x22 || u == 0x5c) { + *cursor++ = '\\'; + switch (u) { + case 0x22: + *cursor++ = '"'; + break; + case 0x5c: + *cursor++ = '\\'; + break; + case 0x8: + *cursor++ = 'b'; + break; + case 0xc: + *cursor++ = 'f'; + break; + case 0xa: + *cursor++ = 'n'; + break; + case 0xd: + *cursor++ = 'r'; + break; + case 0x9: + *cursor++ = 't'; + break; + default: + *cursor++ = 'u'; + *cursor++ = '0'; + *cursor++ = '0'; + *cursor++ = hexdig(u>>4); + *cursor++ = hexdig(u & 0xf); + } + } else { + *cursor++ = (uchar)u; + } + } else { + if (u < 0x0800) { + *cursor++ = 0xc0 | ((uchar) (u >> 6)); + } else { + // is it one of the Unicode non-characters? + if (isUnicodeNonCharacter(u)) { + *cursor++ = replacement; + ++ch; + continue; + } + + if (u > 0xffff) { + *cursor++ = 0xf0 | ((uchar) (u >> 18)); + *cursor++ = 0x80 | (((uchar) (u >> 12)) & 0x3f); + } else { + *cursor++ = 0xe0 | (((uchar) (u >> 12)) & 0x3f); + } + *cursor++ = 0x80 | (((uchar) (u >> 6)) & 0x3f); + } + *cursor++ = 0x80 | ((uchar) (u&0x3f)); + } + ++ch; + } + + ba.resize(cursor - (const uchar *)ba.constData()); + return ba; +} + static void valueToJson(const Value *v, QByteArray &json, int indent) { ValueType type = v ? (ValueType)v->type : NullValue; @@ -99,7 +227,7 @@ static void valueToJson(const Value *v, QByteArray &json, int indent) break; case StringValue: json += '"'; - json += v->shallowString().toUtf8(); + json += escapedString(v->shallowString()); json += '"'; break; case ArrayValue: @@ -154,7 +282,7 @@ static void objectContentToJson(const Object *o, QByteArray &json, int indent) Entry *e = o->entryAt(i); json += indentString; json += '"'; - json += e->shallowKey().toUtf8(); + json += escapedString(e->shallowKey()); json += "\": "; const Value *v = e->value(); valueToJson(v, json, indent); diff --git a/tests/auto/tst_qtbinaryjson.cpp b/tests/auto/tst_qtbinaryjson.cpp index 2b3c042..9063025 100644 --- a/tests/auto/tst_qtbinaryjson.cpp +++ b/tests/auto/tst_qtbinaryjson.cpp @@ -477,26 +477,28 @@ void TestQtBinaryJson::toVariantMap() void TestQtBinaryJson::toJson() { JsonObject object; - object.insert("Key", QString("Value")); + object.insert("\\Key\n", QString("Value")); object.insert("null", JsonValue()); JsonArray array; array.append(true); array.append(999.); array.append(QLatin1String("string")); array.append(JsonValue()); + array.append(QLatin1String("\\\a\n\r\b\tabcABC\"")); object.insert("Array", array); QByteArray json = object.toJson(); QByteArray expected = "{\n" - " \"Key\": \"Value\",\n" + " \"\\\\Key\\n\": \"Value\",\n" " \"null\": null,\n" " \"Array\": [\n" " true,\n" " 999,\n" " \"string\",\n" - " null\n" + " null,\n" + " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n" " ]\n" "}\n"; QCOMPARE(json, expected); |