| 218 | } |
| 219 | |
| 220 | static void EscapeStr(const std::string& s, std::string& out) { |
| 221 | out += '"'; |
| 222 | for (unsigned char c : s) { |
| 223 | switch (c) { |
| 224 | case '"': out += "\\\""; break; |
| 225 | case '\\': out += "\\\\"; break; |
| 226 | case '\n': out += "\\n"; break; |
| 227 | case '\r': out += "\\r"; break; |
| 228 | case '\t': out += "\\t"; break; |
| 229 | default: |
| 230 | if (c < 0x20) { |
| 231 | // Escape control characters as \u00XX |
| 232 | char esc[8]; |
| 233 | snprintf(esc, sizeof(esc), "\\u%04X", c); |
| 234 | out += esc; |
| 235 | } else { |
| 236 | // Pass through printable ASCII (0x20-0x7E) and UTF-8 |
| 237 | // continuation/lead bytes (>= 0x80) verbatim to preserve |
| 238 | // multi-byte sequences |
| 239 | out += (char)c; |
| 240 | } |
| 241 | break; |
| 242 | } |
| 243 | } |
| 244 | out += '"'; |
| 245 | } |
| 246 | |
| 247 | static void StringifyImpl(const Value& val, std::string& out, int depth) { |
| 248 | static constexpr int MAX_DEPTH = 64; |