diff options
Diffstat (limited to 'libqpdf/JSON.cc')
-rw-r--r-- | libqpdf/JSON.cc | 130 |
1 files changed, 76 insertions, 54 deletions
diff --git a/libqpdf/JSON.cc b/libqpdf/JSON.cc index 1dc09013..76db652b 100644 --- a/libqpdf/JSON.cc +++ b/libqpdf/JSON.cc @@ -125,6 +125,7 @@ JSON::JSON_array::write(Pipeline* p, size_t depth) const } JSON::JSON_string::JSON_string(std::string const& utf8) : + JSON_value(vt_string), utf8(utf8), encoded(encode_string(utf8)) { @@ -137,16 +138,19 @@ JSON::JSON_string::write(Pipeline* p, size_t) const } JSON::JSON_number::JSON_number(long long value) : + JSON_value(vt_number), encoded(std::to_string(value)) { } JSON::JSON_number::JSON_number(double value) : + JSON_value(vt_number), encoded(QUtil::double_to_string(value, 6)) { } JSON::JSON_number::JSON_number(std::string const& value) : + JSON_value(vt_number), encoded(value) { } @@ -158,6 +162,7 @@ JSON::JSON_number::write(Pipeline* p, size_t) const } JSON::JSON_bool::JSON_bool(bool val) : + JSON_value(vt_bool), value(val) { } @@ -175,6 +180,7 @@ JSON::JSON_null::write(Pipeline* p, size_t) const } JSON::JSON_blob::JSON_blob(std::function<void(Pipeline*)> fn) : + JSON_value(vt_blob), fn(fn) { } @@ -212,41 +218,61 @@ JSON::unparse() const std::string JSON::encode_string(std::string const& str) { - std::string result; - size_t len = str.length(); - for (size_t i = 0; i < len; ++i) { - unsigned char ch = static_cast<unsigned char>(str.at(i)); - switch (ch) { - case '\\': - result += "\\\\"; - break; - case '\"': - result += "\\\""; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - default: - if (ch < 32) { - result += "\\u" + QUtil::int_to_string_base(ch, 16, 4); - } else { - result.append(1, static_cast<char>(ch)); + static auto constexpr hexchars = "0123456789abcdef"; + + auto begin = str.cbegin(); + auto end = str.cend(); + auto iter = begin; + while (iter != end) { + auto c = static_cast<unsigned char>(*iter); + if ((c > 34 && c != '\\') || c == ' ' || c == 33) { + // Optimistically check that no char in str requires escaping. + // Hopefully we can just return the input str. + ++iter; + } else { + // We found a char that requires escaping. Initialize result to the + // chars scanned so far, append/replace the rest of str one char at + // a time, and return the result. + std::string result{begin, iter}; + + for (; iter != end; ++iter) { + auto ch = static_cast<unsigned char>(*iter); + if ((ch > 34 && ch != '\\') || ch == ' ' || ch == 33) { + // Check for most common case first. + result += *iter; + } else { + switch (ch) { + case '\\': + result += "\\\\"; + break; + case '\"': + result += "\\\""; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + default: + result += ch < 16 ? "\\u000" : "\\u001"; + result += hexchars[ch % 16]; + } + } } + return result; } } - return result; + return str; } JSON @@ -348,56 +374,52 @@ JSON::makeBlob(std::function<void(Pipeline*)> fn) bool JSON::isArray() const { - return nullptr != dynamic_cast<JSON_array const*>(this->m->value.get()); + return m->value->type_code == vt_array; } bool JSON::isDictionary() const { - return nullptr != - dynamic_cast<JSON_dictionary const*>(this->m->value.get()); + return m->value->type_code == vt_dictionary; } bool JSON::getString(std::string& utf8) const { - auto v = dynamic_cast<JSON_string const*>(this->m->value.get()); - if (v == nullptr) { - return false; + if (m->value->type_code == vt_string) { + auto v = dynamic_cast<JSON_string const*>(this->m->value.get()); + utf8 = v->utf8; + return true; } - utf8 = v->utf8; - return true; + return false; } bool JSON::getNumber(std::string& value) const { - auto v = dynamic_cast<JSON_number const*>(this->m->value.get()); - if (v == nullptr) { - return false; + if (m->value->type_code == vt_number) { + auto v = dynamic_cast<JSON_number const*>(this->m->value.get()); + value = v->encoded; + return true; } - value = v->encoded; - return true; + return false; } bool JSON::getBool(bool& value) const { - auto v = dynamic_cast<JSON_bool const*>(this->m->value.get()); - if (v == nullptr) { - return false; + if (m->value->type_code == vt_bool) { + auto v = dynamic_cast<JSON_bool const*>(this->m->value.get()); + value = v->value; + return true; } - value = v->value; - return true; + return false; } bool JSON::isNull() const { - if (dynamic_cast<JSON_null const*>(this->m->value.get())) { - return true; - } - return false; + return m->value->type_code == vt_null; } bool |