nx_record_tsv.nx
buildroot/runtime/nx_record_tsv.nx
about
nx_record_tsv.nx -- the TSV <-> NXR1 COMPATIBILITY BRIDGE. This is what makes seq1326 rung 2 (adoption)
possible WITHOUT a flag day.
THE ADOPTION PROBLEM: every plane's decoder today splits on TAB and counts columns by position. Changing
the physical record format breaks all of them at once, so "migrate the plane" has meant "rewrite every
reader in the same commit" -- which is why the retirement never happened and 289 files are still TSV.
THE FIX is the same one Iceberg uses for format evolution: separate the PHYSICAL encoding from the
LOGICAL view. Store NXR1 (typed, id-addressed, delimiter-free). Render a TSV line ON DEMAND for any
reader that still wants one. New writers and new readers use the typed API and get all four structural
properties; legacy readers keep working byte-for-byte (rule 19) until they are migrated one at a time.
★THE COMPATIBILITY VIEW MUST REFUSE TO LIE. If a stored record holds a value TSV cannot represent -- one
containing a TAB or a NEWLINE -- rtv_to_tsv REFUSES with a negative code instead of emitting a line that
would silently parse as extra columns or an extra row. A lossy view that fails loudly is a bridge; a
lossy view that fails silently is the corruption class we are retiring.
COLUMN <-> FIELD ID: column i (0-based) maps to field_id i+1. 1-based so id 0 stays reserved, and STABLE:
once a column has an id, reordering or dropping columns never re-points an existing id.
EMPTY vs ABSENT: a TSV empty column round-trips as a PRESENT field of length 0, which is a real semantic
gain -- NXR1 can distinguish "known to be empty" from "not recorded". The reverse direction cannot:
rendering collapses absent and empty to the same empty column, because TSV has no way to say otherwise.
That asymmetry is inherent to TSV and is documented here rather than hidden.
license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 1 imports · 4 importers
imports: nx_record.nx
imported by: nx_plane_check.nxnx_plane_migrate.nxnx_record_tsv_gate.nxnx_store_seed_lib.nx
structs
| none |
consts
| 28 | const RTV_TAB: i64 = 9 |
| 29 | const RTV_NL: i64 = 10 |
| 30 | const RTV_MINUS: i64 = 45 |
| 31 | const RTV_ZERO: i64 = 48 |
| 32 | const RTV_NINE: i64 = 57 |
| 33 | const RTV_BASE10: i64 = 10 |
| 34 | const RTV_NUMBUF: i64 = 32 |
| 35 | const RTV_OUTS: i64 = 32 |
| 36 | const RTV_ERR_TAB_IN_VALUE: i64 = 0 - 1 // record holds a byte TSV cannot represent |
| 37 | const RTV_ERR_CAP: i64 = 0 - 2 // rendered line would exceed the caller buffer |
| 38 | const RTV_MAXCOL: i64 = 512 |
functions
| 43 | func rtv_atoi(p: *u8, n: i64) -> i64 called by 1: rtv_to_nxr |
| 58 | func rtv_itoa(out: *u8, off: i64, v: i64) -> i64 |
| 74 | func rtv_to_nxr(line: *u8, n: i64, types: *u8, ntypes: i64, out: *u8) -> i64 |
| 102 | func rtv_to_tsv(rec: *u8, n: i64, ncols: i64, out: *u8, outcap: i64) -> i64 |