#include "dump.h" #include #include #include #include namespace mars::stream { std::string py_float_repr(double v) { if (std::isnan(v)) return "nan"; if (std::isinf(v)) return v < 0 ? "-inf" : "inf"; if (v == 0.0) return std::signbit(v) ? "-0.0" : "0.0"; // shortest digit string that round-trips char buf[64]; int prec = 1; for (; prec <= 17; ++prec) { std::snprintf(buf, sizeof buf, "%.*e", prec - 1, v); if (std::strtod(buf, nullptr) == v) break; } // buf: [-]d[.ddd]e[+-]XX std::string s(buf); bool neg = s[0] == '-'; if (neg) s.erase(0, 1); size_t epos = s.find('e'); int exp10 = std::atoi(s.c_str() + epos + 1); std::string digits; for (size_t i = 0; i < epos; ++i) if (s[i] != '.') digits.push_back(s[i]); while (digits.size() > 1 && digits.back() == '0') digits.pop_back(); int decpt = exp10 + 1; // value = 0.digits * 10^decpt std::string out = neg ? "-" : ""; if (decpt > -4 && decpt <= 16) { int nd = int(digits.size()); if (decpt <= 0) { out += "0."; out.append(size_t(-decpt), '0'); out += digits; } else if (decpt >= nd) { out += digits; out.append(size_t(decpt - nd), '0'); out += ".0"; } else { out += digits.substr(0, size_t(decpt)); out += "."; out += digits.substr(size_t(decpt)); } } else { out += digits[0]; if (digits.size() > 1) { out += "."; out += digits.substr(1); } char e[16]; std::snprintf(e, sizeof e, "e%c%02d", exp10 < 0 ? '-' : '+', std::abs(exp10)); out += e; } return out; } namespace { // windows-1252 0x80..0x9f -> Unicode (0 = undefined -> U+FFFD like errors="replace") const uint16_t kCp1252High[32] = { 0x20AC, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0x017D, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0x017E, 0x0178}; } // namespace std::string json_quote_cp1252(const std::string& bytes) { std::string o = "\""; char tmp[8]; for (unsigned char c : bytes) { uint32_t u = c; if (c >= 0x80 && c < 0xa0) u = kCp1252High[c - 0x80] ? kCp1252High[c - 0x80] : 0xFFFD; switch (u) { case '"': o += "\\\""; break; case '\\': o += "\\\\"; break; case '\n': o += "\\n"; break; case '\r': o += "\\r"; break; case '\t': o += "\\t"; break; case '\b': o += "\\b"; break; case '\f': o += "\\f"; break; default: if (u < 0x20 || u >= 0x7f) { std::snprintf(tmp, sizeof tmp, "\\u%04x", u); o += tmp; } else { o.push_back(char(u)); } } } o += "\""; return o; } namespace { void dump_into(const Node& node, int indent, std::vector& lines, int max_depth) { std::string pad(size_t(indent) * 2, ' '); char off[16]; for (const Node& c : node.children) { std::string nm = c.tagged ? c.name : ""; std::snprintf(off, sizeof off, "@%08x ", c.offset); if (c.is_complex()) { lines.push_back(std::string(off) + pad + nm + " { # " + std::to_string(c.children.size()) + " items, " + std::to_string(c.size) + " bytes"); if (indent < max_depth) dump_into(c, indent + 1, lines, max_depth); std::snprintf(off, sizeof off, "@%08x ", c.offset + c.size - 4); lines.push_back(std::string(off) + pad + "}"); } else if (c.kind == Kind::Raw) { size_t n = c.raw.size(); lines.push_back(std::string(off) + pad + nm + " raw[" + std::to_string(n) + "] " + hex(c.raw.data(), n < 32 ? n : 32) + (n > 32 ? "..." : "")); } else { std::string val, alt; switch (c.kind) { case Kind::Int: val = std::to_string(c.as_int()); if (!c.hinted) { float f = c.as_float(); if (std::isfinite(f)) alt = " (alt " + py_float_repr(f) + ")"; } break; case Kind::Float: val = py_float_repr(c.as_float()); if (!c.hinted) alt = " (alt " + std::to_string(c.as_int()) + ")"; break; case Kind::Bool: val = c.as_bool() ? "True" : "False"; break; case Kind::Int64: val = std::to_string(c.as_int64()); break; case Kind::String: val = json_quote_cp1252(c.as_string()); break; default: break; } lines.push_back(std::string(off) + pad + nm + " " + kind_name(c.kind) + (c.hinted ? "" : "?") + " " + val + alt); } } } } // namespace std::vector dump_tree(const Node& root, int max_depth) { std::vector lines; dump_into(root, 0, lines, max_depth); return lines; } } // namespace mars::stream