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}