nx_recordlog.nx source
↩ module page · 77 lines · 2877 B
1// nx_recordlog.nx -- REUSABLE append-only timestamped record store (no main).
2//
3// One store, reused everywhere (the genealogist's anti-one-off rule): a pain log,
4// an audit trail, an event journal, a metrics series -- all are "append a keyed,
5// timestamped record + count/aggregate them." Each line: "<unix_ts>\t<key>\n".
6// Additive-only (Cardinal #13): append, never rewrite. Reuses sys_openat_append
7// + sys_now_realtime_sec + sys_read_file. license_tier: ORIGINAL
8//
9// (Existing ad-hoc append loops -- the event-bus log, the warden audit -- are
10// candidates to migrate onto this; logged for the genealogist's dedup pass.)
11
12import "nx_syscalls.nx"
13
14const RL_MODE: i64 = 420 // 0o644
15
16func rl_wn(fd: i64, n: i64) -> i64 {
17 if n == 0 { sys_write(fd, "0" as *u8, 1); return 0 }
18 var m: i64 = n
19 let d: *u8 = sys_mmap(24); var k: i64 = 0
20 while m > 0 { d[k] = (0x30 + (m % 10)) as u8; m = m / 10; k = k + 1 }
21 var i: i64 = k - 1
22 while i >= 0 { let o: *u8 = sys_mmap(1); o[0] = d[i]; sys_write(fd, o, 1); i = i - 1 }
23 return 0
24}
25
26// append one keyed, timestamped record. returns 0 ok, negative on failure.
27func recordlog_append(path: *u8, key: i64) -> i64 {
28 let fd: i64 = sys_openat_append(path, RL_MODE)
29 if fd < 0 { return 0 - 1 }
30 rl_wn(fd, sys_now_realtime_sec())
31 sys_write(fd, "\t" as *u8, 1)
32 rl_wn(fd, key)
33 sys_write(fd, "\n" as *u8, 1)
34 sys_close(fd)
35 return 0
36}
37
38// count records whose key field == `key`. (parses "<ts>\t<key>\n" lines.)
39func recordlog_count(path: *u8, key: i64) -> i64 {
40 let lenbox: *i64 = sys_mmap(16) as *i64
41 lenbox[0] = 0
42 let data: *u8 = sys_read_file(path, lenbox)
43 if (data as i64) == 0 { return 0 }
44 let total: i64 = lenbox[0]
45 var p: i64 = 0
46 var cnt: i64 = 0
47 while p < total {
48 // skip the timestamp field up to the first tab
49 while p < total { if data[p] == 9 as u8 { break } p = p + 1 }
50 if p < total { p = p + 1 } // step past the tab
51 // parse the key field up to end-of-line
52 var k: i64 = 0
53 var hk: i64 = 0
54 while p < total {
55 let c: i64 = data[p] as i64
56 if c == 10 { p = total - total + p; break } // newline ends the record
57 if c >= 0x30 { if c <= 0x39 { k = k * 10 + (c - 0x30); hk = 1 } }
58 p = p + 1
59 }
60 if hk == 1 { if k == key { cnt = cnt + 1 } }
61 if p < total { p = p + 1 } // step past the newline
62 }
63 return cnt
64}
65
66// total records (any key).
67func recordlog_total(path: *u8) -> i64 {
68 let lenbox: *i64 = sys_mmap(16) as *i64
69 lenbox[0] = 0
70 let data: *u8 = sys_read_file(path, lenbox)
71 if (data as i64) == 0 { return 0 }
72 let total: i64 = lenbox[0]
73 var p: i64 = 0
74 var cnt: i64 = 0
75 while p < total { if data[p] == 10 as u8 { cnt = cnt + 1 } p = p + 1 }
76 return cnt
77}