code wiki / (root) / nx_recordlog.nx

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}