nx_asset_signals.nx source
↩ module page · 441 lines · 22826 B
1// nx_asset_signals.nx -- "WHAT WE NEED TO DO" pillar (R4 of the universal organization-tooling arc).
2//
3// Derives TASK SIGNALS from CATALOG STATE -- "what to do" is not a hand-maintained list, it is a set of
4// PREDICATES evaluated over the inventory snapshot (org_research.tsv CONFIRMED: task-signals-derived,
5// dam-lifecycle). A signal is a deterministic fact about a record that implies follow-up work
6// (provenance missing, untagged, metadata gap, near-duplicate, stale, orphaned, un-transcoded). The
7// derived signals are then EMITTED as work items into a DEDICATED, NAMESPACED workstream store so the
8// live 1265-stream production board (prefix "knowledge/store/ws-") is NEVER disturbed (ADDITIVE law #13).
9//
10// ZERO NEW STORAGE / ZERO NEW IDENTITY / ZERO NEW CRYPTO -- pure COMPOSITION (Rule 15, DRY):
11// * catalog enumeration + bytes -> nx_asset_catalog (cat_list / cat_get / cat_count)
12// * record field access -> nx_asset_record (ar_decode / ar_get : tags, is_current,
13// relations, type, width, height,
14// encoding_format, lifecycle_stage)
15// * cryptographic provenance check -> nx_asset_provenance(prov_verify : a record HAS a verifiable
16// R2 credential iff prov_verify==1)
17// * perceptual near-dup grouping -> nx_asset_autotag (at_neardup over per-record dHashes)
18// * work-item write/list (NAMESPACED)-> nx_seg_store (ss_get / ss_begin / ss_add / ss_commit /
19// ss_open / ss_hget) into a DEDICATED prefix
20//
21// WMS WIRING / PRODUCTION CUTOVER SEAM:
22// The emitted work items use the EXACT record contract of nx_workstream_store (key "sig:<cid>:<code>",
23// a TAB-separated value record, and a "ws:ids" enumeration index), written over the SAME substrate the
24// WMS itself is built on (ss_*). We do NOT import nx_workstream_store directly: it lives in
25// runtime/_hdl_build/ and the sovereign import resolver resolves transitive imports relative to the
26// importing file's directory, so a runtime/ organ cannot reach a _hdl_build/ organ (verified: importing
27// it -> "expand_imports failed"). Emitting straight onto ss_* with the dedicated prefix is therefore
28// the clean isolation: the live board store (WS_PREFIX="knowledge/store/ws-") is a DIFFERENT store and
29// is never opened, read, or written here. THE CUTOVER: when the signals organ is co-located with the
30// WMS (move both under one dir, or expose nx_workstream_store from runtime/), as_emit's ss_* calls map
31// 1:1 onto ws_put_p(AS_PREFIX(), key, val) and as_list onto ws_manifest_p(AS_PREFIX(), ...) -- same
32// keys, same TAB record, same ws:ids index -- so the production daemon can adopt them unchanged.
33//
34// PURITY: as_scan is a pure/deterministic function of the catalog snapshot PLUS caller-supplied parallel
35// auxiliary arrays (perceptual hashes + provenance credentials), because two of the seven predicates
36// CANNOT be answered from the record bytes alone and we refuse to fabricate the answer (Rule 4 / honesty):
37// * NEAR_DUP needs a PERCEPTUAL fingerprint -- the record stores none (a dHash is over the pixels, the
38// decode step is the caller's, per nx_asset_autotag's documented boundary). phashes[i] is record i's
39// dHash, or 0 to mean "no fingerprint available -> cannot be a near-dup".
40// * MISSING_PROVENANCE needs the record's CREDENTIAL + the trusted platform pubkey -- a record alone is
41// unverifiable. cred_ptr[i]/cred_len[i] point at record i's R2 credential (len 0 = none); a record
42// "has verifiable provenance" IFF prov_verify(cred, len, pubkey, cid)==1 (deny-by-default).
43// The other five predicates (UNTAGGED / MISSING_METADATA / STALE / ORPHANED / UNTRANSCODED) are pure
44// over the decoded record fields. Aux arrays are indexed PARALLEL to cat_list order (snapshot order).
45//
46// No hardware/persistent-firmware writes (Rule 26). license_tier: ORIGINAL
47import "nx_syscalls.nx"
48import "nx_asset_record.nx"
49import "nx_asset_catalog.nx"
50import "nx_asset_provenance.nx"
51import "nx_asset_autotag.nx"
52import "nx_seg_store.nx"
53const K_MAGIC_4096: i64 = 4096
54
55// ---- SIGNAL CODE vocabulary (data-driven label set, one definition -- Rule 11, no magic strings) ----
56// Each code is the machine-readable reason a record implies follow-up work.
57func SIG_MISSING_PROVENANCE() -> *u8 { return "MISSING_PROVENANCE\x00" as *u8 }
58func SIG_UNTAGGED() -> *u8 { return "UNTAGGED\x00" as *u8 }
59func SIG_MISSING_METADATA() -> *u8 { return "MISSING_METADATA\x00" as *u8 }
60func SIG_NEAR_DUP() -> *u8 { return "NEAR_DUP\x00" as *u8 }
61func SIG_STALE() -> *u8 { return "STALE\x00" as *u8 }
62func SIG_ORPHANED() -> *u8 { return "ORPHANED\x00" as *u8 }
63func SIG_UNTRANSCODED() -> *u8 { return "UNTRANSCODED\x00" as *u8 }
64
65// near-dup threshold (STRICT < threshold): 1 == "Hamming 0 only" = perceptually identical fingerprints.
66// Data-driven so a future caller can loosen it without a code edit (Rule 11).
67func AS_NEARDUP_THRESHOLD() -> i64 { return 1 }
68
69// the DEDICATED, isolated namespace prefix for emitted asset-signal work items. DISTINCT from the
70// production board prefix WS_PREFIX="knowledge/store/ws-" by construction, so emission is purely additive
71// and CANNOT pollute the live workstream board (the load-bearing isolation guarantee of this rung).
72func AS_PREFIX() -> *u8 { return "knowledge/store/asset-signals-\x00" as *u8 }
73
74// ---- small helpers ----
75func as_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
76func as_streq(a: *u8, b: *u8) -> i64 {
77 var i: i64 = 0
78 while 1 == 1 {
79 if a[i] != b[i] { return 0 }
80 if a[i] == (0 as u8) { return 1 }
81 i = i + 1
82 }
83 return 1
84}
85// present = non-null pointer AND non-empty string (mirrors ar_present semantics for decoded fields)
86func as_present(v: *u8) -> i64 {
87 if (v as i64) == 0 { return 0 }
88 if v[0] == (0 as u8) { return 0 }
89 return 1
90}
91// copy src -> dst (NUL-terminated), returns length copied
92func as_cpy(dst: *u8, src: *u8) -> i64 {
93 var i: i64 = 0
94 while src[i] != (0 as u8) { dst[i] = src[i]; i = i + 1 }
95 dst[i] = 0 as u8
96 return i
97}
98// append src at dst[off] (NUL-terminated result), returns new offset
99func as_cat(dst: *u8, off: i64, src: *u8) -> i64 {
100 var i: i64 = 0
101 while src[i] != (0 as u8) { dst[off + i] = src[i]; i = i + 1 }
102 dst[off + i] = 0 as u8
103 return off + i
104}
105
106// ---- minimal WMS-contract write/list over ss_* (the production cutover seam, see header) ----
107// next segment id for a commit under `prefix` = fresh max+1 (ss_next_segid, UNCAPPED; mirrors ws_seg_next).
108// was 1 + capped ss_manifest count -> clobbered the cap segment past the cap. Empty store stays 1-based.
109func as_seg_next(prefix: *u8) -> i64 {
110 let n: i64 = ss_next_segid(prefix)
111 if n < 1 { return 1 }
112 return n
113}
114
115// as_put: register/update ONE record (idempotent: skip-if-unchanged via current-value byte-compare;
116// additive new version otherwise). BYTE-IDENTICAL semantics to ws_put_p, over ss_*. Returns
117// 0 committed a new version / 1 unchanged-skipped / <0 commit error.
118func as_put(prefix: *u8, key: *u8, val: *u8) -> i64 {
119 let pq: *i64 = sys_mmap(16) as *i64
120 let lq: *i64 = sys_mmap(16) as *i64
121 let vl: i64 = as_len(val)
122 if ss_get(prefix, key, pq, lq) == 1 {
123 if lq[0] == vl {
124 let b: *u8 = pq[0] as *u8
125 var same: i64 = 1
126 var i: i64 = 0
127 while i < vl { if b[i] != val[i] { same = 0; i = vl } else { i = i + 1 } }
128 if same == 1 { return 1 }
129 }
130 }
131 let w: *i64 = ss_begin()
132 ss_add(w, 1, key, val, vl)
133 let segid: i64 = as_seg_next(prefix)
134 return ss_commit(prefix, w, segid)
135}
136
137// is NUL-terminated `tok` a TAB-bounded member of list[0..llen)? (whole-field match, mirrors ws_member:
138// "sig:X:UNTAGGED" must not match a longer field). The ws:ids index dedupe predicate.
139func as_member(list: *u8, llen: i64, tok: *u8) -> i64 {
140 let tl: i64 = as_len(tok)
141 var i: i64 = 0
142 while i < llen {
143 var atstart: i64 = 0
144 if i == 0 { atstart = 1 }
145 if i > 0 { if list[i - 1] == (9 as u8) { atstart = 1 } }
146 if atstart == 1 {
147 var fe: i64 = i
148 var go: i64 = 1
149 while go == 1 {
150 if fe >= llen { go = 0 } else {
151 if list[fe] == (9 as u8) { go = 0 } else { fe = fe + 1 }
152 }
153 }
154 if fe - i == tl {
155 var eq: i64 = 1
156 var j: i64 = 0
157 while j < tl { if list[i + j] != tok[j] { eq = 0; j = tl } else { j = j + 1 } }
158 if eq == 1 { return 1 }
159 }
160 }
161 i = i + 1
162 }
163 return 0
164}
165
166// ---- the SIGNAL TABLE (out_signals) layout, heap-mmap parallel arrays (no struct aliasing) ----
167// A signal is the triple {cid, signal_code, detail}. out_signals is an i64[] the CALLER mmaps with
168// 3*cap slots; we fill it as 3 parallel runs to keep field access by offset (build-rule idiom):
169// out_signals[0*cap + k] = ptr to NUL-term CID (k-th signal)
170// out_signals[1*cap + k] = ptr to NUL-term signal_code
171// out_signals[2*cap + k] = ptr to NUL-term detail
172// as_scan returns the number of signals written (k count). Each string is a fresh mmap copy so the
173// signal table outlives the catalog snapshot buffers.
174func as_sig_cid(out_signals: *i64, cap: i64, k: i64) -> *u8 { return out_signals[0 * cap + k] as *u8 }
175func as_sig_code(out_signals: *i64, cap: i64, k: i64) -> *u8 { return out_signals[1 * cap + k] as *u8 }
176func as_sig_detail(out_signals: *i64, cap: i64, k: i64) -> *u8 { return out_signals[2 * cap + k] as *u8 }
177
178// emit ONE signal into the table at index k (fresh NUL-term copies); returns k+1. detail may be empty.
179func as_emit_one(out_signals: *i64, cap: i64, k: i64, cid: *u8, code: *u8, detail: *u8) -> i64 {
180 if k >= cap { return k }
181 let cb: *u8 = sys_mmap(as_len(cid) + 8)
182 as_cpy(cb, cid)
183 out_signals[0 * cap + k] = cb as i64
184 let sb: *u8 = sys_mmap(as_len(code) + 8)
185 as_cpy(sb, code)
186 out_signals[1 * cap + k] = sb as i64
187 let db: *u8 = sys_mmap(as_len(detail) + 8)
188 as_cpy(db, detail)
189 out_signals[2 * cap + k] = db as i64
190 return k + 1
191}
192
193// ---- as_scan: derive ALL task signals over the catalog snapshot + parallel aux arrays ----
194// prefix : the catalog store prefix to scan (cat_list/cat_get).
195// phashes[i] : record i's perceptual dHash (0 = none -> never NEAR_DUP).
196// cred_ptr[i] / cred_len[i] : record i's R2 provenance credential bytes (len 0 = none).
197// pubkey : the 32-byte trusted platform public key prov_verify checks against.
198// out_signals : caller-mmap'd i64[3*cap] (see layout above). cap = max signals.
199// Returns the number of signals derived (>=0). PURE/deterministic over (snapshot, phashes, creds).
200// Records are visited in cat_list order; aux arrays are indexed by that same order.
201func as_scan(prefix: *u8, phashes: *i64, cred_ptr: *i64, cred_len: *i64, pubkey: *u8,
202 out_signals: *i64, cap: i64) -> i64 {
203 // 1) enumerate the catalog membership (snapshot order).
204 let lcap: i64 = K_MAGIC_4096
205 let cids: *i64 = sys_mmap(8 * lcap) as *i64
206 let n: i64 = cat_list(prefix, cids, lcap)
207
208 // 2) NEAR_DUP needs the near-dup GROUPING first: group records by perceptual fingerprint, then a
209 // record is a near-dup iff its group has >1 member. at_neardup assigns each record the index of
210 // its earliest within-threshold peer; we count group sizes to find groups with >1 member.
211 let groups: *i64 = sys_mmap(8 * (n + 16)) as *i64
212 at_neardup(phashes, n, AS_NEARDUP_THRESHOLD(), groups)
213 let gsize: *i64 = sys_mmap(8 * (n + 16)) as *i64
214 var gi: i64 = 0
215 while gi < n { gsize[gi] = 0; gi = gi + 1 }
216 gi = 0
217 while gi < n {
218 // a record with NO fingerprint (phash==0) is its own singleton group id == gi (at_neardup sets
219 // out_groups[i]=i when nothing is within threshold); excluding it from near-dup counting is
220 // handled below by the phash!=0 guard, so we count every group membership here honestly.
221 let g: i64 = groups[gi]
222 gsize[g] = gsize[g] + 1
223 gi = gi + 1
224 }
225
226 // 3) per-record predicate evaluation.
227 let dk: *i64 = sys_mmap(8 * 64) as *i64
228 let dv: *i64 = sys_mmap(8 * 64) as *i64
229 let pp: *i64 = sys_mmap(16) as *i64
230 let ll: *i64 = sys_mmap(16) as *i64
231 var k: i64 = 0
232 var i: i64 = 0
233 while i < n {
234 let cid: *u8 = cids[i] as *u8
235 // fetch + decode the record (snapshot is internally consistent).
236 let got: i64 = cat_get(prefix, cid, pp, ll)
237 if got == 1 {
238 let nf: i64 = ar_decode(pp[0] as *u8, ll[0], dk, dv, 64)
239 let typ: *u8 = ar_get(dk, dv, nf, "type\x00" as *u8)
240 let tags: *u8 = ar_get(dk, dv, nf, "tags\x00" as *u8)
241 let w: *u8 = ar_get(dk, dv, nf, "width\x00" as *u8)
242 let hgt: *u8 = ar_get(dk, dv, nf, "height\x00" as *u8)
243 let enc: *u8 = ar_get(dk, dv, nf, "encoding_format\x00" as *u8)
244 let cur: *u8 = ar_get(dk, dv, nf, "is_current\x00" as *u8)
245 let life: *u8 = ar_get(dk, dv, nf, "lifecycle_stage\x00" as *u8)
246 let rel: *u8 = ar_get(dk, dv, nf, "relations\x00" as *u8)
247
248 // -- MISSING_PROVENANCE: no verifiable R2 credential bound to this record CID. --
249 // deny-by-default: a record HAS provenance only if a credential is present AND prov_verify==1.
250 var has_prov: i64 = 0
251 if cred_len[i] > 0 {
252 if prov_verify(cred_ptr[i] as *u8, cred_len[i], pubkey, cid) == 1 { has_prov = 1 }
253 }
254 if has_prov == 0 {
255 k = as_emit_one(out_signals, cap, k, cid, SIG_MISSING_PROVENANCE(),
256 "no verifiable C2PA credential bound to record CID\x00" as *u8)
257 }
258
259 // -- UNTAGGED: the tags facet is empty. --
260 if as_present(tags) == 0 {
261 k = as_emit_one(out_signals, cap, k, cid, SIG_UNTAGGED(),
262 "tags facet empty -- needs keyword tagging\x00" as *u8)
263 }
264
265 // -- MISSING_METADATA: type=image but width/height absent. --
266 if as_present(typ) == 1 {
267 if as_streq(typ, "image\x00" as *u8) == 1 {
268 if as_present(w) == 0 { k = as_emit_one(out_signals, cap, k, cid, SIG_MISSING_METADATA(),
269 "image missing width/height -- needs metadata extraction\x00" as *u8) }
270 else { if as_present(hgt) == 0 { k = as_emit_one(out_signals, cap, k, cid, SIG_MISSING_METADATA(),
271 "image missing width/height -- needs metadata extraction\x00" as *u8) } }
272 }
273 }
274
275 // -- NEAR_DUP: record is in a perceptual group with >1 member (and HAS a fingerprint). --
276 if phashes[i] != 0 {
277 if gsize[groups[i]] > 1 {
278 k = as_emit_one(out_signals, cap, k, cid, SIG_NEAR_DUP(),
279 "perceptual near-duplicate of another asset -- needs de-dup review\x00" as *u8)
280 }
281 }
282
283 // -- STALE: is_current=="0", OR lifecycle_stage retired/archive. --
284 var stale: i64 = 0
285 if as_present(cur) == 1 { if as_streq(cur, "0\x00" as *u8) == 1 { stale = 1 } }
286 if as_present(life) == 1 {
287 if as_streq(life, "retire\x00" as *u8) == 1 { stale = 1 }
288 if as_streq(life, "archive\x00" as *u8) == 1 { stale = 1 }
289 }
290 if stale == 1 {
291 k = as_emit_one(out_signals, cap, k, cid, SIG_STALE(),
292 "soft-retired/archived (is_current=0 or lifecycle retired)\x00" as *u8)
293 }
294
295 // -- ORPHANED: relations field empty (no graph edges to/from). --
296 if as_present(rel) == 0 {
297 k = as_emit_one(out_signals, cap, k, cid, SIG_ORPHANED(),
298 "no relations -- unlinked in the asset graph\x00" as *u8)
299 }
300
301 // -- UNTRANSCODED: type=video/audio with no web-friendly encoding_format. --
302 if as_present(typ) == 1 {
303 var av: i64 = 0
304 if as_streq(typ, "video\x00" as *u8) == 1 { av = 1 }
305 if as_streq(typ, "audio\x00" as *u8) == 1 { av = 1 }
306 if av == 1 {
307 if as_present(enc) == 0 {
308 k = as_emit_one(out_signals, cap, k, cid, SIG_UNTRANSCODED(),
309 "av asset has no web-friendly encoding_format -- needs transcode\x00" as *u8)
310 }
311 }
312 }
313 }
314 i = i + 1
315 }
316 return k
317}
318
319// ---- as_emit: write each derived signal as a work item into the DEDICATED asset-signals namespace ----
320// Each signal -> one work item record in the store at prefix AS_PREFIX() (NOT the production board):
321// key = "sig:" + cid + ":" + signal_code (deterministic -> idempotent re-emit via ws_put_p)
322// val (TAB-separated, the WMS record shape) = signal_code <TAB> cid <TAB> detail <TAB> ACTIVE <TAB> asset
323// field 0 = signal_code field 1 = cid field 2 = detail field 3 = state field 4 = empire
324// Also maintains a "ws:ids" index (TAB-joined keys) in the SAME dedicated prefix so as_list can
325// enumerate via ws_manifest_p -- exactly the WMS SSOT contract, in an isolated store. Returns the
326// number of work items committed-or-already-present (>=0), or <0 on a store error. Idempotent: a
327// re-emit of identical signals re-states the same keys (ws_put_p skips unchanged) and the same index.
328func as_emit(out_signals: *i64, cap: i64, count: i64) -> i64 {
329 let prefix: *u8 = AS_PREFIX()
330 // build/extend the ws:ids index in-memory as we go (TAB-joined keys).
331 let idxbuf: *u8 = sys_mmap(count * 160 + 256)
332 var io: i64 = 0
333 // seed idxbuf with any EXISTING index so re-emit across runs stays additive (membership preserved).
334 let pq: *i64 = sys_mmap(16) as *i64
335 let lq: *i64 = sys_mmap(16) as *i64
336 if ss_get(prefix, "ws:ids\x00" as *u8, pq, lq) == 1 {
337 let eb: *u8 = pq[0] as *u8
338 var e: i64 = 0
339 while e < lq[0] { idxbuf[io] = eb[e]; io = io + 1; e = e + 1 }
340 }
341 var wrote: i64 = 0
342 var k: i64 = 0
343 while k < count {
344 let cid: *u8 = as_sig_cid(out_signals, cap, k)
345 let code: *u8 = as_sig_code(out_signals, cap, k)
346 let detail: *u8 = as_sig_detail(out_signals, cap, k)
347 // key = "sig:" + cid + ":" + code
348 let key: *u8 = sys_mmap(as_len(cid) + as_len(code) + 16)
349 var ko: i64 = as_cat(key, 0, "sig:\x00" as *u8)
350 ko = as_cat(key, ko, cid)
351 ko = as_cat(key, ko, ":\x00" as *u8)
352 ko = as_cat(key, ko, code)
353 // val = code TAB cid TAB detail TAB ACTIVE TAB asset (the WMS TAB record shape)
354 let val: *u8 = sys_mmap(as_len(code) + as_len(cid) + as_len(detail) + 64)
355 var vo: i64 = as_cat(val, 0, code)
356 val[vo] = 9 as u8; vo = vo + 1
357 vo = as_cat(val, vo, cid)
358 val[vo] = 9 as u8; vo = vo + 1
359 vo = as_cat(val, vo, detail)
360 val[vo] = 9 as u8; vo = vo + 1
361 vo = as_cat(val, vo, "ACTIVE\x00" as *u8)
362 val[vo] = 9 as u8; vo = vo + 1
363 vo = as_cat(val, vo, "asset\x00" as *u8)
364 // commit the work item (idempotent: as_put skips if byte-identical -- production: ws_put_p).
365 let rc: i64 = as_put(prefix, key, val)
366 if rc < 0 { return rc }
367 // extend the index iff this key is not already a TAB-member (idempotent membership).
368 if as_member(idxbuf, io, key) == 0 {
369 if io > 0 { idxbuf[io] = 9 as u8; io = io + 1 }
370 var t: i64 = 0
371 while key[t] != (0 as u8) { idxbuf[io] = key[t]; io = io + 1; t = t + 1 }
372 }
373 wrote = wrote + 1
374 k = k + 1
375 }
376 // commit the (possibly extended) ws:ids index so as_list can enumerate (production: ws_manifest_p).
377 if io > 0 {
378 idxbuf[io] = 0 as u8
379 let rc2: i64 = as_put(prefix, "ws:ids\x00" as *u8, idxbuf)
380 if rc2 < 0 { return rc2 }
381 }
382 return wrote
383}
384
385// ---- as_list: read emitted work items back from the dedicated namespace ----
386// Enumerates the asset-signals store at AS_PREFIX() by reading the ws:ids index and TAB-splitting it
387// (BYTE-IDENTICAL walk to ws_manifest_p, the WMS SSOT enumeration), writing each work-item KEY pointer
388// (fresh NUL-term copy) into out_keys[] (up to cap). Returns the count (>=0), or 0 if nothing emitted.
389// The production board (WS_PREFIX) is never read here -- this reads ONLY the isolated asset namespace.
390func as_list(out_keys: *i64, cap: i64) -> i64 {
391 let h: *i64 = ss_open(AS_PREFIX())
392 if (h as i64) == 0 { return 0 }
393 let pq: *i64 = sys_mmap(16) as *i64
394 let lq: *i64 = sys_mmap(16) as *i64
395 if ss_hget(h, "ws:ids\x00" as *u8, pq, lq) != 1 { return 0 }
396 let ids: *u8 = pq[0] as *u8
397 let idn: i64 = lq[0]
398 var cnt: i64 = 0
399 var i: i64 = 0
400 while i < idn {
401 let kb: *u8 = sys_mmap(256)
402 var o: i64 = 0
403 var go: i64 = 1
404 while go == 1 {
405 if i >= idn { go = 0 } else {
406 if ids[i] == (9 as u8) { i = i + 1; go = 0 } else { kb[o] = ids[i]; o = o + 1; i = i + 1 }
407 }
408 }
409 kb[o] = 0 as u8
410 if o > 0 { if cnt < cap { out_keys[cnt] = kb as i64; cnt = cnt + 1 } }
411 }
412 return cnt
413}
414
415// as_get: retrieve one emitted work item's TAB record by its key (ptr/len out). 1 found / 0 tombstoned
416// / -1 absent -- ss_get semantics (== ws_get_p) over the dedicated namespace. Lets a reader pull field
417// values (signal_code/cid/detail/state/empire) with ws_field / as_field.
418func as_get(key: *u8, ptrout: *i64, lenout: *i64) -> i64 {
419 return ss_get(AS_PREFIX(), key, ptrout, lenout)
420}
421
422// as_field: extract the f-th (0-based) TAB-separated field of rec[0..rlen) into out (NUL-terminated);
423// returns field length. Same parser as ws_field, for readers of an emitted work-item record.
424func as_field(rec: *u8, rlen: i64, f: i64, out: *u8) -> i64 {
425 var cur: i64 = 0
426 var i: i64 = 0
427 var o: i64 = 0
428 while cur < f {
429 if i >= rlen { out[0] = 0 as u8; return 0 }
430 if rec[i] == (9 as u8) { cur = cur + 1 }
431 i = i + 1
432 }
433 var go: i64 = 1
434 while go == 1 {
435 if i >= rlen { go = 0 } else {
436 if rec[i] == (9 as u8) { go = 0 } else { out[o] = rec[i]; o = o + 1; i = i + 1 }
437 }
438 }
439 out[o] = 0 as u8
440 return o
441}