code wiki / (root) / nx_vec.nx

nx_vec.nx source

↩ module page · 110 lines · 3015 B

1// vec.nx -- dynamic-array of i64 (Phase G7 in the roadmap). 2// 3// Specialised to i64 for now; generics will generalise to Vec<T> once 4// parse.nx grows multi-param + full monomorphization support. That 5// migration is API-compatible: rename occurrences of i64 in element 6// positions only. 7// 8// Growth: geometric 2x on push (classical amortized O(1)). Backing 9// storage via sys_mmap; never freed (compiler run-once, servers 10// long-running will switch to a per-request arena later). 11// 12// No bounds-check-on-get to match kernel-code convention; call 13// vec_get only after checking vec_len. 14 15// nx_safety_envelope: 16// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 17// sil_target: SIL1 18// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 19// verdict: NOT_YET_EVALUATED 20 21import "nx_syscalls.nx" 22 23struct Vec { 24 data: *i64, 25 len: i64, 26 cap: i64, 27} 28 29// Build a Vec preallocated to `initial_cap` slots. Cap = 0 defers 30// allocation until first push. 31func vec_new(initial_cap: i64) -> *Vec { 32 let raw: *u8 = sys_mmap(64) 33 let v: *Vec = raw as *Vec 34 v.len = 0 35 v.cap = initial_cap 36 if initial_cap > 0 { 37 v.data = sys_mmap(initial_cap * 8 + 16) as *i64 38 } else { 39 v.data = 0 as *i64 40 } 41 return v 42} 43 44func vec_len(v: *Vec) -> i64 { 45 return v.len 46} 47 48// Read element at index i. Caller is responsible for 0 <= i < len. 49func vec_get(v: *Vec, i: i64) -> i64 { 50 return v.data[i] 51} 52 53// Overwrite element at index i. 54func vec_set(v: *Vec, i: i64, x: i64) -> i64 { 55 v.data[i] = x 56 return 0 57} 58 59// Grow cap (doubling or to at least `want` slots) by copying the 60// old data into a fresh allocation. mmap is page-sized anyway so 61// the waste is bounded. 62func vec_grow(v: *Vec, want: i64) -> i64 { 63 var new_cap: i64 = v.cap * 2 64 if new_cap < want { new_cap = want } 65 if new_cap < 8 { new_cap = 8 } 66 let nd: *i64 = sys_mmap(new_cap * 8 + 16) as *i64 67 var i: i64 = 0 68 while i < v.len { 69 nd[i] = v.data[i] 70 i = i + 1 71 } 72 v.data = nd 73 v.cap = new_cap 74 return 0 75} 76 77// Amortised O(1) push to the end. 78func vec_push(v: *Vec, x: i64) -> i64 { 79 if v.len >= v.cap { 80 vec_grow(v, v.len + 1) 81 } 82 v.data[v.len] = x 83 v.len = v.len + 1 84 return 0 85} 86 87// Pop the last element. Returns 0 and leaves len unchanged when empty 88// -- caller gates with vec_len to distinguish. 89func vec_pop(v: *Vec) -> i64 { 90 if v.len <= 0 { return 0 } 91 v.len = v.len - 1 92 return v.data[v.len] 93} 94 95// Truncate to length `n` (no-op if already shorter). Doesn't shrink 96// the backing buffer -- just resets the logical length. 97func vec_truncate(v: *Vec, n: i64) -> i64 { 98 if n < v.len { v.len = n } 99 return 0 100} 101 102// Linear search for `needle`. Returns first matching index or -1. 103func vec_index_of(v: *Vec, needle: i64) -> i64 { 104 var i: i64 = 0 105 while i < v.len { 106 if v.data[i] == needle { return i } 107 i = i + 1 108 } 109 return -1 110}