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}