code wiki / (root) / nx_buf_dyn.nx

nx_buf_dyn.nx source

↩ module page · 199 lines · 5955 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}