code wiki / (root) / nx_bench_companion_tsv_parse.nx

nx_bench_companion_tsv_parse.nx source

↩ module page · 224 lines · 7963 B

1// nx_bench_companion_tsv_parse.nx -- read bench TSV rows back from bytes. 2// 3// Sibling to [[nx_bench_companion_tsv]] (the emitter). Closes the 4// persistence loop: rows written by the emitter can be read back from 5// disk + paired-diffed without re-running any measurement. 6// 7// Format contract (must match the emitter): 8// label<TAB>n_actors<TAB>completed<TAB>...<TAB>verdict<LF> 9// 20 columns total: label + 19 counter fields. 10// 11// API: 12// nx_bc_parse_tsv_row(buf, buf_len, 13// out_label, out_label_cap, out_label_len, 14// out_report) 15// -> bytes_consumed (>=0; offset just past trailing LF) or 16// NEGATIVE structured verdict 17// 18// Substrate-honest: parser refuses malformed input (missing fields, 19// non-decimal chars, label overflow) at the boundary rather than 20// silently filling out_report with junk. 21 22import "nx_syscalls.nx" 23import "nx_tier.nx" 24import "nx_bench_companion.nx" 25import "nx_bench_companion_tsv.nx" 26 27// ===== Sealed parse verdict ===================================== 28 29const NX_BCTP_OK: nx_int = 0 30const NX_BCTP_NULL_BUF: nx_int = 1 31const NX_BCTP_NULL_REPORT: nx_int = 2 32const NX_BCTP_MALFORMED_TAB: nx_int = 3 33const NX_BCTP_MALFORMED_LF: nx_int = 4 34const NX_BCTP_BAD_DECIMAL: nx_int = 5 35const NX_BCTP_LABEL_OVERFLOW: nx_int = 6 36const NX_BCTP_TOO_SHORT: nx_int = 7 37const NX_BCTP_INVALID: nx_int = 8 38const NX_BCTP_N: nx_int = 9 39 40func nx_bctp_v_is_valid(v: nx_int) -> nx_int { 41 if v < 0 { return 0 } 42 if v >= NX_BCTP_N { return 0 } 43 return 1 44} 45 46// ===== Byte predicates =========================================== 47 48func _bctp_is_digit(b: u8) -> nx_int { 49 if (b as nx_int) < 0x30 { return 0 } 50 if (b as nx_int) > 0x39 { return 0 } 51 return 1 52} 53 54// ===== Find next separator ======================================= 55// 56// Returns the offset of `sep` at or after `off`, or NEGATIVE if not 57// found before `end`. Used to walk the row TAB by TAB. 58 59func _bctp_find(buf: *u8, off: nx_int, end: nx_int, sep: nx_int) -> nx_int { 60 var i: nx_int = off 61 while i < end { 62 if (buf[i] as nx_int) == sep { return i } 63 i = i + 1 64 } 65 return 0 - 1 66} 67 68// ===== Parse decimal ASCII span -> i64 ========================== 69// 70// Parses buf[off..end_excl] as decimal ASCII (optional leading '-'). 71// Writes the parsed value to out_val[0]. Returns NX_BCTP_OK or 72// NX_BCTP_BAD_DECIMAL. 73 74func _bctp_parse_dec(buf: *u8, off: nx_int, end_excl: nx_int, 75 out_val: *i64) -> nx_int { 76 if end_excl <= off { return NX_BCTP_BAD_DECIMAL } 77 var i: nx_int = off 78 var neg: nx_int = 0 79 if (buf[i] as nx_int) == 0x2D { 80 neg = 1 81 i = i + 1 82 } 83 if i >= end_excl { return NX_BCTP_BAD_DECIMAL } 84 var acc: i64 = 0 85 while i < end_excl { 86 let b: u8 = buf[i] 87 if _bctp_is_digit(b) == 0 { return NX_BCTP_BAD_DECIMAL } 88 let d: i64 = (b as i64) - 0x30 89 acc = acc * 10 + d 90 i = i + 1 91 } 92 if neg == 1 { acc = 0 - acc } 93 out_val[0] = acc 94 return NX_BCTP_OK 95} 96 97// ===== Consume one TAB-terminated field as i64 ================== 98// 99// Reads decimal from buf[off..] until TAB, writes value, advances 100// offset to byte after TAB. Returns updated offset or NEGATIVE 101// verdict. 102 103func _bctp_consume_field_i64(buf: *u8, off: nx_int, end: nx_int, 104 out_val: *i64) -> nx_int { 105 let tab_at: nx_int = _bctp_find(buf, off, end, 0x09) 106 if tab_at < 0 { return 0 - NX_BCTP_MALFORMED_TAB } 107 let v: nx_int = _bctp_parse_dec(buf, off, tab_at, out_val) 108 if v != NX_BCTP_OK { return 0 - v } 109 return tab_at + 1 110} 111 112// ===== Public: parse one row ==================================== 113 114func nx_bc_parse_tsv_row(buf: *u8, buf_len: nx_int, 115 out_label: *u8, out_label_cap: nx_int, 116 out_label_len: *i64, 117 out_report: *NxBenchReport) -> nx_int { 118 if (buf as i64) == 0 { return 0 - NX_BCTP_NULL_BUF } 119 if (out_report as i64) == 0 { return 0 - NX_BCTP_NULL_REPORT } 120 if buf_len <= 0 { return 0 - NX_BCTP_TOO_SHORT } 121 122 // Find label\t 123 let label_end: nx_int = _bctp_find(buf, 0, buf_len, 0x09) 124 if label_end < 0 { return 0 - NX_BCTP_MALFORMED_TAB } 125 let label_len_raw: nx_int = label_end - 0 126 if (out_label as i64) != 0 { 127 if label_len_raw > out_label_cap { return 0 - NX_BCTP_LABEL_OVERFLOW } 128 var li: nx_int = 0 129 while li < label_len_raw { 130 out_label[li] = buf[li] 131 li = li + 1 132 } 133 } 134 if (out_label_len as i64) != 0 { out_label_len[0] = label_len_raw as i64 } 135 136 // Scratch i64 for field reads 137 let val_buf: *u8 = sys_mmap(8) 138 let val_p: *i64 = val_buf as *i64 139 140 var off: nx_int = label_end + 1 141 142 // 19 counter fields, all TAB-terminated EXCEPT the last (LF-terminated). 143 // n_actors 144 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 145 if off < 0 { return off } 146 out_report.n_actors = val_p[0] as nx_int 147 148 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 149 if off < 0 { return off } 150 out_report.actors_completed = val_p[0] as nx_int 151 152 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 153 if off < 0 { return off } 154 out_report.actors_failed = val_p[0] as nx_int 155 156 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 157 if off < 0 { return off } 158 out_report.actors_running = val_p[0] as nx_int 159 160 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 161 if off < 0 { return off } 162 out_report.actors_waiting = val_p[0] as nx_int 163 164 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 165 if off < 0 { return off } 166 out_report.total_actor_steps = val_p[0] as nx_int 167 168 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 169 if off < 0 { return off } 170 out_report.cumulative_runtime_us = val_p[0] as nx_size 171 172 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 173 if off < 0 { return off } 174 out_report.n_subscriptions = val_p[0] as nx_int 175 176 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 177 if off < 0 { return off } 178 out_report.mux_pending = val_p[0] as nx_int 179 180 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 181 if off < 0 { return off } 182 out_report.arb_grants_full = val_p[0] as nx_int 183 184 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 185 if off < 0 { return off } 186 out_report.arb_grants_partial = val_p[0] as nx_int 187 188 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 189 if off < 0 { return off } 190 out_report.arb_denials = val_p[0] as nx_int 191 192 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 193 if off < 0 { return off } 194 out_report.arb_preemptions = val_p[0] as nx_int 195 196 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 197 if off < 0 { return off } 198 out_report.broker_grants = val_p[0] as nx_int 199 200 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 201 if off < 0 { return off } 202 out_report.broker_refusals = val_p[0] as nx_int 203 204 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 205 if off < 0 { return off } 206 out_report.broker_deadline_misses = val_p[0] as nx_int 207 208 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 209 if off < 0 { return off } 210 out_report.epoch_count = val_p[0] as nx_int 211 212 off = _bctp_consume_field_i64(buf, off, buf_len, val_p) 213 if off < 0 { return off } 214 out_report.captured_at_us = val_p[0] as nx_size 215 216 // Last field: verdict, LF-terminated 217 let lf_at: nx_int = _bctp_find(buf, off, buf_len, 0x0A) 218 if lf_at < 0 { return 0 - NX_BCTP_MALFORMED_LF } 219 let v_parse: nx_int = _bctp_parse_dec(buf, off, lf_at, val_p) 220 if v_parse != NX_BCTP_OK { return 0 - v_parse } 221 out_report.verdict = val_p[0] as nx_int 222 223 return lf_at + 1 // bytes consumed = offset just past the LF 224}