| 245 | } |
| 246 | |
| 247 | static void StringifyImpl(const Value& val, std::string& out, int depth) { |
| 248 | static constexpr int MAX_DEPTH = 64; |
| 249 | switch (val.type) { |
| 250 | case Type::Null: out += "null"; break; |
| 251 | case Type::Bool: out += val.boolVal ? "true" : "false"; break; |
| 252 | case Type::Number: { |
| 253 | if (val.numVal == (double)(int64_t)val.numVal && |
| 254 | val.numVal >= -1e15 && val.numVal <= 1e15) { |
| 255 | char buf[32]; |
| 256 | snprintf(buf, sizeof(buf), "%lld", (long long)(int64_t)val.numVal); |
| 257 | out += buf; |
| 258 | } else { |
| 259 | char buf[32]; |
| 260 | snprintf(buf, sizeof(buf), "%.17g", val.numVal); |
| 261 | out += buf; |
| 262 | } |
| 263 | break; |
| 264 | } |
| 265 | case Type::String: EscapeStr(val.strVal, out); break; |
| 266 | case Type::Array: { |
| 267 | out += '['; |
| 268 | if (depth < MAX_DEPTH) { |
| 269 | for (size_t i = 0; i < val.arrVal.size(); ++i) { |
| 270 | if (i > 0) out += ','; |
| 271 | StringifyImpl(val.arrVal[i], out, depth + 1); |
| 272 | } |
| 273 | } |
| 274 | out += ']'; |
| 275 | break; |
| 276 | } |
| 277 | case Type::Object: { |
| 278 | out += '{'; |
| 279 | if (depth < MAX_DEPTH) { |
| 280 | bool first = true; |
| 281 | for (auto& [k, v] : val.objVal) { |
| 282 | if (!first) out += ','; |
| 283 | first = false; |
| 284 | EscapeStr(k, out); |
| 285 | out += ':'; |
| 286 | StringifyImpl(v, out, depth + 1); |
| 287 | } |
| 288 | } |
| 289 | out += '}'; |
| 290 | break; |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | std::string Stringify(const Value& val) { |
| 296 | std::string out; |