nx_arena_types.nx source
↩ module page · 122 lines · 4614 B
1// nx_arena_types.nx -- arena type + constants, zero-syscall.
2//
3// FOUR-PILLAR FIX (2026-05-16): nx_arena.nx hard-imports nx_syscalls.nx
4// because nx_arena_new() calls sys_mmap internally. Any caller that
5// wanted to compose against nx_arena PLUS a different syscall layer
6// (e.g., nx_syscalls_x86_64.nx for native exec) hit linker symbol
7// redefinition -- the F-meta-4-class cross-target portability gap.
8//
9// This file ADDS the inversion-of-control entry point. It carries:
10// - struct NxArena (the type)
11// - NX_ARENA_BYTES (sizeof constant)
12// - nx_arena_align_up (pure math)
13// - nx_arena_alloc / _alloc_zero / _reset / _used / _cap_bytes /
14// _alloc_count / _oom_count (operate on a caller-owned arena)
15// - nx_arena_init(a, base, cap) (init a caller-allocated NxArena
16// struct + caller-allocated backing buffer)
17//
18// What it does NOT carry: nx_arena_new (which mmaps). That stays
19// in nx_arena.nx for backwards-compat + convenience. Callers that
20// want a different syscall ABI can:
21//
22// 1. allocate the NxArena struct + backing buffer themselves
23// 2. call nx_arena_init(a, base, cap)
24// 3. pass `a` to nx_arena_alloc + friends as before
25//
26// Per cardinal user-owns-every-bit: this is the canonical shape.
27// The caller mmaps the bytes (using whichever syscall layer matches
28// the target ABI); substrate just uses them.
29//
30// nx_capability_claims:
31// needs: [pointer_arithmetic, struct_field_assign]
32// provides: [arena_type, arena_init, arena_alloc, arena_reset]
33// safety: [no_unchecked_deref, no_floating_point, no_syscall,
34// bit_equal_reproducible, target_agnostic]
35// verdict: [no_silent_failure (oom returns null + n_oom++)]
36// license: ORIGINAL
37// kind: racing_crew_specialist
38
39// No imports. This file is the BASE LAYER -- types + pure-math
40// alloc/reset/inspect with zero syscall dependency. Per cardinal
41// user-owns-every-bit: caller picks the syscall ABI by importing
42// whichever nx_syscalls*.nx layer matches their target.
43
44struct NxArena {
45 base: *u8, // backing storage (caller-owned)
46 cap: i64, // total capacity bytes
47 off: i64, // current bump position (bytes used)
48 n_alloc: i64, // total alloc requests served (for diag)
49 n_oom: i64, // total alloc requests that overflowed cap
50}
51
52const NX_ARENA_BYTES: i64 = 40
53
54// --- construction (no syscalls; caller supplies bytes) -------------
55
56// Init a caller-allocated NxArena with a caller-allocated backing
57// buffer. Caller's responsibility:
58// - allocate sizeof(NxArena) bytes for `a`
59// - allocate `cap` bytes for `base`
60// - keep both alive for the arena's lifetime
61//
62// Per cardinal user-owns-every-bit: substrate doesn't pick the
63// allocation strategy. Use sys_mmap if you want pages; use a
64// static [u8; N] if you want a fixed buffer; use a file mapping
65// if you want disk-backed.
66func nx_arena_init(a: *NxArena, base: *u8, cap: i64) -> i64 {
67 if a == (0 as *NxArena) { return -1 }
68 if base == (0 as *u8) { return -2 }
69 if cap <= 0 { return -3 }
70 a.base = base
71 a.cap = cap
72 a.off = 0
73 a.n_alloc = 0
74 a.n_oom = 0
75 return 0
76}
77
78// --- alloc (no syscalls) -------------------------------------------
79
80// Round x up to the nearest multiple of align (align must be power-of-2).
81func nx_arena_align_up(x: i64, align: i64) -> i64 {
82 let mask: i64 = align - 1
83 return (x + mask) & (~mask)
84}
85
86// Bump n bytes with align-byte alignment. Returns a *u8 into the
87// arena's backing storage on success, or NULL on overflow.
88func nx_arena_alloc(a: *NxArena, n: i64, align: i64) -> *u8 {
89 if n <= 0 { return 0 as *u8 }
90 if align <= 0 { return 0 as *u8 }
91 let aligned: i64 = nx_arena_align_up(a.off, align)
92 let next: i64 = aligned + n
93 if next > a.cap {
94 a.n_oom = a.n_oom + 1
95 return 0 as *u8
96 }
97 a.off = next
98 a.n_alloc = a.n_alloc + 1
99 let p: i64 = (a.base as i64) + aligned
100 return p as *u8
101}
102
103// Zero-init variant.
104func nx_arena_alloc_zero(a: *NxArena, n: i64, align: i64) -> *u8 {
105 let p: *u8 = nx_arena_alloc(a, n, align)
106 if p == (0 as *u8) { return p }
107 var i: i64 = 0
108 while i < n { p[i] = 0; i = i + 1 }
109 return p
110}
111
112// --- reset / inspect (no syscalls) ---------------------------------
113
114func nx_arena_reset(a: *NxArena) -> i64 {
115 a.off = 0
116 return 0
117}
118
119func nx_arena_used(a: *NxArena) -> i64 { return a.off }
120func nx_arena_cap_bytes(a: *NxArena) -> i64 { return a.cap }
121func nx_arena_alloc_count(a: *NxArena) -> i64 { return a.n_alloc }
122func nx_arena_oom_count(a: *NxArena) -> i64 { return a.n_oom }