code wiki / (root) / nx_buf_dyn_owned_candidate_t218.nx

nx_buf_dyn_owned_candidate_t218.nx source

↩ module page · 248 lines · 8496 B

1// nx_buf_dyn.nx -- dynamic growable byte buffer. 2// 3// Every formatter / builder / ELF emitter today picks a fixed 4// upfront cap and bails on overflow. When the cap is wrong the 5// caller has to recompile or re-mmap. This module gives them 6// std::vector-style amortized growth: 2x doubling when full. 7// 8// API: 9// buf = nx_bd_new(initial_cap) 10// nx_bd_byte(buf, b) 11// nx_bd_bytes(buf, src, n) 12// nx_bd_u32(buf, v) -- LE 13// nx_bd_u64(buf, v) -- LE 14// nx_bd_str(buf, s) -- NUL-terminated, NUL not written 15// nx_bd_strz(buf, s) -- NUL-terminated, NUL written 16// nx_bd_clear(buf) -- len=0, keeps capacity 17// buf.buf, buf.len -- direct access for emit 18// 19// Growth: 2x when full, never less than +64. Old bytes are 20// memcpy'd to the new region; the old region is leaked (we don't 21// have munmap discipline yet, and arenas reset wholesale anyway). 22 23// nx_safety_envelope: 24// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 25// sil_target: SIL1 26// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 27// verdict: NOT_YET_EVALUATED 28 29import "syscalls.nx" 30 31struct NxBufDyn { 32 buf: *u8, 33 len: i64, 34 cap: i64, 35} 36 37const NX_BD_BYTES: i64 = 24 38const NX_BD_MIN_CAP: i64 = 64 39const NX_BD_MIN_GROW: i64 = 64 40 41func nx_bd_new(initial_cap: i64) -> *NxBufDyn { 42 let raw: *u8 = sys_mmap(NX_BD_BYTES) 43 let b: *NxBufDyn = raw as *NxBufDyn 44 var c: i64 = initial_cap 45 if c < NX_BD_MIN_CAP { c = NX_BD_MIN_CAP } 46 b.buf = sys_mmap(c) 47 b.len = 0 48 b.cap = c 49 return b 50} 51 52// Ensure room for `extra` more bytes. Doubles cap until it fits. 53func nx_bd_grow(b: *NxBufDyn, extra: i64) -> i64 { 54 if b.len + extra <= b.cap { return 0 } 55 var new_cap: i64 = b.cap * 2 56 if new_cap < b.len + extra { 57 new_cap = b.len + extra + NX_BD_MIN_GROW 58 } 59 let new_buf: *u8 = sys_mmap(new_cap) 60 var i: i64 = 0 61 while i < b.len { 62 new_buf[i] = b.buf[i] 63 i = i + 1 64 } 65 b.buf = new_buf 66 b.cap = new_cap 67 return 0 68} 69 70func nx_bd_byte(b: *NxBufDyn, v: i64) -> i64 { 71 nx_bd_grow(b, 1) 72 b.buf[b.len] = v & 0xFF 73 b.len = b.len + 1 74 return 0 75} 76 77func nx_bd_bytes(b: *NxBufDyn, src: *u8, n: i64) -> i64 { 78 nx_bd_grow(b, n) 79 var i: i64 = 0 80 while i < n { 81 b.buf[b.len + i] = src[i] 82 i = i + 1 83 } 84 b.len = b.len + n 85 return 0 86} 87 88func nx_bd_u16(b: *NxBufDyn, v: i64) -> i64 { 89 nx_bd_byte(b, v & 0xFF) 90 return nx_bd_byte(b, (v >> 8) & 0xFF) 91} 92 93func nx_bd_u32(b: *NxBufDyn, v: i64) -> i64 { 94 nx_bd_byte(b, v & 0xFF) 95 nx_bd_byte(b, (v >> 8) & 0xFF) 96 nx_bd_byte(b, (v >> 16) & 0xFF) 97 return nx_bd_byte(b, (v >> 24) & 0xFF) 98} 99 100func nx_bd_u64(b: *NxBufDyn, v: i64) -> i64 { 101 var i: i64 = 0 102 while i < 8 { 103 nx_bd_byte(b, (v >> (i * 8)) & 0xFF) 104 i = i + 1 105 } 106 return 0 107} 108 109// Append a NUL-terminated string WITHOUT the NUL. 110func nx_bd_str(b: *NxBufDyn, s: *u8) -> i64 { 111 var i: i64 = 0 112 while s[i] != 0 { 113 nx_bd_byte(b, s[i]) 114 i = i + 1 115 } 116 return 0 117} 118 119// Append a NUL-terminated string WITH the NUL. 120func nx_bd_strz(b: *NxBufDyn, s: *u8) -> i64 { 121 var i: i64 = 0 122 while s[i] != 0 { 123 nx_bd_byte(b, s[i]) 124 i = i + 1 125 } 126 return nx_bd_byte(b, 0) 127} 128 129func nx_bd_clear(b: *NxBufDyn) -> i64 { 130 b.len = 0 131 return 0 132} 133 134// Patch a u32 at a previously-recorded offset (e.g. backpatching 135// a length field whose value wasn't known at the time of writing). 136func nx_bd_patch_u32(b: *NxBufDyn, off: i64, v: i64) -> i64 { 137 if off + 4 > b.len { return -1 } 138 b.buf[off] = v & 0xFF 139 b.buf[off + 1] = (v >> 8) & 0xFF 140 b.buf[off + 2] = (v >> 16) & 0xFF 141 b.buf[off + 3] = (v >> 24) & 0xFF 142 return 0 143} 144 145// ---- self-test --------------------------------------------------- 146 147func main() -> i64 { 148 let b: *NxBufDyn = nx_bd_new(8) 149 if b.cap != NX_BD_MIN_CAP { return __syscall(93, 1, 0, 0, 0, 0, 0) } // promoted to MIN_CAP 150 151 nx_bd_byte(b, 0xAA) 152 nx_bd_byte(b, 0xBB) 153 if b.len != 2 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 154 if b.buf[0] != 0xAA { return __syscall(93, 3, 0, 0, 0, 0, 0) } 155 156 nx_bd_u32(b, 0x12345678) 157 if b.len != 6 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 158 if b.buf[2] != 0x78 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 159 if b.buf[5] != 0x12 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 160 161 // Force growth: write past initial cap. 162 let blob: *u8 = sys_mmap(256) 163 var i: i64 = 0 164 while i < 256 { blob[i] = i; i = i + 1 } 165 nx_bd_bytes(b, blob, 256) 166 if b.len != 262 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 167 if b.cap < 262 { return __syscall(93, 8, 0, 0, 0, 0, 0) } 168 // Old bytes still intact? 169 if b.buf[0] != 0xAA { return __syscall(93, 9, 0, 0, 0, 0, 0) } 170 if b.buf[1] != 0xBB { return __syscall(93, 10, 0, 0, 0, 0, 0) } 171 // First byte of blob copy 172 if b.buf[6] != 0 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 173 if b.buf[6 + 100] != 100 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 174 175 // Backpatch: write a u32 placeholder, then patch. 176 let b2: *NxBufDyn = nx_bd_new(8) 177 nx_bd_u32(b2, 0) 178 nx_bd_byte(b2, 0xFF) 179 nx_bd_patch_u32(b2, 0, 0xCAFEBABE) 180 if b2.buf[0] != 0xBE { return __syscall(93, 13, 0, 0, 0, 0, 0) } 181 if b2.buf[3] != 0xCA { return __syscall(93, 14, 0, 0, 0, 0, 0) } 182 if b2.buf[4] != 0xFF { return __syscall(93, 15, 0, 0, 0, 0, 0) } 183 184 // strz writes the NUL. 185 let s: *u8 = sys_mmap(8) 186 s[0] = 0x68; s[1] = 0x69; s[2] = 0 187 let b3: *NxBufDyn = nx_bd_new(8) 188 nx_bd_strz(b3, s) 189 if b3.len != 3 { return __syscall(93, 16, 0, 0, 0, 0, 0) } 190 if b3.buf[2] != 0 { return __syscall(93, 17, 0, 0, 0, 0, 0) } 191 192 // clear preserves cap. 193 let pre_cap: i64 = b3.cap 194 nx_bd_clear(b3) 195 if b3.len != 0 { return __syscall(93, 18, 0, 0, 0, 0, 0) } 196 if b3.cap != pre_cap { return __syscall(93, 19, 0, 0, 0, 0, 0) } 197 198 return 0 199} 200 201// Owned capture storage is separate from legacy arena-backed NxBufDyn. 202// Caller initializes all fields to zero and retains exclusive ownership. Only 203// this API may replace buf; descriptors and external source spans must stay live. 204// max_bytes=0 applies no caller policy ceiling; i64 extent and kernel admission still apply. 205struct NxBufOwned { buf:*u8, len:i64, cap:i64, } 206const NX_BO_BYTES:i64=24 207const NX_BO_I64_MAX:i64=9223372036854775807 208const NX_BO_INVALID:i64=-100 209const NX_BO_RANGE:i64=-101 210const NX_BO_CAPACITY:i64=-102 211const NX_BO_ALLOCATION:i64=-103 212const NX_BO_ALIAS:i64=-104 213const NX_BO_RELEASE:i64=-105 214func nx_bo_state(b:*NxBufOwned)->i64{ 215 let bp:i64=b as i64;if bp<=0||bp>NX_BO_I64_MAX-NX_BO_BYTES{return NX_BO_INVALID} 216 if b.len<0||b.cap<0||b.len>b.cap{return NX_BO_INVALID} 217 let p:i64=b.buf as i64;if b.cap==0{if p!=0||b.len!=0{return NX_BO_INVALID};return 0} 218 if p<=0||b.cap>NX_BO_I64_MAX-p{return NX_BO_RANGE} 219 if bp<p+b.cap&&p<bp+NX_BO_BYTES{return NX_BO_ALIAS};return 0 220} 221func nx_bo_append(b:*NxBufOwned,src:*u8,n:i64,max_bytes:i64)->i64{ 222 let valid:i64=nx_bo_state(b);if valid<0{return valid} 223 if n<0||max_bytes<0{return NX_BO_INVALID} 224 if n>NX_BO_I64_MAX-b.len{return NX_BO_RANGE} 225 let need:i64=b.len+n;if max_bytes>0&&need>max_bytes{return NX_BO_CAPACITY} 226 if n==0{return 0} 227 let sp:i64=src as i64;if sp<=0{return NX_BO_INVALID};if n>NX_BO_I64_MAX-sp{return NX_BO_RANGE} 228 let bp:i64=b as i64;let oldp:i64=b.buf as i64 229 if sp<bp+NX_BO_BYTES&&bp<sp+n{return NX_BO_ALIAS} 230 if b.cap>0&&sp<oldp+b.cap&&oldp<sp+n{return NX_BO_ALIAS} 231 if need<=b.cap{var i:i64=0;while i<n{b.buf[b.len+i]=src[i];i=i+1};b.len=need;return 0} 232 var next:i64=need 233 if b.cap>0&&b.cap<=NX_BO_I64_MAX/2{let doubled:i64=b.cap*2;if doubled>next{next=doubled}} 234 if max_bytes>0&&next>max_bytes{next=max_bytes} 235 let fresh:*u8=sys_mmap_try(next);if (fresh as i64)<=0{return NX_BO_ALLOCATION} 236 var i:i64=0;while i<b.len{fresh[i]=b.buf[i];i=i+1} 237 var j:i64=0;while j<n{fresh[b.len+j]=src[j];j=j+1} 238 if b.cap>0{let freed:i64=sys_munmap_direct(b.buf,b.cap);if freed<0{sys_munmap_direct(fresh,next);return NX_BO_RELEASE}} 239 b.buf=fresh;b.len=need;b.cap=next;return 0 240} 241// Successful release zeroes descriptor; repeated release is a no-op. 242// A release refusal retains the descriptor so the caller can report/reconcile it. 243func nx_bo_release(b:*NxBufOwned)->i64{ 244 let valid:i64=nx_bo_state(b);if valid<0{return valid} 245 if b.cap==0{return 0} 246 let rc:i64=sys_munmap_direct(b.buf,b.cap);if rc<0{return NX_BO_RELEASE} 247 b.buf=0 as *u8;b.len=0;b.cap=0;return 0 248}