code wiki / (root) / nx_cell_lib.nx

nx_cell_lib.nx source

↩ module page · 157 lines · 6732 B

1// nx_cell_lib.nx -- ORGANISATION AND PLACE CELLS AS ROWS (shared lib, 2026-08-24). 2// 3// A CELL is one node of the hierarchy a feedback surface rolls up through: a congregation, an agency, a 4// nonprofit program (organisation cells) or a locality, region, nation (place cells). One record kind, 5// one key family, one parent pointer -- the tenant row every multi-organisation surface needs and none 6// had (absence proven 2026-08-24: no tenant lib in 23,167 sources). Composes the survey engine's store 7// discipline: canon-encoded records on the append-only seg_store, latest version wins, history kept. 8// key "cell:<id>" 9// fields type=cell id kind name parent denom usec 10// id a-z 0-9 only, 1..CEL_ID_MAX -- no dash, so "<instrument>-<cell>" survey ids split without 11// ambiguity; CEL_ID_MAX is DERIVED: the engine's survey-id bound is 40 (se_id_ok) and the 12// instrument name is bounded at CEL_INST_MAX, so CEL_INST_MAX + 1 + CEL_ID_MAX == 40. 13// kind must appear in the caller-supplied comma list (conf data, never a literal here) 14// parent another cell id that EXISTS, or "-" for a root; an unknown parent REFUSES by name 15// denom declared denominator (roster size, population) for post-stratification; 0 = undeclared 16// license_tier: ORIGINAL 17import "nx_survey_engine.nx" 18 19const CEL_SURVEY_ID_MAX: i64 = 40 // se_id_ok's bound, restated once so the derivation below is visible 20const CEL_INST_MAX: i64 = 15 21const CEL_ID_MAX: i64 = 24 // CEL_SURVEY_ID_MAX - CEL_INST_MAX - 1 (the dash) 22const CEL_KEY_CAP: i64 = 64 23const CEL_FIELDS: i64 = 16 24const CEL_NUM_BYTES: i64 = 32 25const CEL_WORD: i64 = 8 26const CEL_OK: i64 = 0 27const CEL_ERR_ID: i64 = 0 - 1 28const CEL_ERR_KIND: i64 = 0 - 2 29const CEL_ERR_PARENT: i64 = 0 - 3 30const CEL_ERR_DENOM: i64 = 0 - 4 31const CEL_ERR_STORE: i64 = 0 - 5 32const CEL_ERR_NAME: i64 = 0 - 6 33const CEL_ROOT: *u8 = "-" 34const CEL_ASCII_A: i64 = 97 35const CEL_ASCII_Z: i64 = 122 36const CEL_ASCII_0: i64 = 48 37const CEL_ASCII_9: i64 = 57 38const CEL_ASCII_COMMA: i64 = 44 39const CEL_ASCII_DASH: i64 = 45 40 41func cel_id_ok(s: *u8) -> i64 { 42 var i: i64 = 0 43 while s[i] != (0 as u8) { 44 let c: i64 = s[i] as i64 45 var ok: i64 = 0 46 if c >= CEL_ASCII_A { if c <= CEL_ASCII_Z { ok = 1 } } 47 if c >= CEL_ASCII_0 { if c <= CEL_ASCII_9 { ok = 1 } } 48 if ok == 0 { return 0 } 49 i = i + 1 50 } 51 if i < 1 { return 0 } 52 if i > CEL_ID_MAX { return 0 } 53 return 1 54} 55func cel_inst_ok(s: *u8) -> i64 { 56 if cel_id_ok(s) == 0 { return 0 } 57 if se_slen(s) > CEL_INST_MAX { return 0 } 58 return 1 59} 60// kind must equal one comma-separated entry of kinds exactly 61func cel_kind_ok(kind: *u8, kinds: *u8) -> i64 { 62 let kl: i64 = se_slen(kind) 63 if kl < 1 { return 0 } 64 var i: i64 = 0 65 while kinds[i] != (0 as u8) { 66 var j: i64 = 0 67 var m: i64 = 1 68 while j < kl { 69 if kinds[i + j] == (0 as u8) { m = 0; j = kl } else { 70 if kinds[i + j] != kind[j] { m = 0; j = kl } else { j = j + 1 } 71 } 72 } 73 if m == 1 { 74 var endc: i64 = kinds[i + kl] as i64 75 if endc == 0 { return 1 } 76 if endc == CEL_ASCII_COMMA { return 1 } 77 } 78 while kinds[i] != (0 as u8) { if kinds[i] == (CEL_ASCII_COMMA as u8) { break } i = i + 1 } 79 if kinds[i] == (CEL_ASCII_COMMA as u8) { i = i + 1 } 80 } 81 return 0 82} 83func cel_key(id: *u8, out: *u8) -> i64 { 84 var o: i64 = se_cat(out, 0, "cell:" as *u8) 85 o = se_cat(out, o, id) 86 out[o] = 0 as u8 87 return o 88} 89// survey id of an instrument at a cell: "<inst>-<cell>" 90func cel_survey_id(inst: *u8, id: *u8, out: *u8) -> i64 { 91 var o: i64 = se_cat(out, 0, inst) 92 o = se_catc(out, o, CEL_ASCII_DASH) 93 o = se_cat(out, o, id) 94 out[o] = 0 as u8 95 return o 96} 97func cel_get(prefix: *u8, id: *u8, ks: *i64, vs: *i64, maxf: i64) -> i64 { 98 let key: *u8 = sys_mmap(CEL_KEY_CAP) 99 cel_key(id, key) 100 return se_get_dec(prefix, key, ks, vs, maxf) 101} 102func cel_field(ks: *i64, vs: *i64, nf: i64, key: *u8) -> *u8 { return sv_get(ks, vs, nf, key) } 103func cel_field_n(ks: *i64, vs: *i64, nf: i64, key: *u8, dflt: i64) -> i64 { return se_field_n(ks, vs, nf, key, dflt) } 104 105// put (or re-state) a cell. Every refusal is a NAMED code; nothing is written on refusal. 106func cel_put(prefix: *u8, id: *u8, kind: *u8, name: *u8, parent: *u8, denom: i64, kinds: *u8) -> i64 { 107 if cel_id_ok(id) == 0 { return CEL_ERR_ID } 108 if cel_kind_ok(kind, kinds) == 0 { return CEL_ERR_KIND } 109 if se_slen(name) < 1 { return CEL_ERR_NAME } 110 if denom < 0 { return CEL_ERR_DENOM } 111 if se_seq(parent, CEL_ROOT) == 0 { 112 if cel_id_ok(parent) == 0 { return CEL_ERR_PARENT } 113 if se_seq(parent, id) == 1 { return CEL_ERR_PARENT } 114 let pks: *i64 = sys_mmap(CEL_WORD * CEL_FIELDS) as *i64 115 let pvs: *i64 = sys_mmap(CEL_WORD * CEL_FIELDS) as *i64 116 if cel_get(prefix, parent, pks, pvs, CEL_FIELDS) < 0 { return CEL_ERR_PARENT } 117 } 118 let nowb: *u8 = sys_mmap(CEL_NUM_BYTES) 119 se_catn(nowb, 0, sys_now_us()) 120 let denomb: *u8 = sys_mmap(CEL_NUM_BYTES) 121 se_catn(denomb, 0, denom) 122 let ks: *i64 = sys_mmap(CEL_WORD * CEL_FIELDS) as *i64 123 let vs: *i64 = sys_mmap(CEL_WORD * CEL_FIELDS) as *i64 124 var nf: i64 = 0 125 ks[nf] = "type" as *u8 as i64; vs[nf] = "cell" as *u8 as i64; nf = nf + 1 126 ks[nf] = "id" as *u8 as i64; vs[nf] = id as i64; nf = nf + 1 127 ks[nf] = "kind" as *u8 as i64; vs[nf] = kind as i64; nf = nf + 1 128 ks[nf] = "name" as *u8 as i64; vs[nf] = name as i64; nf = nf + 1 129 ks[nf] = "parent" as *u8 as i64; vs[nf] = parent as i64; nf = nf + 1 130 ks[nf] = "denom" as *u8 as i64; vs[nf] = denomb as i64; nf = nf + 1 131 ks[nf] = "usec" as *u8 as i64; vs[nf] = nowb as i64; nf = nf + 1 132 let rec: *u8 = sys_mmap(SE_MAGIC_4096) 133 let rl: i64 = canon_encode(ks, vs, nf, rec) 134 let w: *i64 = ss_begin() 135 let key: *u8 = sys_mmap(CEL_KEY_CAP) 136 cel_key(id, key) 137 if ss_add(w, 1, key, rec, rl) != 0 { return CEL_ERR_STORE } 138 if ss_commit(prefix, w, sys_now_us()) != 0 { return CEL_ERR_STORE } 139 return CEL_OK 140} 141// every live cell (latest version, tombstones squeezed). Returns count, or LOUD -1 past the walk bound. 142func cel_list(prefix: *u8, kout: *i64, vout: *i64, lout: *i64, cap: i64) -> i64 { 143 let n: i64 = se_walk_latest(prefix, "cell:" as *u8, kout, vout, lout, cap) 144 if n < 0 { return 0 - 1 } 145 var m: i64 = 0 146 var i: i64 = 0 147 while i < n { 148 if vout[i] != 0 { 149 kout[m] = kout[i] 150 vout[m] = vout[i] 151 lout[m] = lout[i] 152 m = m + 1 153 } 154 i = i + 1 155 } 156 return m 157}