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}