code wiki / _hdl_build / nx_cms_store.nx

nx_cms_store.nx source

↩ module page · 166 lines · 6857 B

1// nx_cms_store.nx -- CMS ladder step 10a: the per-site CONTENT STORE (content as DATA, the 2// WordPress content/theme split, sovereign). Record format, line-based and auditable by eye: 3// @key\n 4// value bytes (may span lines)\n 5// @nextkey\n ... 6// A value runs from after its key line to the byte before the next '@'-at-line-start (its trailing 7// newline excluded). Keys ending in _html hold SANITIZED rich text (nx_html_sanitize on write); 8// everything else is plain text, escaped at render. Writes are ATOMIC (temp + sys_renameat) and 9// ADDITIVE (rule 13): the prior store is kept at <path>.prev before every swap = one-deep revision, 10// rollback = swap back. license_tier: ORIGINAL 11import "nx_syscalls.nx" 12const K_MAGIC_65536: i64 = 65536 13 14func cst_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 15 16// find the value span of key in buf[0..n): writes start into *vs, returns value length, or -1 17func cst_find(buf: *u8, n: i64, key: *u8, vs: *i64) -> i64 { 18 let kl: i64 = cst_slen(key) 19 var i: i64 = 0 20 var linestart: i64 = 1 21 while i < n { 22 var hit: i64 = 0 23 if linestart == 1 { if (buf[i] as i64) == 64 { 24 // compare key after '@' up to newline 25 hit = 1 26 var q: i64 = 0 27 while q < kl { 28 if i + 1 + q >= n { hit = 0 } 29 if hit == 1 { if (buf[i+1+q] as i64) != (key[q] as i64) { hit = 0 } } 30 q = q + 1 31 } 32 if hit == 1 { if i + 1 + kl < n { if (buf[i+1+kl] as i64) != 10 { hit = 0 } } } 33 if hit == 1 { if i + 1 + kl >= n { hit = 0 } } 34 } } 35 if hit == 1 { 36 let start: i64 = i + 1 + kl + 1 37 // value ends at next '@' at line start, or EOF 38 var e: i64 = start 39 var ls: i64 = 1 40 var fin: i64 = 0 - 1 41 while e < n { 42 if ls == 1 { if (buf[e] as i64) == 64 { fin = e; e = n } } 43 if e < n { ls = 0; if (buf[e] as i64) == 10 { ls = 1 } e = e + 1 } 44 } 45 if fin < 0 { fin = n } 46 // exclude the value's trailing newline (the one before the next key line) 47 var vl: i64 = fin - start 48 if vl > 0 { if (buf[start+vl-1] as i64) == 10 { vl = vl - 1 } } 49 vs[0] = start 50 return vl 51 } 52 linestart = 0 53 if (buf[i] as i64) == 10 { linestart = 1 } 54 i = i + 1 55 } 56 return 0 - 1 57} 58 59// get a copy of key's value into out (cap): returns length or -1 60func cst_get(buf: *u8, n: i64, key: *u8, out: *u8, cap: i64) -> i64 { 61 let vs: *i64 = sys_mmap(16) as *i64 62 let vl: i64 = cst_find(buf, n, key, vs) 63 if vl < 0 { return 0 - 1 } 64 var i: i64 = 0 65 while i < vl { if i < cap { out[i] = buf[vs[0]+i] } i = i + 1 } 66 if vl > cap { return cap } 67 return vl 68} 69 70// set key=val: rebuilds the store into out (existing key replaced in place, new key appended). 71// returns new total length. 72func cst_set(buf: *u8, n: i64, key: *u8, val: *u8, vl: i64, out: *u8, cap: i64) -> i64 { 73 let vs: *i64 = sys_mmap(16) as *i64 74 let old: i64 = cst_find(buf, n, key, vs) 75 let kl: i64 = cst_slen(key) 76 var o: i64 = 0 77 if old < 0 { 78 // append: copy whole store, ensure trailing newline, add record 79 var i: i64 = 0 80 while i < n { if o < cap { out[o] = buf[i]; o = o + 1 } i = i + 1 } 81 if o > 0 { if (out[o-1] as i64) != 10 { if o < cap { out[o] = 10 as u8; o = o + 1 } } } 82 if o < cap { out[o] = 64 as u8; o = o + 1 } 83 var q: i64 = 0 84 while q < kl { if o < cap { out[o] = key[q]; o = o + 1 } q = q + 1 } 85 if o < cap { out[o] = 10 as u8; o = o + 1 } 86 var m: i64 = 0 87 while m < vl { if o < cap { out[o] = val[m]; o = o + 1 } m = m + 1 } 88 if o < cap { out[o] = 10 as u8; o = o + 1 } 89 return o 90 } 91 // replace: prefix up to value start, new value, then from old value end (incl its trailing newline) 92 var i2: i64 = 0 93 while i2 < vs[0] { if o < cap { out[o] = buf[i2]; o = o + 1 } i2 = i2 + 1 } 94 var m2: i64 = 0 95 while m2 < vl { if o < cap { out[o] = val[m2]; o = o + 1 } m2 = m2 + 1 } 96 var t: i64 = vs[0] + old 97 while t < n { if o < cap { out[o] = buf[t]; o = o + 1 } t = t + 1 } 98 return o 99} 100 101// read a whole store file into out (cap); returns length or -1 102func cst_read(path: *u8, out: *u8, cap: i64) -> i64 { 103 let fd: i64 = sys_openat_rd(path) 104 if fd < 0 { return 0 - 1 } 105 var total: i64 = 0 106 var go: i64 = 1 107 while go == 1 { 108 let r: i64 = sys_read(fd, out + total, cap - total) 109 go = 0 110 if r > 0 { total = total + r; if total < cap { go = 1 } } 111 } 112 sys_close(fd) 113 return total 114} 115 116// copy file src -> dst (mode 0644). returns 1/0. 117func cst_copy(src: *u8, dst: *u8) -> i64 { 118 let sf: i64 = sys_openat_rd(src) 119 if sf < 0 { return 0 } 120 let df: i64 = sys_openat_wr(dst, 0x1a4) 121 if df < 0 { sys_close(sf); return 0 } 122 let b: *u8 = sys_mmap(K_MAGIC_65536) 123 var go: i64 = 1 124 while go == 1 { 125 let r: i64 = sys_read(sf, b, K_MAGIC_65536) 126 go = 0 127 if r > 0 { sys_write(df, b, r); go = 1 } 128 } 129 sys_close(sf); sys_close(df) 130 return 1 131} 132 133// ATOMIC publish: keep <path>.prev (one-deep revision, additive), write <path>.new, rename over path. 134func cst_write_atomic(path: *u8, buf: *u8, n: i64) -> i64 { 135 let pl: i64 = cst_slen(path) 136 let newp: *u8 = sys_mmap(512) 137 let prevp: *u8 = sys_mmap(512) 138 var i: i64 = 0 139 while i < pl { newp[i] = path[i]; prevp[i] = path[i]; i = i + 1 } 140 newp[pl] = 46 as u8; newp[pl+1] = 110 as u8; newp[pl+2] = 101 as u8; newp[pl+3] = 119 as u8; newp[pl+4] = 0 as u8 141 prevp[pl] = 46 as u8; prevp[pl+1] = 112 as u8; prevp[pl+2] = 114 as u8; prevp[pl+3] = 101 as u8; prevp[pl+4] = 118 as u8; prevp[pl+5] = 0 as u8 142 cst_copy(path, prevp) // no current store yet -> no .prev, fine 143 let fd: i64 = sys_openat_wr(newp, 0x1a4) 144 if fd < 0 { return 0 } 145 sys_write(fd, buf, n) 146 sys_close(fd) 147 if sys_renameat(newp, path) != 0 { return 0 } 148 return 1 149} 150 151// ROLLBACK: swap <path>.prev back over path (current first preserved at <path>.rolledoff). 1/0. 152func cst_rollback(path: *u8) -> i64 { 153 let pl: i64 = cst_slen(path) 154 let prevp: *u8 = sys_mmap(512) 155 let offp: *u8 = sys_mmap(512) 156 var i: i64 = 0 157 while i < pl { prevp[i] = path[i]; offp[i] = path[i]; i = i + 1 } 158 prevp[pl] = 46 as u8; prevp[pl+1] = 112 as u8; prevp[pl+2] = 114 as u8; prevp[pl+3] = 101 as u8; prevp[pl+4] = 118 as u8; prevp[pl+5] = 0 as u8 159 offp[pl] = 46 as u8; offp[pl+1] = 111 as u8; offp[pl+2] = 102 as u8; offp[pl+3] = 102 as u8; offp[pl+4] = 0 as u8 160 let chk: i64 = sys_openat_rd(prevp) 161 if chk < 0 { return 0 } 162 sys_close(chk) 163 cst_copy(path, offp) 164 if sys_renameat(prevp, path) != 0 { return 0 } 165 return 1 166}