code wiki / (root) / nx_diff.nx

nx_diff.nx source

↩ module page · 182 lines · 5596 B

1// nx_diff.nx -- byte-level binary diff (sovereign cmp / xxd | diff). 2// 3// The F6 reproducibility gate compares the SHA-256 of compiled 4// outputs. When a build fails the gate, you get "hashes differ" 5// and nothing else -- you have to dig in to find WHERE. This 6// module implements the digging: 7// 8// - cmp-style: first differing byte offset (or -1 if equal). 9// - hex-window: dump 16-byte windows around each difference so 10// you can eyeball whether the diff is endian, padding, a 11// symbol-name string, or a relocation patch. 12// - diff stats: total differing bytes + % similarity. 13// 14// Pairs with bench/f6_manifest.txt + bootstrap_proof.sh. When a 15// stage-N hash drifts, run nx_diff against the stage-1 baseline to 16// find what changed. 17// 18// Not a textual diff -- this is for binaries. For source-level 19// diffs use git diff. 20 21// nx_safety_envelope: 22// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 23// sil_target: SIL1 24// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 25// verdict: NOT_YET_EVALUATED 26 27import "syscalls.nx" 28 29// Returns the offset of the first differing byte, or -1 if a == b 30// over the first `len` bytes. Bails as soon as it finds one -- 31// O(len) worst case, O(1) common case for early divergence. 32func nx_diff_first(a: *u8, b: *u8, len: i64) -> i64 { 33 var i: i64 = 0 34 while i < len { 35 if a[i] != b[i] { return i } 36 i = i + 1 37 } 38 return -1 39} 40 41// Total count of differing bytes over `len`. Use for a similarity 42// percentage, not for early-exit. 43func nx_diff_count(a: *u8, b: *u8, len: i64) -> i64 { 44 var i: i64 = 0 45 var n: i64 = 0 46 while i < len { 47 if a[i] != b[i] { n = n + 1 } 48 i = i + 1 49 } 50 return n 51} 52 53// Print one byte as two hex chars to fd. 54func nx_diff_hex_byte(fd: i64, v: i64) -> i64 { 55 let buf: *u8 = sys_mmap(8) 56 let hi: i64 = (v >> 4) & 0xF 57 let lo: i64 = v & 0xF 58 if hi < 10 { buf[0] = 0x30 + hi } else { buf[0] = 0x57 + hi } 59 if lo < 10 { buf[1] = 0x30 + lo } else { buf[1] = 0x57 + lo } 60 sys_write(fd, buf, 2) 61 return 0 62} 63 64// Print `len` bytes from `p` as space-separated hex to fd. Replaces 65// non-printable bytes in a trailing ASCII column with `.`. 66func nx_diff_hex_window(fd: i64, p: *u8, len: i64) -> i64 { 67 var i: i64 = 0 68 while i < len { 69 nx_diff_hex_byte(fd, p[i]) 70 sys_write(fd, " " as *u8, 1) 71 i = i + 1 72 } 73 sys_write(fd, " | " as *u8, 3) 74 var j: i64 = 0 75 while j < len { 76 let c: i64 = p[j] 77 let one: *u8 = sys_mmap(4) 78 if c >= 0x20 { 79 if c < 0x7F { one[0] = c } else { one[0] = 0x2E } 80 } else { 81 one[0] = 0x2E 82 } 83 sys_write(fd, one, 1) 84 j = j + 1 85 } 86 return 0 87} 88 89// Print decimal i64 to fd. 90func nx_diff_dec(fd: i64, v: i64) -> i64 { 91 if v == 0 { sys_write(fd, "0" as *u8, 1); return 0 } 92 let buf: *u8 = sys_mmap(32) 93 var n: i64 = v 94 var neg: i64 = 0 95 if n < 0 { neg = 1; n = 0 - n } 96 var k: i64 = 0 97 while n > 0 { 98 buf[k] = 0x30 + (n - (n / 10) * 10) 99 n = n / 10 100 k = k + 1 101 } 102 if neg == 1 { sys_write(fd, "-" as *u8, 1) } 103 var j: i64 = k - 1 104 while j >= 0 { 105 sys_write(fd, (((buf as i64) + j) as *u8), 1) 106 j = j - 1 107 } 108 return 0 109} 110 111// Pretty-print: header + first-diff offset + 16-byte windows of 112// both buffers around the diff. Returns 0 if equal, 1 if differing. 113// Returns -1 if `len_a != len_b` (header still printed). 114func nx_diff_report(fd: i64, a: *u8, b: *u8, len_a: i64, len_b: i64) -> i64 { 115 sys_write(fd, "diff: " as *u8, 6) 116 nx_diff_dec(fd, len_a) 117 sys_write(fd, " vs " as *u8, 4) 118 nx_diff_dec(fd, len_b) 119 sys_write(fd, " bytes\n" as *u8, 7) 120 121 if len_a != len_b { 122 sys_write(fd, " (length mismatch)\n" as *u8, 20) 123 return -1 124 } 125 126 let off: i64 = nx_diff_first(a, b, len_a) 127 if off < 0 { 128 sys_write(fd, " identical\n" as *u8, 12) 129 return 0 130 } 131 132 sys_write(fd, " first diff at offset " as *u8, 23) 133 nx_diff_dec(fd, off) 134 sys_write(fd, "\n" as *u8, 1) 135 136 // Print a 16-byte window starting `lo` <= off, clamped to 137 // [0, len_a). Rounds down to 16-byte boundary for readability. 138 let lo: i64 = (off / 16) * 16 139 var win: i64 = 16 140 if lo + win > len_a { win = len_a - lo } 141 142 sys_write(fd, " A: " as *u8, 5) 143 nx_diff_hex_window(fd, (((a as i64) + lo) as *u8), win) 144 sys_write(fd, "\n" as *u8, 1) 145 146 sys_write(fd, " B: " as *u8, 5) 147 nx_diff_hex_window(fd, (((b as i64) + lo) as *u8), win) 148 sys_write(fd, "\n" as *u8, 1) 149 150 let total: i64 = nx_diff_count(a, b, len_a) 151 sys_write(fd, " total differing bytes: " as *u8, 25) 152 nx_diff_dec(fd, total) 153 sys_write(fd, "\n" as *u8, 1) 154 return 1 155} 156 157// ---- self-test --------------------------------------------------- 158 159func main() -> i64 { 160 let a: *u8 = sys_mmap(64) 161 let b: *u8 = sys_mmap(64) 162 var i: i64 = 0 163 while i < 64 { 164 a[i] = i 165 b[i] = i 166 i = i + 1 167 } 168 169 if nx_diff_first(a, b, 64) != -1 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 170 if nx_diff_count(a, b, 64) != 0 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 171 172 b[20] = 0xFF 173 b[33] = 0xAA 174 175 if nx_diff_first(a, b, 64) != 20 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 176 if nx_diff_count(a, b, 64) != 2 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 177 178 // Length-mismatch path (only checks return code). 179 if nx_diff_report(2, a, b, 64, 32) != -1 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 180 181 return 0 182}