Q-W1 pt1: extract core/json (json::Reader/Writer), collapse wire Cursor family into core/wire, shared readFileBytes — five JSON decoders and three cursor copies deleted, byte-identical formats, 59/59 green

This commit is contained in:
2026-07-28 19:59:02 -04:00
parent 88e7765ee5
commit 67a41728f3
25 changed files with 1695 additions and 1696 deletions
+134
View File
@@ -0,0 +1,134 @@
// core/wire implementation — see wire.h. The bodies are the hardened
// assignment_request / sample_usage / provenance (post Q-W0 T2-01a backport)
// cursor, unified; any behavioral change here changes every ext-state wire
// seam at once.
#include "core/wire/wire.h"
#include <cstdlib>
#include <limits>
namespace reasampler::wire {
void putField(std::string& out, const std::string& field) {
out += std::to_string(field.size());
out += ':';
out += field;
}
bool parseUnsignedDecimal(const std::string& s, std::int64_t& out) {
if (s.empty()) return false;
std::int64_t value = 0;
constexpr std::int64_t kMax = std::numeric_limits<std::int64_t>::max();
for (const char c : s) {
if (c < '0' || c > '9') return false; // any non-digit -> reject whole
const int digit = c - '0';
// Guard value*10 + digit against overflow before performing it.
if (value > (kMax - digit) / 10) return false;
value = value * 10 + digit;
}
out = value;
return true;
}
bool Cursor::literal(const char* lit) {
if (!ok_) return false;
std::size_t i = 0;
for (; lit[i] != '\0'; ++i) {
if (pos_ + i >= s_.size() || s_[pos_ + i] != lit[i]) return fail();
}
pos_ += i;
return true;
}
bool Cursor::field(std::string& out) {
if (!ok_) return false;
const std::size_t colon = s_.find(':', pos_);
if (colon == std::string::npos) return fail();
if (colon == pos_) return fail(); // empty length token
// Cap: SIZE_MAX fits in at most 20 decimal digits; a longer run is bogus.
if (colon - pos_ > 20u) return fail();
std::size_t len = 0;
for (std::size_t i = pos_; i < colon; ++i) {
const char c = s_[i];
if (c < '0' || c > '9') return fail();
const std::size_t digit = static_cast<std::size_t>(c - '0');
// Overflow guard: if len would exceed SIZE_MAX after multiply+add, fail.
if (len > (std::numeric_limits<std::size_t>::max() - digit) / 10u)
return fail();
len = len * 10u + digit;
}
const std::size_t start = colon + 1;
// Subtraction-first form: start + len cannot wrap on a huge len.
if (start > s_.size() || len > s_.size() - start) return fail();
out.assign(s_, start, len);
pos_ = start + len;
return true;
}
bool Cursor::fieldInt64(std::int64_t& out) {
std::string f;
if (!field(f)) return false;
if (f.empty()) return fail();
std::size_t i = 0;
bool neg = false;
if (f[0] == '-') {
neg = true;
i = 1;
if (f.size() == 1) return fail(); // bare "-"
}
// Cap at 19 digits (INT64_MAX = 9223372036854775807 — 19 digits). A 20-digit
// positive value would overflow INT64_MAX; a 20-digit negative might be valid
// (INT64_MIN) but is conservatively rejected too — see header.
if (f.size() - i > 19u) return fail();
std::int64_t v = 0;
for (; i < f.size(); ++i) {
const char c = f[i];
if (c < '0' || c > '9') return fail();
const std::int64_t digit = static_cast<std::int64_t>(c - '0');
// Overflow guard: v * 10 + digit must not exceed INT64_MAX.
if (v > (std::numeric_limits<std::int64_t>::max() - digit) / 10)
return fail();
v = v * 10 + digit;
}
out = neg ? -v : v;
return true;
}
bool Cursor::fieldInt(int& out) {
std::int64_t v = 0;
if (!fieldInt64(v)) return false;
if (v < std::numeric_limits<int>::min() || v > std::numeric_limits<int>::max())
return fail();
out = static_cast<int>(v);
return true;
}
bool Cursor::fieldSizeT(std::size_t& out) {
std::string f;
if (!field(f)) return false;
if (f.empty() || f.size() > 20u) return fail();
std::size_t v = 0;
for (const char c : f) {
if (c < '0' || c > '9') return fail();
const std::size_t digit = static_cast<std::size_t>(c - '0');
if (v > (std::numeric_limits<std::size_t>::max() - digit) / 10u)
return fail();
v = v * 10u + digit;
}
out = v;
return true;
}
bool Cursor::fieldDouble(double& out) {
std::string f;
if (!field(f)) return false;
const char* b = f.c_str();
char* end = nullptr;
double v = std::strtod(b, &end);
if (end != b + f.size()) return fail();
out = v;
return true;
}
} // namespace reasampler::wire
+88
View File
@@ -0,0 +1,88 @@
// core/wire — the ONE length-prefixed ext-state wire codec (Q-W1; audit
// T2-01(b)). Pure: standard library only — NO REAPER, NO SWELL, NO VST3.
//
// The `<decimal-len>':'<bytes>` field grammar ("one grammar across every
// ext-state seam") was previously implemented as three near-identical
// putField + Cursor copies (provenance / assignment_request / sample_usage)
// plus a fourth guarded decimal accumulate (bank_sync::parseBankGeneration) —
// and the copies drifted on the hardening. This is the single survivor,
// carrying the FULL hardening everywhere:
// - length digit-run capped at 20 (SIZE_MAX's decimal width) so a crafted
// digit run cannot accumulate past SIZE_MAX via repeated multiply;
// - overflow guard on every accumulate (multiply+add checked BEFORE applied);
// - subtraction-first bounds check so a huge len cannot wrap `start + len`;
// - fieldInt/fieldInt64 parse sign+digits manually with an INT64 overflow
// guard and an int range check — an out-of-range field FAILS the parse
// (closing the strtol errno/range gap the provenance copy carried).
//
// Wire formats on disk / ext-state are FROZEN: encode is byte-identical to the
// pre-collapse writers (std::to_string length + ':' + bytes), decode is
// tolerant-identical for every value a house writer can emit. "Never UB, never
// a partial value" is the parse-integrity promise.
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
namespace reasampler::wire {
// Append one length-prefixed field: <decimal-len> ':' <bytes>
void putField(std::string& out, const std::string& field);
// Whole-string, non-negative decimal parse WITHOUT exceptions or locale
// surprises (the bank_sync generation-stamp core). False on empty, any
// non-digit (incl. a leading '+'/'-'), or overflow past INT64_MAX; the
// accumulate is overflow-guarded so a pathologically long digit run can never
// wrap into a bogus small value.
bool parseUnsignedDecimal(const std::string& s, std::int64_t& out);
// Bounds-checked cursor over an encoded string. All reads are bounds-checked;
// any short read fails the whole parse (ok_ latches false — every subsequent
// read also fails, so a caller may check ok() once at the end).
class Cursor {
public:
explicit Cursor(const std::string& s) : s_(s) {}
bool ok() const { return ok_; }
bool atEnd() const { return pos_ >= s_.size(); }
// Consumes an exact literal at the cursor (the magic tag). Fails if absent.
bool literal(const char* lit);
// Reads one length-prefixed field into `out`. Fails on a missing ':', an
// empty or non-numeric length, a length that would overflow SIZE_MAX, or a
// length that runs past the end.
bool field(std::string& out);
// Length-prefixed signed 64-bit decimal (optional leading '-'). Digit run
// capped at 19 (INT64_MAX's decimal width); overflow fails the parse. A
// 20-digit negative (only INT64_MIN itself) is conservatively rejected —
// house writers emit generation timestamps and small enums, never that.
bool fieldInt64(std::int64_t& out);
// fieldInt64 narrowed to int; a value outside [INT_MIN, INT_MAX] FAILS the
// parse (the fixed form of the provenance copy's silent strtol narrowing).
bool fieldInt(int& out);
// Length-prefixed unsigned decimal (element counts). Digit run capped at
// 20; overflow-guarded accumulate. Callers still apply their own
// count-vs-wire-size sanity bound BEFORE any reserve() on the result.
bool fieldSizeT(std::size_t& out);
// Length-prefixed %.17g double. Full-token strtod; trailing bytes fail.
// Deliberately NO errno/ERANGE rejection: the writers emit %.17g of live
// doubles (incl. "inf"), and those must decode back — same accept set as
// every prior copy.
bool fieldDouble(double& out);
private:
bool fail() { ok_ = false; return false; }
const std::string& s_;
std::size_t pos_ = 0;
bool ok_ = true;
};
} // namespace reasampler::wire