code wiki / _hdl_build / nx_inventory.nx

nx_inventory.nx source

↩ module page · 317 lines · 12407 B

1// nx_inventory.nx -- LIB: the UNIVERSAL inventory / supply-chain substrate (operator: "the inventory and other 2// systems hardware-rung-up to manage any and all supply chains -- cataloging for a will all the things a person 3// owns, what's in the pantry and when it's expiring, what can I make, meal planning"). ONE domain-agnostic item 4// store; the pantry, an estate/will asset catalog, and any other supply chain are just different `domain`s over 5// the same engine (the inventory analogue of the universal builder). Sovereign (seg-store, NO TSV), INTEGER 6// only (quantities are counts; money is CENTS -- no float), additive (history kept; consume = a new version). 7// Schema (prefix passed in, e.g. knowledge/store/inv-): 8// inv:<domain>:ids -> TAB list of item ids in the domain 9// inv:<domain>:item:<id> -> name <t> category <t> qty <t> unit <t> location <t> value_cents <t> expiry_day <t> owner 10// (expiry_day = integer day number; 0 = non-perishable. owner = beneficiary for a will, or "" for a pantry.) 11// license_tier: ORIGINAL 12import "nx_seg_store.nx" 13import "nx_ad_serve.nx" 14import "nx_syscalls.nx" 15 16const IV_TAB: i64 = 9 17 18// ---- small helpers ---- 19func iv_field(rec: *u8, rlen: i64, f: i64, out: *u8) -> i64 { 20 var cur: i64 = 0 21 var i: i64 = 0 22 var o: i64 = 0 23 while cur < f { 24 if i >= rlen { out[0] = 0 as u8; return 0 } 25 if rec[i] == (IV_TAB as u8) { cur = cur + 1 } 26 i = i + 1 27 } 28 var go: i64 = 1 29 while go == 1 { 30 if i >= rlen { go = 0 } else { 31 if rec[i] == (IV_TAB as u8) { go = 0 } else { out[o] = rec[i]; o = o + 1; i = i + 1 } 32 } 33 } 34 out[o] = 0 as u8 35 return o 36} 37func iv_atoi(s: *u8, slen: i64) -> i64 { 38 var v: i64 = 0 39 var i: i64 = 0 40 var neg: i64 = 0 41 if slen > 0 { if s[0] == (45 as u8) { neg = 1; i = 1 } } 42 while i < slen { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } 43 if neg == 1 { return 0 - v } 44 return v 45} 46func iv_num(dst: *u8, off: i64, v: i64) -> i64 { 47 var m: i64 = v 48 var o: i64 = off 49 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m } 50 let t: *u8 = sys_mmap(28); var k: i64 = 0 51 if m == 0 { t[0] = 48 as u8; k = 1 } 52 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 53 var i: i64 = 0 54 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 55 return o + k 56} 57// append "$D.CC" for an integer cents amount (no float) 58func iv_money(dst: *u8, off: i64, cents: i64) -> i64 { 59 var o: i64 = off 60 dst[o] = 36 as u8; o = o + 1 // '$' 61 o = iv_num(dst, o, cents / 100) 62 dst[o] = 46 as u8; o = o + 1 // '.' 63 let r: i64 = cents % 100 64 dst[o] = (48 + (r / 10)) as u8; o = o + 1 65 dst[o] = (48 + (r % 10)) as u8; o = o + 1 66 return o 67} 68 69func iv_item_key(domain: *u8, id: *u8, out: *u8) -> i64 { 70 var o: i64 = 0 71 o = as_append(out, o, "inv:" as *u8) 72 o = as_append(out, o, domain) 73 o = as_append(out, o, ":item:" as *u8) 74 o = as_append(out, o, id) 75 out[o] = 0 as u8 76 return o 77} 78func iv_ids_key(domain: *u8, out: *u8) -> i64 { 79 var o: i64 = 0 80 o = as_append(out, o, "inv:" as *u8) 81 o = as_append(out, o, domain) 82 o = as_append(out, o, ":ids" as *u8) 83 out[o] = 0 as u8 84 return o 85} 86func iv_seg_next(prefix: *u8) -> i64 { 87 let segs: *i64 = sys_mmap(8 * 260) as *i64 88 let nseg: i64 = ss_manifest(prefix, segs) 89 if nseg < 0 { return 1 } 90 return 1 + nseg 91} 92func iv_streq_store(prefix: *u8, key: *u8, val: *u8, vlen: i64) -> i64 { 93 let pq: *i64 = sys_mmap(16) as *i64 94 let lq: *i64 = sys_mmap(16) as *i64 95 if ss_get(prefix, key, pq, lq) != 1 { return 0 } 96 let b: *u8 = pq[0] as *u8 97 let n: i64 = lq[0] 98 if n != vlen { return 0 } 99 var i: i64 = 0 100 while i < n { if b[i] != val[i] { return 0 } i = i + 1 } 101 return 1 102} 103// 1 if id is one of the TAB-separated tokens in buf[0..len) 104func iv_id_in_list(buf: *u8, len: i64, id: *u8) -> i64 { 105 let idl: i64 = as_len(id) 106 var i: i64 = 0 107 var ls: i64 = 0 108 while i <= len { 109 var sep: i64 = 0 110 if i == len { sep = 1 } else { if buf[i] == (IV_TAB as u8) { sep = 1 } } 111 if sep == 1 { 112 let tl: i64 = i - ls 113 if tl == idl { 114 var m: i64 = 1 115 var t: i64 = 0 116 while t < tl { if buf[ls + t] != id[t] { m = 0; t = tl } else { t = t + 1 } } 117 if m == 1 { return 1 } 118 } 119 ls = i + 1 120 } 121 i = i + 1 122 } 123 return 0 124} 125 126// ---- core: get / fields ---- 127func iv_get(prefix: *u8, domain: *u8, id: *u8, ptrout: *i64, lenout: *i64) -> i64 { 128 let ikey: *u8 = sys_mmap(96) 129 iv_item_key(domain, id, ikey) 130 return ss_get(prefix, ikey, ptrout, lenout) 131} 132// extract field f of an item into out; 1 if the item exists, else 0 133func iv_field_of(prefix: *u8, domain: *u8, id: *u8, f: i64, out: *u8) -> i64 { 134 let pq: *i64 = sys_mmap(16) as *i64 135 let lq: *i64 = sys_mmap(16) as *i64 136 if iv_get(prefix, domain, id, pq, lq) != 1 { return 0 } 137 iv_field(pq[0] as *u8, lq[0], f, out) 138 return 1 139} 140func iv_int_field(prefix: *u8, domain: *u8, id: *u8, f: i64) -> i64 { 141 let out: *u8 = sys_mmap(32) 142 if iv_field_of(prefix, domain, id, f, out) == 0 { return 0 - 1 } 143 return iv_atoi(out, as_len(out)) 144} 145func iv_qty(prefix: *u8, domain: *u8, id: *u8) -> i64 { return iv_int_field(prefix, domain, id, 2) } 146func iv_value(prefix: *u8, domain: *u8, id: *u8) -> i64 { return iv_int_field(prefix, domain, id, 5) } 147func iv_expiry(prefix: *u8, domain: *u8, id: *u8) -> i64 { return iv_int_field(prefix, domain, id, 6) } 148 149// ---- catalog: add/update an item (idempotent skip-if-unchanged; keeps the ids index in sync) ---- 150func iv_add(prefix: *u8, domain: *u8, id: *u8, name: *u8, cat: *u8, qty: i64, unit: *u8, loc: *u8, value: i64, expiry: i64, owner: *u8) -> i64 { 151 let val: *u8 = sys_mmap(512) 152 var o: i64 = 0 153 o = as_append(val, o, name); val[o] = IV_TAB as u8; o = o + 1 154 o = as_append(val, o, cat); val[o] = IV_TAB as u8; o = o + 1 155 o = iv_num(val, o, qty); val[o] = IV_TAB as u8; o = o + 1 156 o = as_append(val, o, unit); val[o] = IV_TAB as u8; o = o + 1 157 o = as_append(val, o, loc); val[o] = IV_TAB as u8; o = o + 1 158 o = iv_num(val, o, value); val[o] = IV_TAB as u8; o = o + 1 159 o = iv_num(val, o, expiry); val[o] = IV_TAB as u8; o = o + 1 160 o = as_append(val, o, owner) 161 let vlen: i64 = o 162 val[o] = 0 as u8 163 let ikey: *u8 = sys_mmap(96) 164 iv_item_key(domain, id, ikey) 165 if iv_streq_store(prefix, ikey, val, vlen) == 1 { return 0 } 166 let idskey: *u8 = sys_mmap(64) 167 iv_ids_key(domain, idskey) 168 let pq: *i64 = sys_mmap(16) as *i64 169 let lq: *i64 = sys_mmap(16) as *i64 170 let newids: *u8 = sys_mmap(8192) 171 var nio: i64 = 0 172 var idschanged: i64 = 1 173 if ss_get(prefix, idskey, pq, lq) == 1 { 174 let cur: *u8 = pq[0] as *u8 175 let cl: i64 = lq[0] 176 var t: i64 = 0 177 while t < cl { newids[t] = cur[t]; t = t + 1 } 178 nio = cl 179 if iv_id_in_list(cur, cl, id) == 1 { idschanged = 0 } else { newids[nio] = IV_TAB as u8; nio = nio + 1; nio = as_append(newids, nio, id) } 180 } else { 181 nio = as_append(newids, 0, id) 182 } 183 let w: *i64 = ss_begin() 184 ss_add(w, 1, ikey, val, vlen) 185 if idschanged == 1 { newids[nio] = 0 as u8; ss_add(w, 1, idskey, newids, nio) } 186 let seg: i64 = iv_seg_next(prefix) 187 ss_commit(prefix, w, seg) 188 return 1 189} 190 191// list the item ids of a domain into outids (ptrs to fresh copies); returns count 192func iv_list(prefix: *u8, domain: *u8, outids: *i64) -> i64 { 193 let idskey: *u8 = sys_mmap(64) 194 iv_ids_key(domain, idskey) 195 let pq: *i64 = sys_mmap(16) as *i64 196 let lq: *i64 = sys_mmap(16) as *i64 197 if ss_get(prefix, idskey, pq, lq) != 1 { return 0 } 198 let buf: *u8 = pq[0] as *u8 199 let len: i64 = lq[0] 200 var n: i64 = 0 201 var i: i64 = 0 202 var ls: i64 = 0 203 while i <= len { 204 var sep: i64 = 0 205 if i == len { sep = 1 } else { if buf[i] == (IV_TAB as u8) { sep = 1 } } 206 if sep == 1 { 207 let tl: i64 = i - ls 208 if tl > 0 { 209 let tok: *u8 = sys_mmap(48) 210 var t: i64 = 0 211 while t < tl { tok[t] = buf[ls + t]; t = t + 1 } 212 tok[tl] = 0 as u8 213 outids[n] = tok as i64 214 n = n + 1 215 } 216 ls = i + 1 217 } 218 i = i + 1 219 } 220 return n 221} 222 223// total value (cents) across a domain -- the will/estate net worth 224func iv_value_total(prefix: *u8, domain: *u8) -> i64 { 225 let ids: *i64 = sys_mmap(8 * 256) as *i64 226 let n: i64 = iv_list(prefix, domain, ids) 227 var tot: i64 = 0 228 var i: i64 = 0 229 while i < n { tot = tot + iv_value(prefix, domain, ids[i] as *u8); i = i + 1 } 230 return tot 231} 232 233// items expiring within `within` days of `as_of` (skips non-perishables, expiry==0) -> outids; returns count 234func iv_expiring(prefix: *u8, domain: *u8, as_of: i64, within: i64, outids: *i64) -> i64 { 235 let ids: *i64 = sys_mmap(8 * 256) as *i64 236 let n: i64 = iv_list(prefix, domain, ids) 237 var c: i64 = 0 238 var i: i64 = 0 239 while i < n { 240 let e: i64 = iv_expiry(prefix, domain, ids[i] as *u8) 241 if e > 0 { if e <= as_of + within { outids[c] = ids[i]; c = c + 1 } } 242 i = i + 1 243 } 244 return c 245} 246 247// consume n units of an item (supply-chain depletion). Returns the new qty, or -1 if absent / insufficient. 248func iv_consume(prefix: *u8, domain: *u8, id: *u8, nconsume: i64) -> i64 { 249 let pq: *i64 = sys_mmap(16) as *i64 250 let lq: *i64 = sys_mmap(16) as *i64 251 if iv_get(prefix, domain, id, pq, lq) != 1 { return 0 - 1 } 252 let rec: *u8 = pq[0] as *u8 253 let rl: i64 = lq[0] 254 let f: *u8 = sys_mmap(64) 255 let name: *u8 = sys_mmap(128); iv_field(rec, rl, 0, name) 256 let cat: *u8 = sys_mmap(64); iv_field(rec, rl, 1, cat) 257 let ql: i64 = iv_field(rec, rl, 2, f); let qty: i64 = iv_atoi(f, ql) 258 let unit: *u8 = sys_mmap(32); iv_field(rec, rl, 3, unit) 259 let loc: *u8 = sys_mmap(64); iv_field(rec, rl, 4, loc) 260 let vl: i64 = iv_field(rec, rl, 5, f); let value: i64 = iv_atoi(f, vl) 261 let el: i64 = iv_field(rec, rl, 6, f); let expiry: i64 = iv_atoi(f, el) 262 let owner: *u8 = sys_mmap(64); iv_field(rec, rl, 7, owner) 263 if qty < nconsume { return 0 - 1 } 264 let newqty: i64 = qty - nconsume 265 iv_add(prefix, domain, id, name, cat, newqty, unit, loc, value, expiry, owner) 266 return newqty 267} 268 269// ---- reorder / par levels (the supply-chain management layer) ------------------------------------------ 270// A par level is the desired minimum stock; reorder when qty < par, buying (par - qty). Stored separately 271// (inv:<domain>:par:<id>) so the item schema is untouched. par 0 / absent = not tracked for reorder. 272func iv_par_key(domain: *u8, id: *u8, out: *u8) -> i64 { 273 var o: i64 = 0 274 o = as_append(out, o, "inv:" as *u8) 275 o = as_append(out, o, domain) 276 o = as_append(out, o, ":par:" as *u8) 277 o = as_append(out, o, id) 278 out[o] = 0 as u8 279 return o 280} 281func iv_set_par(prefix: *u8, domain: *u8, id: *u8, par: i64) -> i64 { 282 let key: *u8 = sys_mmap(96) 283 iv_par_key(domain, id, key) 284 let val: *u8 = sys_mmap(24) 285 let vlen: i64 = iv_num(val, 0, par) 286 val[vlen] = 0 as u8 287 if iv_streq_store(prefix, key, val, vlen) == 1 { return 0 } 288 let w: *i64 = ss_begin() 289 ss_add(w, 1, key, val, vlen) 290 let seg: i64 = iv_seg_next(prefix) 291 ss_commit(prefix, w, seg) 292 return 1 293} 294func iv_get_par(prefix: *u8, domain: *u8, id: *u8) -> i64 { 295 let key: *u8 = sys_mmap(96) 296 iv_par_key(domain, id, key) 297 let pq: *i64 = sys_mmap(16) as *i64 298 let lq: *i64 = sys_mmap(16) as *i64 299 if ss_get(prefix, key, pq, lq) != 1 { return 0 } 300 return iv_atoi(pq[0] as *u8, lq[0]) 301} 302// items needing reorder (qty < par, par>0) -> out_ids + out_need (= par - qty); returns count 303func iv_low_stock(prefix: *u8, domain: *u8, out_ids: *i64, out_need: *i64) -> i64 { 304 let ids: *i64 = sys_mmap(8 * 256) as *i64 305 let n: i64 = iv_list(prefix, domain, ids) 306 var c: i64 = 0 307 var i: i64 = 0 308 while i < n { 309 let par: i64 = iv_get_par(prefix, domain, ids[i] as *u8) 310 if par > 0 { 311 let q: i64 = iv_qty(prefix, domain, ids[i] as *u8) 312 if q < par { out_ids[c] = ids[i]; out_need[c] = par - q; c = c + 1 } 313 } 314 i = i + 1 315 } 316 return c 317}