code wiki / _hdl_build / nx_alloc.nx
nx_alloc.nx source
↩ module page · 100 lines · 5669 B
1// nx_alloc.nx -- SOVEREIGN MEMORY ALLOCATOR (OS gap: MEMORY-MGMT). A heap (malloc/free) over one mmap'd arena:
2// inline block headers [size(incl hdr) | free], first-fit allocate with split, free + coalesce adjacent free blocks.
3// No libc malloc -- our own, deterministic + auditable. NEVER-BRICK: operates on userspace RAM, writes 0 firmware.
4// Grounded: os_mem.raw (Memory_management).
5// T1 alloc 3 blocks, write distinct patterns, read back == no overlap.
6// T2 free the middle, re-malloc reuses that region (first-fit).
7// T3 free all + coalesce -> a single free block == whole arena (no fragmentation creep).
8// T4 teeth: an over-arena malloc fails (returns -1).
9// expect_exit: 0 Sovereign: nx_syscalls.
10import "nx_syscalls.nx"
11import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
12import "nx_g_puts_lib.nx"
13const K_MAGIC_65536: i64 = 65536
14const K_MAGIC_100000: i64 = 100000
15
16// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
17// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
18// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
19// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
20func g_pn(v: i64) -> i64 { nxi_out(v); return 0 }
21func ck(name: *u8, c: i64) -> i64 { if c==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } g_puts(name); g_puts("\n" as *u8); return c }
22
23const HDR: i64 = 16 // 8 bytes size + 8 bytes free-flag
24func ar_rd(a: *u8, o: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v=v|((a[o+i] as i64)<<(i*8)); i=i+1 } return v }
25func ar_wr(a: *u8, o: i64, v: i64) -> i64 { var i: i64=0; while i<8 { a[o+i]=((v>>(i*8))&255) as u8; i=i+1 } return 0 }
26
27func na_init(a: *u8, total: i64) -> i64 { ar_wr(a,0,total); ar_wr(a,8,1); return 0 } // one big free block
28// first-fit allocate `req` data bytes; split if leftover >= HDR+8; returns DATA byte-offset, or -1 on no fit.
29func na_malloc(a: *u8, total: i64, req: i64) -> i64 {
30 var o: i64=0
31 while o<total {
32 let sz: i64=ar_rd(a,o); let fr: i64=ar_rd(a,o+8)
33 if sz<=0 { return 0-1 }
34 if fr==1 { if sz-HDR>=req {
35 if sz-HDR-req >= HDR+8 { let no: i64=o+req+HDR; ar_wr(a,no,sz-(req+HDR)); ar_wr(a,no+8,1); ar_wr(a,o,req+HDR); ar_wr(a,o+8,0) }
36 else { ar_wr(a,o+8,0) }
37 return o+HDR
38 } }
39 o=o+sz
40 }
41 return 0-1
42}
43func na_free(a: *u8, dataoff: i64) -> i64 { ar_wr(a, dataoff-HDR+8, 1); return 0 } // mark block free
44// merge every run of adjacent free blocks into one.
45func na_coalesce(a: *u8, total: i64) -> i64 {
46 var o: i64=0
47 while o<total {
48 let sz: i64=ar_rd(a,o); if sz<=0 { o=total }
49 else { let fr: i64=ar_rd(a,o+8)
50 if fr==1 { let no: i64=o+sz
51 if no<total { let nfr: i64=ar_rd(a,no+8); if nfr==1 { ar_wr(a,o,sz+ar_rd(a,no)) } else { o=no } } else { o=no } }
52 else { o=o+sz } }
53 }
54 return 0
55}
56
57func main() -> i64 {
58 g_puts("nx_alloc (SOVEREIGN memory allocator: malloc/free/coalesce over an arena; no libc)\n" as *u8)
59 var pass: i64=0; var total: i64=0
60 let A: i64=K_MAGIC_65536
61 let a: *u8 = sys_mmap(A)
62 na_init(a, A)
63
64 // T1: alloc 3 blocks, write distinct first-byte patterns, read back (no overlap)
65 let m1: i64=na_malloc(a,A,100); let m2: i64=na_malloc(a,A,200); let m3: i64=na_malloc(a,A,300)
66 a[m1]=0xA1 as u8; a[m2]=0xB2 as u8; a[m3]=0xC3 as u8
67 var t1: i64=0
68 if m1>=0 { if m2>=0 { if m3>=0 { if a[m1]==(0xA1 as u8) { if a[m2]==(0xB2 as u8) { if a[m3]==(0xC3 as u8) { if m1!=m2 { if m2!=m3 { t1=1 } } } } } } } }
69 g_puts(" T1 m1="); g_pn(m1); g_puts(" m2="); g_pn(m2); g_puts(" m3="); g_pn(m3); g_puts("\n" as *u8)
70 pass=pass+ck("T1: 3 mallocs, distinct non-overlapping regions, write/read intact" as *u8, t1); total=total+1
71
72 // T2: free the middle, re-malloc 150 -> first-fit reuses m2's region
73 na_free(a, m2)
74 let m4: i64=na_malloc(a,A,150)
75 var t2: i64=0; if m4==m2 { t2=1 }
76 g_puts(" T2 freed m2="); g_pn(m2); g_puts(", re-malloc(150)="); g_pn(m4); g_puts(" (first-fit reuse)\n" as *u8)
77 pass=pass+ck("T2: free + re-malloc reuses the freed region (first-fit)" as *u8, t2); total=total+1
78
79 // T3: free everything + coalesce -> one block == whole arena
80 na_free(a,m1); na_free(a,m3); na_free(a,m4)
81 na_coalesce(a, A)
82 let first: i64=ar_rd(a,0)
83 var t3: i64=0; if first==A { t3=1 }
84 g_puts(" T3 after free-all + coalesce, first block size="); g_pn(first); g_puts(" (expect "); g_pn(A); g_puts(")\n" as *u8)
85 pass=pass+ck("T3: free-all + coalesce reconstitutes one arena-sized free block (no fragmentation creep)" as *u8, t3); total=total+1
86
87 // T4 teeth: over-arena malloc fails
88 let big: i64=na_malloc(a,A,K_MAGIC_100000)
89 var t4: i64=0; if big==(0-1) { t4=1 }
90 pass=pass+ck("T4 (teeth): an over-arena malloc returns -1 (fails safely)" as *u8, t4); total=total+1
91
92 var okall: i64=0; if pass==total { okall=1 }
93 g_puts("---- nx_alloc: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8)
94 if okall==1 {
95 let logf: i64=sys_openat_append("knowledge/status/alloc.log" as *u8, 420)
96 if logf>=0 { let z: i64=sys_write(logf,"NXALLOC GREEN: sovereign malloc/free/coalesce over arena; first-fit reuse + coalesce reconstitutes arena\n" as *u8,99); sys_close(logf) }
97 g_puts("verdict=GREEN (sovereign memory allocator: malloc/free/first-fit-reuse/coalesce; deterministic, no libc; the MEMORY-MGMT gap)\n" as *u8); sys_exit(0); return 0
98 }
99 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1
100}