147 lines
5.2 KiB
C++
147 lines
5.2 KiB
C++
#include "dump.h"
|
|
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
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<std::string>& 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 : "<tagless>";
|
|
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<std::string> dump_tree(const Node& root, int max_depth) {
|
|
std::vector<std::string> lines;
|
|
dump_into(root, 0, lines, max_depth);
|
|
return lines;
|
|
}
|
|
|
|
} // namespace mars::stream
|