sots-engine/src/mars/stream/dump.cpp

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