code wiki / (root) / nx_matter_lib.nx

nx_matter_lib.nx source

↩ module page · 201 lines · 8213 B

1// nx_matter_lib.nx -- Nishi Office: Matter/Client SPINE (F502) core lib. 2// Client/matter/party records on the append-only immutable seg_store (nx_registry), 3// canonicalized + content-addressed (nx_canon_cid). The legal-SOTA feature = CONFLICTS 4// CHECK (RPC 1.7/1.9): scan every matter's parties for a name across the whole book. 5// LIB (no main) so the CLI organ AND the independent gate import the SAME shipped code. 6// SCALE-LAW (DECLARED): conflict scan is O(N-parties) linear (fail-closed, exact); a 7// party-name index = the flagged next rung, NOT a hidden cap. license_tier: ORIGINAL 8 9import "nx_canon_cid.nx" 10import "nx_registry.nx" 11import "nx_itoa_lib.nx" // THE shared MSB-first emitter -- mt_catn composes it (see below) 12const K_MAGIC_2048: i64 = 2048 13 14func mt_r32(p: *u8, off: i64) -> i64 { 15 let a: i64 = p[off] 16 let b: i64 = p[off + 1] 17 let c: i64 = p[off + 2] 18 let d: i64 = p[off + 3] 19 return (((((a << 8) | b) << 8) | c) << 8) | d 20} 21 22func mt_catcopy(dst: *u8, off: i64, s: *u8) -> i64 { 23 var i: i64 = 0 24 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 25 return off + i 26} 27 28// ★COMPOSES THE SHARED EMITTER (2026-08-15). This was a hand-rolled clone -- one of the ~87 the 29// nx_itoa_lib header counts -- and re-typing it had cost THREE separate defects, all of which the 30// incumbent already handled: 31// 1. NEGATIVES emitted ZERO BYTES. The digit loop was `while m > 0` with `m == 0` as the only 32// special case, so a negative rendered as an EMPTY FIELD. Caught live as `overdraw_rc=` with 33// nothing after it -- the value was -1, the REFUSAL SENTINEL, and the tooth asserting it was 34// PASSING throughout. ★★A NUMBER FORMATTER THAT EMITS NOTHING FOR A NEGATIVE TURNS AN ERROR CODE 35// INTO AN ABSENT FIELD, and an absent field reads as "not measured" rather than "measured, and 36// negative" -- the worst available direction for a BILLING library, because every positive 37// amount kept printing perfectly. 38// 2. i64 MIN. My first fix wrote `m = 0 - m`, which for INT64_MIN stays NEGATIVE, so the loop ran 39// zero times and it emitted a lone '-'. nxi_buf clamps that case explicitly. 40// 3. A LEAK: `sys_mmap(24)` PER CALL, never freed -- needed only because the old body built digits 41// least-significant-first and had to reverse through scratch. nxi_buf is MSB-first and allocates 42// NOTHING. That is the exact leak nx_itoa_lib was extracted to kill. 43// ★★★FIXING A CLONE BY PATCHING IT LEAVES THE OTHER 86 AND ADDS A NEW BUG; FIXING IT BY COMPOSING THE 44// INCUMBENT INHERITS EVERY GUARD THE INCUMBENT ALREADY EARNED. Contract is identical (digits only, no 45// NUL, returns the offset AFTER them), which is precisely why nxi_buf exists alongside ccz_cat_num. 46func mt_catn(dst: *u8, off: i64, v: i64) -> i64 { 47 return nxi_buf(dst, off, v) 48} 49 50func mt_streq(a: *u8, b: *u8) -> i64 { 51 var i: i64 = 0 52 while 1 == 1 { 53 if a[i] != b[i] { return 0 } 54 if a[i] == (0 as u8) { return 1 } 55 i = i + 1 56 } 57 return 1 58} 59 60// decode a field value from an NXR1 canonical record ("NXR1"|u32 n|(u32 kl|k|u32 vl|v)*) 61// into out (NUL-terminated); returns vlen or -1 if absent. 62func mt_field(rec: *u8, reclen: i64, key: *u8, keylen: i64, out: *u8) -> i64 { 63 let n: i64 = mt_r32(rec, 4) 64 var o: i64 = 8 65 var i: i64 = 0 66 while i < n { 67 let kl: i64 = mt_r32(rec, o) 68 o = o + 4 69 let kstart: i64 = o 70 o = o + kl 71 let vl: i64 = mt_r32(rec, o) 72 o = o + 4 73 let vstart: i64 = o 74 o = o + vl 75 if kl == keylen { 76 var mm: i64 = 1 77 var c: i64 = 0 78 while c < kl { if rec[kstart + c] != key[c] { mm = 0 } c = c + 1 } 79 if mm == 1 { 80 var d: i64 = 0 81 while d < vl { out[d] = rec[vstart + d]; d = d + 1 } 82 out[vl] = 0 as u8 83 return vl 84 } 85 } 86 i = i + 1 87 } 88 out[0] = 0 as u8 89 return 0 - 1 90} 91 92// build a canonical record from n (key,val) string pairs into out; returns len 93func mt_rec(keys: *i64, vals: *i64, n: i64, out: *u8) -> i64 { 94 return canon_encode(keys, vals, n, out) 95} 96 97func mt_client_put(prefix: *u8, id: *u8, name: *u8, email: *u8) -> i64 { 98 let k: *i64 = sys_mmap(8 * 3) as *i64 99 let v: *i64 = sys_mmap(8 * 3) as *i64 100 k[0] = ("name" as *u8) as i64 101 v[0] = name as i64 102 k[1] = ("email" as *u8) as i64 103 v[1] = email as i64 104 k[2] = ("type" as *u8) as i64 105 v[2] = ("client" as *u8) as i64 106 let rec: *u8 = sys_mmap(K_MAGIC_2048) 107 let rl: i64 = canon_encode(k, v, 3, rec) 108 return reg_put(prefix, "client:" as *u8, "client:__idx__" as *u8, id, rec, rl) 109} 110 111func mt_matter_put(prefix: *u8, mid: *u8, client: *u8, title: *u8, area: *u8) -> i64 { 112 let k: *i64 = sys_mmap(8 * 4) as *i64 113 let v: *i64 = sys_mmap(8 * 4) as *i64 114 k[0] = ("client" as *u8) as i64 115 v[0] = client as i64 116 k[1] = ("title" as *u8) as i64 117 v[1] = title as i64 118 k[2] = ("area" as *u8) as i64 119 v[2] = area as i64 120 k[3] = ("status" as *u8) as i64 121 v[3] = ("open" as *u8) as i64 122 let rec: *u8 = sys_mmap(K_MAGIC_2048) 123 let rl: i64 = canon_encode(k, v, 4, rec) 124 return reg_put(prefix, "matter:" as *u8, "matter:__idx__" as *u8, mid, rec, rl) 125} 126 127// party id = "<mid>:<seq>" 128func mt_party_add(prefix: *u8, mid: *u8, seq: i64, name: *u8, role: *u8) -> i64 { 129 let pid: *u8 = sys_mmap(128) 130 var o: i64 = mt_catcopy(pid, 0, mid) 131 pid[o] = 58 as u8 132 o = o + 1 133 o = mt_catn(pid, o, seq) 134 pid[o] = 0 as u8 135 let k: *i64 = sys_mmap(8 * 3) as *i64 136 let v: *i64 = sys_mmap(8 * 3) as *i64 137 k[0] = ("matter" as *u8) as i64 138 v[0] = mid as i64 139 k[1] = ("name" as *u8) as i64 140 v[1] = name as i64 141 k[2] = ("role" as *u8) as i64 142 v[2] = role as i64 143 let rec: *u8 = sys_mmap(K_MAGIC_2048) 144 let rl: i64 = canon_encode(k, v, 3, rec) 145 return reg_put(prefix, "party:" as *u8, "party:__idx__" as *u8, pid, rec, rl) 146} 147 148// CONFLICTS CHECK: count parties named `name` across ALL matters; append matching 149// matter ids (newline-sep) into matters. Returns the match count. 150func mt_conflict(prefix: *u8, name: *u8, matters: *u8) -> i64 { 151 // SIZE-TO-NEED: buffer derived from the index itself. The pair this replaces 152 // returned -1 once the party index passed 1 MiB, and -1 skips the walk below 153 // -- a CONFLICT CHECK would have reported NO CONFLICT because the index grew. 154 let idxbox: *i64 = sys_mmap(16) as *i64 155 let ilen: i64 = reg_index_read(prefix, "party:__idx__" as *u8, idxbox) 156 let idx: *u8 = idxbox[0] as *u8 157 let idb: *u8 = sys_mmap(256) 158 let po: *i64 = sys_mmap(16) as *i64 159 let lo: *i64 = sys_mmap(16) as *i64 160 let nmb: *u8 = sys_mmap(256) 161 let mtb: *u8 = sys_mmap(256) 162 var count: i64 = 0 163 var mo: i64 = 0 164 var ls: i64 = 0 165 var i: i64 = 0 166 while i <= ilen { 167 var eol: i64 = 0 168 if i == ilen { eol = 1 } 169 if i < ilen { if idx[i] == 10 as u8 { eol = 1 } } 170 if eol == 1 { 171 if i > ls { 172 var c: i64 = 0 173 while ls + c < i { idb[c] = idx[ls + c]; c = c + 1 } 174 idb[c] = 0 as u8 175 if reg_get(prefix, "party:" as *u8, idb, po, lo) == 1 { 176 let rec: *u8 = po[0] as *u8 177 mt_field(rec, lo[0], "name" as *u8, 4, nmb) 178 if mt_streq(nmb, name) == 1 { 179 mt_field(rec, lo[0], "matter" as *u8, 6, mtb) 180 mo = mt_catcopy(matters, mo, mtb) 181 matters[mo] = 10 as u8 182 mo = mo + 1 183 count = count + 1 184 } 185 } 186 } 187 ls = i + 1 188 } 189 i = i + 1 190 } 191 matters[mo] = 0 as u8 192 return count 193} 194 195// fetch matter field into out; returns vlen or -1 196func mt_matter_field(prefix: *u8, mid: *u8, field: *u8, flen: i64, out: *u8) -> i64 { 197 let po: *i64 = sys_mmap(16) as *i64 198 let lo: *i64 = sys_mmap(16) as *i64 199 if reg_get(prefix, "matter:" as *u8, mid, po, lo) != 1 { out[0] = 0 as u8; return 0 - 1 } 200 return mt_field(po[0] as *u8, lo[0], field, flen, out) 201}