code wiki / (root) / nx_str.nx

nx_str.nx source

↩ module page · 221 lines · 6754 B

1// nx_str.nx -- C-style string utilities (NUL-terminated bytes). 2// 3// Almost every NishiLang module re-implements strlen / streq / a 4// scratch copy loop. Centralise the basic vocabulary here so all 5// callers agree on edge-case handling (empty strings, prefix 6// boundaries, NUL handling). 7// 8// Bytes are i64 in NishiLang (we don't have u8 scalars), but byte 9// slots in arrays / pointed-to memory ARE u8. We treat every byte 10// as 0..255 and never sign-extend. 11// 12// nx_strconv handles parse/format for integers; this module handles 13// raw character-level operations. 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 "syscalls.nx" 22 23func nx_str_len(s: *u8) -> i64 { 24 var n: i64 = 0 25 while s[n] != 0 { n = n + 1 } 26 return n 27} 28 29// Returns 1 if a == b, 0 otherwise. 30func nx_str_eq(a: *u8, b: *u8) -> i64 { 31 var i: i64 = 0 32 while a[i] != 0 { 33 if b[i] != a[i] { return 0 } 34 i = i + 1 35 } 36 if b[i] != 0 { return 0 } 37 return 1 38} 39 40// strcmp-style: returns negative if a<b, 0 if equal, positive if a>b. 41func nx_str_cmp(a: *u8, b: *u8) -> i64 { 42 var i: i64 = 0 43 while a[i] != 0 { 44 if a[i] != b[i] { return a[i] - b[i] } 45 if b[i] == 0 { return 1 } // shouldn't reach due to a[i]!=0 && b[i]==0 46 i = i + 1 47 } 48 if b[i] != 0 { return 0 - b[i] } 49 return 0 50} 51 52// Copy `n` bytes from src to dst. Source/dest must not overlap. 53// Returns dst. 54func nx_str_memcpy(dst: *u8, src: *u8, n: i64) -> *u8 { 55 var i: i64 = 0 56 while i < n { 57 dst[i] = src[i] 58 i = i + 1 59 } 60 return dst 61} 62 63// Set first `n` bytes of dst to `v`. Returns dst. 64func nx_str_memset(dst: *u8, v: i64, n: i64) -> *u8 { 65 var i: i64 = 0 66 while i < n { 67 dst[i] = v 68 i = i + 1 69 } 70 return dst 71} 72 73// Compare first `n` bytes. strcmp-style. 74func nx_str_memcmp(a: *u8, b: *u8, n: i64) -> i64 { 75 var i: i64 = 0 76 while i < n { 77 if a[i] != b[i] { return a[i] - b[i] } 78 i = i + 1 79 } 80 return 0 81} 82 83// Copy src (NUL-terminated) into dst, including the NUL. Returns dst. 84func nx_str_cpy(dst: *u8, src: *u8) -> *u8 { 85 var i: i64 = 0 86 while src[i] != 0 { 87 dst[i] = src[i] 88 i = i + 1 89 } 90 dst[i] = 0 91 return dst 92} 93 94// Append src (NUL-terminated) to dst (NUL-terminated, must have room). 95// Returns dst. 96func nx_str_cat(dst: *u8, src: *u8) -> *u8 { 97 let dn: i64 = nx_str_len(dst) 98 var i: i64 = 0 99 while src[i] != 0 { 100 dst[dn + i] = src[i] 101 i = i + 1 102 } 103 dst[dn + i] = 0 104 return dst 105} 106 107// Find first occurrence of byte `c` in `s`. Returns offset or -1. 108func nx_str_chr(s: *u8, c: i64) -> i64 { 109 var i: i64 = 0 110 while s[i] != 0 { 111 if s[i] == c { return i } 112 i = i + 1 113 } 114 if c == 0 { return i } 115 return -1 116} 117 118// Find LAST occurrence of byte `c` in `s`. Returns offset or -1. 119func nx_str_rchr(s: *u8, c: i64) -> i64 { 120 let n: i64 = nx_str_len(s) 121 var i: i64 = n - 1 122 while i >= 0 { 123 if s[i] == c { return i } 124 i = i - 1 125 } 126 return -1 127} 128 129// Find first occurrence of substring `needle` in `hay`. Returns 130// offset or -1. Naive O(n*m) -- fine for short needles, which is 131// the toolchain use case. 132func nx_str_str(hay: *u8, needle: *u8) -> i64 { 133 let nn: i64 = nx_str_len(needle) 134 if nn == 0 { return 0 } 135 let hn: i64 = nx_str_len(hay) 136 if nn > hn { return -1 } 137 var i: i64 = 0 138 while i <= hn - nn { 139 var j: i64 = 0 140 var ok: i64 = 1 141 while j < nn { 142 if hay[i + j] != needle[j] { ok = 0; j = nn } 143 else { j = j + 1 } 144 } 145 if ok == 1 { return i } 146 i = i + 1 147 } 148 return -1 149} 150 151// Returns 1 if `hay` starts with `pfx`, 0 otherwise. 152func nx_str_starts_with(hay: *u8, pfx: *u8) -> i64 { 153 var i: i64 = 0 154 while pfx[i] != 0 { 155 if hay[i] != pfx[i] { return 0 } 156 i = i + 1 157 } 158 return 1 159} 160 161// Returns 1 if `hay` ends with `sfx`, 0 otherwise. 162func nx_str_ends_with(hay: *u8, sfx: *u8) -> i64 { 163 let hn: i64 = nx_str_len(hay) 164 let sn: i64 = nx_str_len(sfx) 165 if sn > hn { return 0 } 166 var i: i64 = 0 167 while i < sn { 168 if hay[hn - sn + i] != sfx[i] { return 0 } 169 i = i + 1 170 } 171 return 1 172} 173 174// ---- self-test --------------------------------------------------- 175 176func main() -> i64 { 177 let foo: *u8 = sys_mmap(8) 178 foo[0] = 0x66; foo[1] = 0x6F; foo[2] = 0x6F; foo[3] = 0 179 let foobar: *u8 = sys_mmap(16) 180 foobar[0] = 0x66; foobar[1] = 0x6F; foobar[2] = 0x6F 181 foobar[3] = 0x62; foobar[4] = 0x61; foobar[5] = 0x72; foobar[6] = 0 182 let bar: *u8 = sys_mmap(8) 183 bar[0] = 0x62; bar[1] = 0x61; bar[2] = 0x72; bar[3] = 0 184 185 if nx_str_len(foo) != 3 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 186 if nx_str_len(foobar) != 6 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 187 188 if nx_str_eq(foo, foo) != 1 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 189 if nx_str_eq(foo, bar) != 0 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 190 if nx_str_eq(foo, foobar) != 0 { return __syscall(93, 5, 0, 0, 0, 0, 0) } // foo prefix of foobar 191 192 if nx_str_starts_with(foobar, foo) != 1 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 193 if nx_str_ends_with(foobar, bar) != 1 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 194 if nx_str_ends_with(foobar, foo) != 0 { return __syscall(93, 8, 0, 0, 0, 0, 0) } 195 196 if nx_str_chr(foobar, 0x62) != 3 { return __syscall(93, 9, 0, 0, 0, 0, 0) } 197 if nx_str_rchr(foobar, 0x6F) != 2 { return __syscall(93, 10, 0, 0, 0, 0, 0) } // last 'o' is at idx 2 198 if nx_str_chr(foobar, 0x7A) != -1 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 199 200 if nx_str_str(foobar, bar) != 3 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 201 if nx_str_str(foobar, foo) != 0 { return __syscall(93, 13, 0, 0, 0, 0, 0) } 202 203 let scratch: *u8 = sys_mmap(32) 204 nx_str_cpy(scratch, foobar) 205 if scratch[0] != 0x66 { return __syscall(93, 14, 0, 0, 0, 0, 0) } 206 if scratch[5] != 0x72 { return __syscall(93, 15, 0, 0, 0, 0, 0) } 207 if scratch[6] != 0 { return __syscall(93, 16, 0, 0, 0, 0, 0) } 208 209 nx_str_cat(scratch, bar) 210 if scratch[6] != 0x62 { return __syscall(93, 17, 0, 0, 0, 0, 0) } 211 if scratch[8] != 0x72 { return __syscall(93, 18, 0, 0, 0, 0, 0) } 212 if scratch[9] != 0 { return __syscall(93, 19, 0, 0, 0, 0, 0) } 213 214 nx_str_memset(scratch, 0xAA, 4) 215 if scratch[0] != 0xAA { return __syscall(93, 20, 0, 0, 0, 0, 0) } 216 if scratch[3] != 0xAA { return __syscall(93, 21, 0, 0, 0, 0, 0) } 217 218 if nx_str_memcmp(foo, foo, 3) != 0 { return __syscall(93, 22, 0, 0, 0, 0, 0) } 219 220 return 0 221}