nx_memory_lib.nx source
↩ module page · 113 lines · 6062 B
1// nx_memory_lib.nx -- CONSOLIDATED memory tool (MCP name: nx_memory, tool #6 of the 15), LIBRARY half.
2// READ-ONLY first increment over the sovereign seg-stores (the ecosystem's memory substrate):
3// stores <dir> -> discover every seg-store under a dir (by its manifest) + live segment count
4// get <prefix> <key> -> LATEST value bytes (version-resolved by the store, not raw file bytes)
5// history <prefix> <key> -> every version's kind+len (the time-travel surface, metadata only)
6// index <prefix> <idxkey> -> a registry's newline id-index (e.g. __toolidx__ / __works__ / __blidx__)
7// This is STRUCTURED state over MCP -- what raw file reads (nx_fs) cannot give: latest-version resolution,
8// tombstone semantics, version history. put/tombstone = a LATER increment behind its own write-cap.
9// license_tier: ORIGINAL
10import "nx_syscalls.nx"
11import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
12import "nx_seg_store.nx" // ss_get / ss_scan / ss_manifest_dyn -- the canonical store readers
13
14const MX_IDX_CAP: i64 = 1048576 // index copy buffer (mirrors reg_index sizing)
15const MX_VER_SLOTS: i64 = 260 // per-key version metadata slots (mirrors SS_VER_SLOTS)
16const MX_PATH_CAP: i64 = 1024
17const MX_DENT_BUF: i64 = 65536 // getdents64 batch buffer (proven sizing)
18const MX_ASCII_0: i64 = 48 // '0'
19
20func mx_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
21// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
22// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
23// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
24// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
25func mx_putn(v: i64) -> i64 { nxi_out(v); return 0 }
26func mx_seq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 }
27func mx_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
28// does NUL-terminated s end with NUL-terminated suf?
29func mx_ends(s: *u8, suf: *u8) -> i64 {
30 let n: i64 = mx_slen(s)
31 let m: i64 = mx_slen(suf)
32 if m > n { return 0 }
33 var i: i64 = 0
34 while i < m { if s[n - m + i] != suf[i] { return 0 } i = i + 1 }
35 return 1
36}
37
38// get: print the LATEST value of key. Returns len | -1 absent | tombstone sentinel (0-(3 as i64)).
39func mx_get(prefix: *u8, key: *u8) -> i64 {
40 let po: *i64 = sys_mmap(16) as *i64
41 let lo: *i64 = sys_mmap(16) as *i64
42 let r: i64 = ss_get(prefix, key, po, lo)
43 if r < 0 { mx_puts("NX-MEMORY ABSENT: no such key in this store\n" as *u8); return 0 - 1 }
44 if r == 0 { mx_puts("NX-MEMORY TOMBSTONED: key exists but its latest version is a delete marker\n" as *u8); return 0 - (3 as i64) }
45 sys_write(1, po[0] as *u8, lo[0])
46 return lo[0]
47}
48// history: one metadata line per version ("v<i> kind=<k> len=<n>"); returns version count (0 = absent).
49func mx_history(prefix: *u8, key: *u8) -> i64 {
50 let kinds: *i64 = sys_mmap(8 * MX_VER_SLOTS) as *i64
51 let ptrs: *i64 = sys_mmap(8 * MX_VER_SLOTS) as *i64
52 let lens: *i64 = sys_mmap(8 * MX_VER_SLOTS) as *i64
53 let n: i64 = ss_scan(prefix, key, kinds, ptrs, lens)
54 mx_puts("versions=" as *u8); mx_putn(n); mx_puts(" (most-recent window; oldest first)\n" as *u8)
55 var i: i64 = 0
56 while i < n {
57 mx_puts("v" as *u8); mx_putn(i)
58 mx_puts(" kind=" as *u8); mx_putn(kinds[i])
59 mx_puts(" len=" as *u8); mx_putn(lens[i])
60 mx_puts("\n" as *u8)
61 i = i + 1
62 }
63 return n
64}
65// index: print a registry's newline id-index; returns its length (0 empty/absent).
66func mx_index(prefix: *u8, idxkey: *u8) -> i64 {
67 let po: *i64 = sys_mmap(16) as *i64
68 let lo: *i64 = sys_mmap(16) as *i64
69 if ss_get(prefix, idxkey, po, lo) != 1 { mx_puts("NX-MEMORY: no index under that key\n" as *u8); return 0 }
70 sys_write(1, po[0] as *u8, lo[0])
71 return lo[0]
72}
73// stores: discover seg-stores in `dir` by their "<prefix>manifest.txt"; print "prefix nsegs=<n>" per store.
74// Returns store count; -1 if the dir cannot be opened. (manifest-archive.txt does NOT end with manifest.txt.)
75func mx_stores(dir: *u8) -> i64 {
76 let fd: i64 = sys_openat_rd(dir)
77 if fd < 0 { mx_puts("NX-MEMORY ABSENT: cannot open dir " as *u8); mx_puts(dir); mx_puts("\n" as *u8); return 0 - 1 }
78 let dbuf: *u8 = sys_mmap(MX_DENT_BUF)
79 let pfx: *u8 = sys_mmap(MX_PATH_CAP)
80 let segp: *i64 = sys_mmap(16) as *i64
81 var cnt: i64 = 0
82 var run: i64 = 1
83 while run == 1 {
84 let n: i64 = sys_getdents64(fd, dbuf, MX_DENT_BUF)
85 if n <= 0 { run = 0 } else {
86 var off: i64 = 0
87 while off < n {
88 let rec: *u8 = ((dbuf as i64 + off) as *u8)
89 let reclen: i64 = dirent_reclen(rec)
90 if reclen <= 0 { off = n } else {
91 let name: *u8 = dirent_name(rec)
92 if mx_ends(name, "manifest.txt" as *u8) == 1 {
93 // prefix = dir + "/" + name minus the "manifest.txt" suffix
94 var o: i64 = 0
95 var i: i64 = 0
96 while dir[i] != (0 as u8) { pfx[o] = dir[i]; o = o + 1; i = i + 1 }
97 if o > 0 { if pfx[o-1] != (47 as u8) { pfx[o] = 47 as u8; o = o + 1 } }
98 let keep: i64 = mx_slen(name) - mx_slen("manifest.txt" as *u8)
99 i = 0
100 while i < keep { pfx[o] = name[i]; o = o + 1; i = i + 1 }
101 pfx[o] = 0 as u8
102 let ns: i64 = ss_manifest_dyn(pfx, segp)
103 mx_puts(pfx); mx_puts(" nsegs=" as *u8); mx_putn(ns); mx_puts("\n" as *u8)
104 cnt = cnt + 1
105 }
106 off = off + reclen
107 }
108 }
109 }
110 }
111 sys_close(fd)
112 return cnt
113}