code wiki / _hdl_build / nx_canon_bin.nx

nx_canon_bin.nx source

↩ module page · 108 lines · 5108 B

1// nx_canon_bin.nx -- BINARY-SAFE canonical records, built ON TOP of nx_canon_cid (the substrate pattern: do NOT 2// touch the foundation -- Rule 19 additive, Rule 3 no patch cascade; sibling of nx_uxf_cid). The shipped 3// canon_encode derives value length from strlen => STRING-ONLY (a value with a 0x00 byte truncates), so it cannot 4// carry image/audio/compressed-text payloads. This adds the SAME NXR1 canonical form (magic "NXR1" | u32be nfields 5// | per field KEY-SORTED: u32be klen | key | u32be vlen | value) with EXPLICIT value lengths => binary-safe. 6// Re-encoding the same fields reproduces identical bytes => identical cid_of() CID (audit 2026-06-22: this is the 7// primitive .nmz / the media container needs; candidate for the substrate ws to adopt as canon_encode_bin proper). 8// license_tier: ORIGINAL 9import "nx_syscalls.nx" 10import "nx_canon_cid.nx" // cid_of (reuse the substrate's sha256 CID; do not reimplement) 11 12func cb_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 13func cb_w32(p: *u8, o: i64, v: i64) -> i64 { p[o]=((v>>24)&0xff) as u8; p[o+1]=((v>>16)&0xff) as u8; p[o+2]=((v>>8)&0xff) as u8; p[o+3]=(v&0xff) as u8; return o+4 } 14func cb_be32(b: *u8, o: i64) -> i64 { return ((b[o] as i64)<<24)|((b[o+1] as i64)<<16)|((b[o+2] as i64)<<8)|(b[o+3] as i64) } 15// byte-lexicographic compare of NUL-terminated keys (same rule as nx_canon_cid.cc_cmp): <0 / 0 / >0 16func cb_cmp(a: *u8, b: *u8) -> i64 { var i: i64=0; while 1==1 { let ca: i64=a[i] as i64; let cb: i64=b[i] as i64; if ca!=cb { return ca-cb } if ca==0 { return 0 } i=i+1 } return 0 } 17 18// BINARY-SAFE encode: `n` fields (keys NUL-term text, vals raw bytes + explicit vlens) -> canonical NXR1 in out, 19// key-sorted so insertion order does NOT matter (1 logical record => 1 byte string => 1 CID). returns byte length. 20func canon_encode_bin(keys: *i64, vals: *i64, vlens: *i64, n: i64, out: *u8) -> i64 { 21 let idx: *i64 = sys_mmap(8 * (n + 2)) as *i64 22 var i: i64 = 0 23 while i < n { idx[i] = i; i = i + 1 } 24 i = 1 25 while i < n { 26 let cur: i64 = idx[i] 27 var j: i64 = i - 1 28 var go: i64 = 1 29 while go == 1 { 30 if j < 0 { go = 0 } 31 if go == 1 { if cb_cmp(keys[idx[j]] as *u8, keys[cur] as *u8) > 0 { idx[j+1] = idx[j]; j = j - 1 } else { go = 0 } } 32 } 33 idx[j+1] = cur 34 i = i + 1 35 } 36 out[0]=78 as u8; out[1]=88 as u8; out[2]=82 as u8; out[3]=49 as u8 // "NXR1" 37 var o: i64 = cb_w32(out, 4, n) 38 i = 0 39 while i < n { 40 let k: *u8 = keys[idx[i]] as *u8 41 let v: *u8 = vals[idx[i]] as *u8 42 let vl: i64 = vlens[idx[i]] 43 let kl: i64 = cb_slen(k) 44 o = cb_w32(out, o, kl) 45 var t: i64 = 0; while t < kl { out[o]=k[t]; o=o+1; t=t+1 } 46 o = cb_w32(out, o, vl) 47 t = 0; while t < vl { out[o]=v[t]; o=o+1; t=t+1 } 48 i = i + 1 49 } 50 return o 51} 52 53// BINARY-SAFE extract: copy the value of field `key` into out (cap); returns vlen, or -1 if absent / not NXR1. 54func canon_get_bin(m: *u8, n: i64, key: *u8, out: *u8, cap: i64) -> i64 { 55 if n < 8 { return 0-1 } 56 if m[0]!=(78 as u8) { return 0-1 } 57 if m[1]!=(88 as u8) { return 0-1 } 58 if m[2]!=(82 as u8) { return 0-1 } 59 if m[3]!=(49 as u8) { return 0-1 } 60 let count: i64 = cb_be32(m, 4) 61 let want: i64 = cb_slen(key) 62 var p: i64 = 8 63 var e: i64 = 0 64 while e < count { 65 if p + 4 > n { return 0-1 } 66 let kl: i64 = cb_be32(m, p); p = p + 4 67 var hit: i64 = 0 68 if kl == want { hit = 1; var t: i64=0; while t<kl { if m[p+t]!=key[t] { hit=0; t=kl } else { t=t+1 } } } 69 p = p + kl 70 if p + 4 > n { return 0-1 } 71 let vl: i64 = cb_be32(m, p); p = p + 4 72 if hit == 1 { var w: i64=0; while w<vl { if w<cap { out[w]=m[p+w] } w=w+1 } return vl } 73 p = p + vl 74 e = e + 1 75 } 76 return 0-1 77} 78 79// BINARY-SAFE full decode (the tolerant reader, binary version of nx_uxf_decode.canon_decode): out_keys[i] = ptr to 80// a NUL-term copy of the key, out_voff[i] = byte offset of the value WITHIN m, out_vlens[i] = value length. Values 81// stay IN m (no NUL-term, no truncation). returns nfields, or negative on malformed framing. 82func canon_decode_bin(m: *u8, n: i64, out_keys: *i64, out_voff: *i64, out_vlens: *i64, maxf: i64) -> i64 { 83 if n < 8 { return 0-1 } 84 if m[0]!=(78 as u8) { return 0-2 } 85 if m[1]!=(88 as u8) { return 0-2 } 86 if m[2]!=(82 as u8) { return 0-2 } 87 if m[3]!=(49 as u8) { return 0-2 } 88 let count: i64 = cb_be32(m, 4) 89 var p: i64 = 8 90 var e: i64 = 0 91 while e < count { 92 if e >= maxf { return e } 93 if p + 4 > n { return 0-4 } 94 let kl: i64 = cb_be32(m, p); p = p + 4 95 if p + kl + 4 > n { return 0-4 } 96 let kc: *u8 = sys_mmap(kl + 1) 97 var t: i64 = 0; while t < kl { kc[t]=m[p+t]; t=t+1 } kc[kl]=0 as u8 98 p = p + kl 99 let vl: i64 = cb_be32(m, p); p = p + 4 100 if p + vl > n { return 0-4 } 101 out_keys[e] = kc as i64 102 out_voff[e] = p 103 out_vlens[e] = vl 104 p = p + vl 105 e = e + 1 106 } 107 return count 108}