Commit 415e67951ba678139bb352ff33375c23eab28ca7
Committed by
Jay Berkenbilt
1 parent
1787d850
Refactor JSON::encode_string
Showing
1 changed file
with
52 additions
and
32 deletions
libqpdf/JSON.cc
| @@ -220,41 +220,61 @@ JSON::unparse() const | @@ -220,41 +220,61 @@ JSON::unparse() const | ||
| 220 | std::string | 220 | std::string |
| 221 | JSON::encode_string(std::string const& str) | 221 | JSON::encode_string(std::string const& str) |
| 222 | { | 222 | { |
| 223 | - std::string result; | ||
| 224 | - size_t len = str.length(); | ||
| 225 | - for (size_t i = 0; i < len; ++i) { | ||
| 226 | - unsigned char ch = static_cast<unsigned char>(str.at(i)); | ||
| 227 | - switch (ch) { | ||
| 228 | - case '\\': | ||
| 229 | - result += "\\\\"; | ||
| 230 | - break; | ||
| 231 | - case '\"': | ||
| 232 | - result += "\\\""; | ||
| 233 | - break; | ||
| 234 | - case '\b': | ||
| 235 | - result += "\\b"; | ||
| 236 | - break; | ||
| 237 | - case '\f': | ||
| 238 | - result += "\\f"; | ||
| 239 | - break; | ||
| 240 | - case '\n': | ||
| 241 | - result += "\\n"; | ||
| 242 | - break; | ||
| 243 | - case '\r': | ||
| 244 | - result += "\\r"; | ||
| 245 | - break; | ||
| 246 | - case '\t': | ||
| 247 | - result += "\\t"; | ||
| 248 | - break; | ||
| 249 | - default: | ||
| 250 | - if (ch < 32) { | ||
| 251 | - result += "\\u" + QUtil::int_to_string_base(ch, 16, 4); | ||
| 252 | - } else { | ||
| 253 | - result.append(1, static_cast<char>(ch)); | 223 | + static auto constexpr hexchars = "0123456789abcdef"; |
| 224 | + | ||
| 225 | + auto begin = str.cbegin(); | ||
| 226 | + auto end = str.cend(); | ||
| 227 | + auto iter = begin; | ||
| 228 | + while (iter != end) { | ||
| 229 | + auto c = static_cast<unsigned char>(*iter); | ||
| 230 | + if ((c > 34 && c != '\\') || c == ' ' || c == 33) { | ||
| 231 | + // Optimistically check that no char in str requires escaping. | ||
| 232 | + // Hopefully we can just return the input str. | ||
| 233 | + ++iter; | ||
| 234 | + } else { | ||
| 235 | + // We found a char that requires escaping. Initialize result to the | ||
| 236 | + // chars scanned so far, append/replace the rest of str one char at | ||
| 237 | + // a time, and return the result. | ||
| 238 | + std::string result{begin, iter}; | ||
| 239 | + | ||
| 240 | + for (; iter != end; ++iter) { | ||
| 241 | + auto ch = static_cast<unsigned char>(*iter); | ||
| 242 | + if ((ch > 34 && ch != '\\') || ch == ' ' || ch == 33) { | ||
| 243 | + // Check for most common case first. | ||
| 244 | + result += *iter; | ||
| 245 | + } else { | ||
| 246 | + switch (ch) { | ||
| 247 | + case '\\': | ||
| 248 | + result += "\\\\"; | ||
| 249 | + break; | ||
| 250 | + case '\"': | ||
| 251 | + result += "\\\""; | ||
| 252 | + break; | ||
| 253 | + case '\b': | ||
| 254 | + result += "\\b"; | ||
| 255 | + break; | ||
| 256 | + case '\f': | ||
| 257 | + result += "\\f"; | ||
| 258 | + break; | ||
| 259 | + case '\n': | ||
| 260 | + result += "\\n"; | ||
| 261 | + break; | ||
| 262 | + case '\r': | ||
| 263 | + result += "\\r"; | ||
| 264 | + break; | ||
| 265 | + case '\t': | ||
| 266 | + result += "\\t"; | ||
| 267 | + break; | ||
| 268 | + default: | ||
| 269 | + result += ch < 16 ? "\\u000" : "\\u001"; | ||
| 270 | + result += hexchars[ch % 16]; | ||
| 271 | + } | ||
| 272 | + } | ||
| 254 | } | 273 | } |
| 274 | + return result; | ||
| 255 | } | 275 | } |
| 256 | } | 276 | } |
| 257 | - return result; | 277 | + return str; |
| 258 | } | 278 | } |
| 259 | 279 | ||
| 260 | JSON | 280 | JSON |