nomen-lang 0.2.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOMEN_AGENTS.md +7 -1
- package/core/System/Arena.nm +166 -0
- package/core/System/Array.nm +30 -161
- package/core/System/BigInt.nm +36 -57
- package/core/System/Buffer.nm +197 -279
- package/core/System/ClassBuffer.nm +120 -130
- package/core/System/Console.nm +4 -2
- package/core/System/Controls/Button.nm +4 -4
- package/core/System/Controls/CheckBox.nm +1 -1
- package/core/System/Controls/LayoutParams.nm +1 -1
- package/core/System/Controls/Text.nm +2 -2
- package/core/System/Controls/TextBox.nm +6 -6
- package/core/System/Controls/Window.nm +2 -2
- package/core/System/Graph.nm +4 -4
- package/core/System/LinkedList.nm +4 -4
- package/core/System/List.nm +6 -6
- package/core/System/Map.nm +33 -30
- package/core/System/Set.nm +24 -24
- package/core/System/Stream/Directory.nm +4 -4
- package/core/System/Stream/File.nm +19 -12
- package/core/System/String.nm +300 -34
- package/core/System/StringBuilder.nm +53 -117
- package/core/System/Text/CharIndex.nm +144 -0
- package/core/System/Text/Chars.nm +32 -0
- package/core/System/Text/JsonTree.nm +48 -96
- package/core/System/Text/Regex.nm +726 -40
- package/core/System/Text/Utf8.nm +163 -0
- package/core/System/bool.nm +2 -1
- package/core/System/char.nm +27 -1
- package/core/System/float.nm +2 -1
- package/core/System/float32.nm +2 -1
- package/core/System/float64.nm +2 -1
- package/core/System/int.nm +4 -2
- package/core/System/int16.nm +2 -1
- package/core/System/int32.nm +2 -1
- package/core/System/int64.nm +2 -1
- package/core/System/int8.nm +2 -1
- package/core/System/ufloat.nm +2 -1
- package/core/System/ufloat32.nm +2 -1
- package/core/System/ufloat64.nm +2 -1
- package/core/System/uint.nm +2 -1
- package/core/System/uint16.nm +2 -1
- package/core/System/uint32.nm +2 -1
- package/core/System/uint64.nm +2 -1
- package/core/System/uint8.nm +2 -1
- package/core/docs/System/BigInt.md +0 -1
- package/dist/index.mjs +5273 -1339
- package/package.json +1 -1
- package/src/index.ts +58 -7
package/core/System/String.nm
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
// result is re-wrapped with its length.
|
|
4
4
|
extern func strdup = (string s, move out string)
|
|
5
5
|
|
|
6
|
+
// Raw memory externs — the allocation primitives every `unsafe` Nomen body
|
|
7
|
+
// across core (Buffer, StringBuilder, Array, ...) is built on. This file is
|
|
8
|
+
// part of the always-linked base types, so the declarations are visible to
|
|
9
|
+
// every core file regardless of pull order. Free externs emit under an
|
|
10
|
+
// `extern_<name>` label that wraps the identically-named C symbol, so these
|
|
11
|
+
// can never collide with it (docs/CORE_RAW.md roadmap items 1+2).
|
|
12
|
+
extern func calloc = (int count, int size, out uint64)
|
|
13
|
+
extern func malloc = (int bytes, out uint64)
|
|
14
|
+
extern func realloc = (uint64 address, int bytes, out uint64)
|
|
15
|
+
extern func free = (uint64 address)
|
|
16
|
+
extern func memcpy = (uint64 dst, uint64 src, int bytes, out uint64)
|
|
17
|
+
extern func memset = (uint64 dst, int value, int bytes, out uint64)
|
|
18
|
+
|
|
6
19
|
/**
|
|
7
20
|
* A UTF-8 text string — a heap-owned, length-tracked sequence of bytes
|
|
8
21
|
**/
|
|
@@ -18,29 +31,47 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
18
31
|
return h
|
|
19
32
|
}
|
|
20
33
|
|
|
21
|
-
// Returns an OWNED copy of self (`move out string`).
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
// with its length.
|
|
34
|
+
// Returns an OWNED byte-exact copy of self (`move out string`). Fresh
|
|
35
|
+
// allocation, never an alias of self's storage — the result can outlive
|
|
36
|
+
// the receiver (e.g. escape a match branch that reclaims the scrutinee
|
|
37
|
+
// temp). Copies by tracked length, so embedded NULs survive (an extern
|
|
38
|
+
// strdup boundary would re-synthesize length via strlen and truncate).
|
|
27
39
|
pub func to_string = (self, move out string) {
|
|
28
|
-
return strdup(self)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
pub func at = (self, int index: index >= 0 && index < self.length, out char) {
|
|
32
40
|
```
|
|
33
41
|
#arch: c
|
|
34
|
-
|
|
42
|
+
// Fat-string ABI: copy self.len bytes; no strlen anywhere.
|
|
43
|
+
char* buf = (char*)malloc(self.len + 1);
|
|
44
|
+
memcpy(buf, self.ptr, self.len);
|
|
45
|
+
buf[self.len] = 0;
|
|
46
|
+
return (nomen_string){ buf, self.len };
|
|
35
47
|
```
|
|
36
48
|
```
|
|
37
49
|
#arch: aarch64
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
50
|
+
// x19/x20 = self (ptr, len). malloc + memcpy of known length;
|
|
51
|
+
// memcpy returns the dst in x0, which becomes the return ptr half.
|
|
52
|
+
sub sp, sp, #16
|
|
53
|
+
str x20, [sp, #0]
|
|
54
|
+
add x0, x20, #1
|
|
55
|
+
bl _malloc
|
|
56
|
+
mov x1, x19
|
|
57
|
+
mov x2, x20
|
|
58
|
+
bl _memcpy
|
|
59
|
+
ldr x1, [sp, #0]
|
|
60
|
+
add x2, x0, x1
|
|
61
|
+
strb wzr, [x2] // NUL-terminate for libc consumers
|
|
62
|
+
add sp, sp, #16
|
|
41
63
|
```
|
|
42
64
|
}
|
|
43
65
|
|
|
66
|
+
// The indexing primitive everything string code builds on — `unsafe`
|
|
67
|
+
// Nomen, single-sourced for both backends. `self as ptr char` is the fat
|
|
68
|
+
// value's backing bytes (the thin `char*` the raw blocks used to poke).
|
|
69
|
+
pub func at = (self, int index: index >= 0 && index < self.length, out char) {
|
|
70
|
+
unsafe {
|
|
71
|
+
return (self as ptr char)[index]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
44
75
|
// Bounds-checked access: `fallback` when index is outside
|
|
45
76
|
// [0, self.length).
|
|
46
77
|
pub func at_or = (self, int index, char fallback, out char) {
|
|
@@ -71,8 +102,9 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
71
102
|
) {
|
|
72
103
|
```
|
|
73
104
|
#arch: c
|
|
105
|
+
// Fat-string ABI: slice out of the tracked buffer — no NUL scan.
|
|
74
106
|
nomen_view _r;
|
|
75
|
-
_r.ptr = (void*)(self + start);
|
|
107
|
+
_r.ptr = (void*)(self.ptr + start);
|
|
76
108
|
_r.len = (long)(end - start);
|
|
77
109
|
return _r;
|
|
78
110
|
```
|
|
@@ -87,21 +119,17 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
87
119
|
}
|
|
88
120
|
|
|
89
121
|
pub func set = (ref self, int index: index >= 0 && index < self.length, char value) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
```
|
|
95
|
-
#arch: aarch64
|
|
96
|
-
// x19 = self (pointer), x1 = index, w2 = value
|
|
97
|
-
strb w2, [x19, x1]
|
|
98
|
-
```
|
|
122
|
+
unsafe {
|
|
123
|
+
(self as ptr char)[index] = value
|
|
124
|
+
}
|
|
99
125
|
}
|
|
100
126
|
|
|
101
127
|
pub func #op_eq = (self, string other, out bool) {
|
|
102
128
|
```
|
|
103
129
|
#arch: c
|
|
104
|
-
|
|
130
|
+
// Length-aware compare: equal lengths + equal bytes. Embedded
|
|
131
|
+
// NULs are data, not terminators (strcmp truncated at the first).
|
|
132
|
+
return self.len == other.len && memcmp(self.ptr, other.ptr, self.len) == 0;
|
|
105
133
|
```
|
|
106
134
|
```
|
|
107
135
|
#arch: aarch64
|
|
@@ -127,12 +155,15 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
127
155
|
pub func #op_add = (self, string other, out string) {
|
|
128
156
|
```
|
|
129
157
|
#arch: c
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
158
|
+
// Fat-string ABI: one malloc sized by the tracked lens, two
|
|
159
|
+
// memcpys, NUL-terminate for libc consumers. No strlen/strcpy.
|
|
160
|
+
long left_len = self.len;
|
|
161
|
+
long right_len = other.len;
|
|
162
|
+
char* result = (char*)malloc(left_len + right_len + 1);
|
|
163
|
+
memcpy(result, self.ptr, left_len);
|
|
164
|
+
memcpy(result + left_len, other.ptr, right_len);
|
|
165
|
+
result[left_len + right_len] = 0;
|
|
166
|
+
return (nomen_string){ result, left_len + right_len };
|
|
136
167
|
```
|
|
137
168
|
```
|
|
138
169
|
#arch: aarch64
|
|
@@ -165,16 +196,17 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
165
196
|
pub func #op_mul = (self, int count, out string) {
|
|
166
197
|
```
|
|
167
198
|
#arch: c
|
|
168
|
-
|
|
199
|
+
// Fat-string ABI: memcpy loop of the tracked stride. No strlen.
|
|
200
|
+
size_t str_len = self.len;
|
|
169
201
|
size_t total = str_len * (size_t)count + 1;
|
|
170
202
|
char* buf = malloc(total);
|
|
171
203
|
char* p = buf;
|
|
172
204
|
for (int i = 0; i < count; i++) {
|
|
173
|
-
memcpy(p, self, str_len);
|
|
205
|
+
memcpy(p, self.ptr, str_len);
|
|
174
206
|
p += str_len;
|
|
175
207
|
}
|
|
176
208
|
*p = 0;
|
|
177
|
-
return buf;
|
|
209
|
+
return (nomen_string){ buf, total - 1 };
|
|
178
210
|
```
|
|
179
211
|
```
|
|
180
212
|
#arch: aarch64
|
|
@@ -209,4 +241,238 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
209
241
|
ldp x20, x21, [sp], #16
|
|
210
242
|
```
|
|
211
243
|
}
|
|
244
|
+
|
|
245
|
+
// Replace the first non-overlapping occurrence of needle with
|
|
246
|
+
// replacement. No match — or an empty needle — returns an owned copy
|
|
247
|
+
// of self. Views pass straight in (no `to_string` copy): both sides
|
|
248
|
+
// are only ever read.
|
|
249
|
+
pub func replace_first = (self, view string needle, view string replacement, out string) {
|
|
250
|
+
var sb = StringBuilder()
|
|
251
|
+
var int i = 0
|
|
252
|
+
var int n = self.length
|
|
253
|
+
var int nlen = needle.length
|
|
254
|
+
var bool replaced = nlen == 0
|
|
255
|
+
while i < n {
|
|
256
|
+
if !replaced && nlen > 0 && i + nlen <= n && match_at(self, needle, i) {
|
|
257
|
+
sb.append_string_view(replacement)
|
|
258
|
+
i = i + nlen
|
|
259
|
+
replaced = true
|
|
260
|
+
} else {
|
|
261
|
+
sb.append_char(self.at(i))
|
|
262
|
+
i = i + 1
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return sb.to_string()
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Replace all non-overlapping occurrences of needle with replacement,
|
|
269
|
+
// left to right. Same edge behavior as replace_first.
|
|
270
|
+
pub func replace_all = (self, view string needle, view string replacement, out string) {
|
|
271
|
+
var sb = StringBuilder()
|
|
272
|
+
var int i = 0
|
|
273
|
+
var int n = self.length
|
|
274
|
+
var int nlen = needle.length
|
|
275
|
+
while i < n {
|
|
276
|
+
if nlen > 0 && i + nlen <= n && match_at(self, needle, i) {
|
|
277
|
+
sb.append_string_view(replacement)
|
|
278
|
+
i = i + nlen
|
|
279
|
+
} else {
|
|
280
|
+
sb.append_char(self.at(i))
|
|
281
|
+
i = i + 1
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return sb.to_string()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// First index of `needle` in self, or -1 when absent. An empty needle
|
|
288
|
+
// matches at 0 (JavaScript indexOf semantics).
|
|
289
|
+
pub func index_of = (self, view string needle, out int) {
|
|
290
|
+
return self.index_of_from(needle, 0)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// First index of `needle` at or after byte offset `from`, or -1. `from`
|
|
294
|
+
// is clamped into [0, self.length]; an empty needle returns the clamped
|
|
295
|
+
// `from`, so a `from` past the end yields self.length for the empty
|
|
296
|
+
// needle and -1 otherwise.
|
|
297
|
+
pub func index_of_from = (self, view string needle, int from, out int) {
|
|
298
|
+
var int f = from
|
|
299
|
+
if f < 0 {
|
|
300
|
+
f = 0
|
|
301
|
+
}
|
|
302
|
+
if f > self.length {
|
|
303
|
+
f = self.length
|
|
304
|
+
}
|
|
305
|
+
if needle.length == 0 {
|
|
306
|
+
return f
|
|
307
|
+
}
|
|
308
|
+
var int last = self.length - needle.length
|
|
309
|
+
var int i = f
|
|
310
|
+
while i <= last; i += 1 {
|
|
311
|
+
if match_at(self, needle, i) {
|
|
312
|
+
return i
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return -1
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Whether `needle` occurs anywhere in self.
|
|
319
|
+
pub func contains = (self, view string needle, out bool) {
|
|
320
|
+
return self.index_of(needle) >= 0
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Whether self begins with `prefix`.
|
|
324
|
+
pub func starts_with = (self, view string prefix, out bool) {
|
|
325
|
+
if prefix.length > self.length {
|
|
326
|
+
return false
|
|
327
|
+
}
|
|
328
|
+
return match_at(self, prefix, 0)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Whether self ends with `suffix`.
|
|
332
|
+
pub func ends_with = (self, view string suffix, out bool) {
|
|
333
|
+
if suffix.length > self.length {
|
|
334
|
+
return false
|
|
335
|
+
}
|
|
336
|
+
return match_at(self, suffix, self.length - suffix.length)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// The code of the byte at `index`, or -1 when out of bounds — the
|
|
340
|
+
// JavaScript charCodeAt NaN case as a plain int parsers can test.
|
|
341
|
+
// The code of the byte at `index`. Like `at`, the constraint makes
|
|
342
|
+
// out-of-bounds reads unrepresentable: a literal index is verified at
|
|
343
|
+
// compile time, and a dynamic index must be proven in-range by flow
|
|
344
|
+
// facts (a `while i < self.length` loop or an `if` guard) — an
|
|
345
|
+
// unverifiable call is a compile error. For a sentinel read, use
|
|
346
|
+
// `char_code_at_or`; for a trapping read, `char_code_at_or_panic`.
|
|
347
|
+
pub func char_code_at = (self, int index: index >= 0 && index < self.length, out int) {
|
|
348
|
+
return self.at(index) as int
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// The code of the byte at `index`, with `fallback` when index is
|
|
352
|
+
// outside [0, self.length).
|
|
353
|
+
pub func char_code_at_or = (self, int index, int fallback, out int) {
|
|
354
|
+
if index >= 0 && index < self.length {
|
|
355
|
+
return self.at(index) as int
|
|
356
|
+
}
|
|
357
|
+
return fallback
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// The code of the byte at `index`; traps when index is outside
|
|
361
|
+
// [0, self.length) — out-of-range is a bug, not a case.
|
|
362
|
+
pub func char_code_at_or_panic = (self, int index, out int) {
|
|
363
|
+
if index >= 0 && index < self.length {
|
|
364
|
+
return self.at(index) as int
|
|
365
|
+
}
|
|
366
|
+
panic("index out of range")
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Owned copy of self[start, end) with both bounds clamped into
|
|
370
|
+
// [0, self.length] and an inverted pair swapped (JavaScript substring
|
|
371
|
+
// semantics: out-of-range operands can never trap).
|
|
372
|
+
pub func substring = (self, int start, int end, out string) {
|
|
373
|
+
var int a = start
|
|
374
|
+
var int b = end
|
|
375
|
+
if a < 0 {
|
|
376
|
+
a = 0
|
|
377
|
+
}
|
|
378
|
+
if a > self.length {
|
|
379
|
+
a = self.length
|
|
380
|
+
}
|
|
381
|
+
if b > self.length {
|
|
382
|
+
b = self.length
|
|
383
|
+
}
|
|
384
|
+
if b < a {
|
|
385
|
+
b = a
|
|
386
|
+
}
|
|
387
|
+
var view v = self.slice(a, b)
|
|
388
|
+
return v.to_string()
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Owned copy of self with leading and trailing ASCII whitespace
|
|
392
|
+
// (tab, LF, VT, FF, CR, space) removed. All-whitespace yields "".
|
|
393
|
+
pub func trim = (self, out string) {
|
|
394
|
+
var int start = 0
|
|
395
|
+
while start < self.length; start += 1 {
|
|
396
|
+
if !self.at(start).is_ascii_space() {
|
|
397
|
+
break
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
var int end = self.length
|
|
401
|
+
while end > start; end -= 1 {
|
|
402
|
+
if !self.at(end - 1).is_ascii_space() {
|
|
403
|
+
break
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
var view v = self.slice(start, end)
|
|
407
|
+
return v.to_string()
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Owned copy of self with trailing ASCII whitespace removed.
|
|
411
|
+
pub func trim_end = (self, out string) {
|
|
412
|
+
var int end = self.length
|
|
413
|
+
while end > 0; end -= 1 {
|
|
414
|
+
if !self.at(end - 1).is_ascii_space() {
|
|
415
|
+
break
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
var view v = self.slice(0, end)
|
|
419
|
+
return v.to_string()
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Owned copy of self with leading ASCII whitespace removed.
|
|
423
|
+
pub func trim_start = (self, out string) {
|
|
424
|
+
var int start = 0
|
|
425
|
+
while start < self.length; start += 1 {
|
|
426
|
+
if !self.at(start).is_ascii_space() {
|
|
427
|
+
break
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
var view v = self.slice(start, self.length)
|
|
431
|
+
return v.to_string()
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Owned ASCII-lowercased copy of self (A-Z mapped to a-z; all other
|
|
435
|
+
// bytes — including UTF-8 sequences — pass through unchanged).
|
|
436
|
+
pub func to_lowercase = (self, out string) {
|
|
437
|
+
var sb = StringBuilder()
|
|
438
|
+
var int i = 0
|
|
439
|
+
while i < self.length; i += 1 {
|
|
440
|
+
var int code = self.at(i) as int
|
|
441
|
+
if code > 64 && code < 91 {
|
|
442
|
+
code += 32
|
|
443
|
+
}
|
|
444
|
+
sb.append_char(code as char)
|
|
445
|
+
}
|
|
446
|
+
return sb.to_string()
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Owned ASCII-uppercased copy of self (a-z mapped to A-Z; all other
|
|
450
|
+
// bytes — including UTF-8 sequences — pass through unchanged).
|
|
451
|
+
pub func to_uppercase = (self, out string) {
|
|
452
|
+
var sb = StringBuilder()
|
|
453
|
+
var int i = 0
|
|
454
|
+
while i < self.length; i += 1 {
|
|
455
|
+
var int code = self.at(i) as int
|
|
456
|
+
if code > 96 && code < 123 {
|
|
457
|
+
code -= 32
|
|
458
|
+
}
|
|
459
|
+
sb.append_char(code as char)
|
|
460
|
+
}
|
|
461
|
+
return sb.to_string()
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// needle matches text at byte offset pos (caller guarantees pos + len fits;
|
|
466
|
+
// the trailing guard makes the bound explicit so `.at` verifies).
|
|
467
|
+
func match_at = (view string text, view string needle, int pos, out bool) {
|
|
468
|
+
var int i = 0
|
|
469
|
+
var int nlen = needle.length
|
|
470
|
+
var int n = text.length
|
|
471
|
+
while i < nlen && pos + i < n {
|
|
472
|
+
if text.at(pos + i) != needle.at(i) {
|
|
473
|
+
return false
|
|
474
|
+
}
|
|
475
|
+
i = i + 1
|
|
476
|
+
}
|
|
477
|
+
return i == nlen
|
|
212
478
|
}
|
|
@@ -14,48 +14,19 @@ pub struct StringBuilder {
|
|
|
14
14
|
var cap = 0
|
|
15
15
|
|
|
16
16
|
// Ensure capacity for at least `needed` bytes. Grows geometrically.
|
|
17
|
+
// Plain Nomen + the memory externs — no pointer arithmetic needed, so
|
|
18
|
+
// this doesn't even require an unsafe context.
|
|
17
19
|
func ensure = (ref self, int needed) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
int new_cap = self
|
|
22
|
-
if
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
#arch: aarch64
|
|
29
|
-
// x19 = self, x1 = needed
|
|
30
|
-
ldr x2, [x19, #24] // cap
|
|
31
|
-
cmp x2, x1
|
|
32
|
-
b.ge .Lsb_ensure_end
|
|
33
|
-
// pick new_cap
|
|
34
|
-
mov x3, #16
|
|
35
|
-
cmp x2, #0
|
|
36
|
-
csel x3, x3, x2, eq
|
|
37
|
-
.Lsb_ensure_loop:
|
|
38
|
-
cmp x3, x1
|
|
39
|
-
b.ge .Lsb_ensure_grow
|
|
40
|
-
lsl x3, x3, #1
|
|
41
|
-
b .Lsb_ensure_loop
|
|
42
|
-
.Lsb_ensure_grow:
|
|
43
|
-
// save needed (x1) and new_cap (x3) across realloc
|
|
44
|
-
sub sp, sp, #32
|
|
45
|
-
str x1, [sp, #0]
|
|
46
|
-
str x3, [sp, #8]
|
|
47
|
-
str x2, [sp, #16] // old cap (unused after, but keeps stack aligned)
|
|
48
|
-
ldr x0, [x19, #8] // data
|
|
49
|
-
mov x1, x3
|
|
50
|
-
bl _realloc
|
|
51
|
-
str x0, [sp, #24] // saved new data ptr
|
|
52
|
-
ldr x1, [sp, #8] // new_cap
|
|
53
|
-
str x1, [x19, #24] // cap = new_cap
|
|
54
|
-
ldr x0, [sp, #24]
|
|
55
|
-
str x0, [x19, #8] // data = realloc result
|
|
56
|
-
add sp, sp, #32
|
|
57
|
-
.Lsb_ensure_end:
|
|
58
|
-
```
|
|
20
|
+
if self.cap >= needed {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
var int new_cap = self.cap
|
|
24
|
+
if new_cap == 0 {
|
|
25
|
+
new_cap = 16
|
|
26
|
+
}
|
|
27
|
+
while new_cap < needed; new_cap *= 2 {}
|
|
28
|
+
self.data = realloc(self.data, new_cap)
|
|
29
|
+
self.cap = new_cap
|
|
59
30
|
}
|
|
60
31
|
|
|
61
32
|
// Append a single byte (char).
|
|
@@ -110,65 +81,43 @@ pub struct StringBuilder {
|
|
|
110
81
|
```
|
|
111
82
|
}
|
|
112
83
|
|
|
113
|
-
// Append a null-terminated string.
|
|
84
|
+
// Append a null-terminated string. `unsafe` Nomen single-sourced for
|
|
85
|
+
// both backends: the fat pair's pointer half feeds memcpy directly, and
|
|
86
|
+
// the tracked length replaces the strlen the old C body paid.
|
|
114
87
|
func append_string = (ref self, string s) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
88
|
+
unsafe {
|
|
89
|
+
var int sl = s.length
|
|
90
|
+
self.ensure(self.len + sl + 1)
|
|
91
|
+
memcpy(self.data + self.len, (s as ptr char) as uint64, sl)
|
|
92
|
+
self.len = self.len + sl
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Append a view's bytes without materializing it: `append_string` takes
|
|
97
|
+
// an owned string, so passing a slice would need a `to_string` copy per
|
|
98
|
+
// call. Views lower to the same (ptr, len) fat pair, so the bytes are
|
|
99
|
+
// memcpy'd straight out of the borrowed buffer (used by
|
|
100
|
+
// `string.replace_first`/`replace_all`).
|
|
101
|
+
pub func append_string_view = (ref self, view string s) {
|
|
102
|
+
unsafe {
|
|
103
|
+
var int sl = s.length
|
|
104
|
+
self.ensure(self.len + sl + 1)
|
|
105
|
+
memcpy(self.data + self.len, (s as ptr char) as uint64, sl)
|
|
106
|
+
self.len = self.len + sl
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Replace the buffer's contents with a copy of `s`'s bytes. The only
|
|
111
|
+
// way to start a StringBuilder from a slice without a `to_string()`
|
|
112
|
+
// copy: the view's bytes are copied once into fresh storage, so the
|
|
113
|
+
// result is owned and independent of the source.
|
|
114
|
+
pub func seed = (ref self, view string s) {
|
|
115
|
+
unsafe {
|
|
116
|
+
var int sl = s.length
|
|
117
|
+
self.ensure(sl + 1)
|
|
118
|
+
memcpy(self.data, (s as ptr char) as uint64, sl)
|
|
119
|
+
self.len = sl
|
|
125
120
|
}
|
|
126
|
-
memcpy((unsigned char*)(unsigned long long)self->data + self->len, s, sl);
|
|
127
|
-
self->len = self->len + sl;
|
|
128
|
-
```
|
|
129
|
-
```
|
|
130
|
-
#arch: aarch64
|
|
131
|
-
// Fat-string ABI: x19 = self; s = (x1 ptr, x2 len). The length is
|
|
132
|
-
// already in a register — no strlen.
|
|
133
|
-
sub sp, sp, #32
|
|
134
|
-
str x1, [sp, #0] // s ptr
|
|
135
|
-
str x2, [sp, #8] // sl
|
|
136
|
-
ldr x1, [x19, #16] // len
|
|
137
|
-
add x1, x1, x2 // len + sl
|
|
138
|
-
add x1, x1, #1 // +1 for null room
|
|
139
|
-
// inline ensure(x1)
|
|
140
|
-
ldr x2, [x19, #24]
|
|
141
|
-
cmp x2, x1
|
|
142
|
-
b.ge .Lsb_as_copy
|
|
143
|
-
mov x3, #16
|
|
144
|
-
cmp x2, #0
|
|
145
|
-
csel x3, x3, x2, eq
|
|
146
|
-
.Lsb_as_loop:
|
|
147
|
-
cmp x3, x1
|
|
148
|
-
b.ge .Lsb_as_grow
|
|
149
|
-
lsl x3, x3, #1
|
|
150
|
-
b .Lsb_as_loop
|
|
151
|
-
.Lsb_as_grow:
|
|
152
|
-
str x3, [x19, #24]
|
|
153
|
-
ldr x0, [x19, #8]
|
|
154
|
-
mov x1, x3
|
|
155
|
-
bl _realloc
|
|
156
|
-
str x0, [x19, #8]
|
|
157
|
-
.Lsb_as_copy:
|
|
158
|
-
// memcpy(data + len, s.ptr, sl)
|
|
159
|
-
ldr x0, [x19, #8] // data
|
|
160
|
-
ldr x1, [x19, #16] // len
|
|
161
|
-
add x0, x0, x1 // dest = data + len
|
|
162
|
-
ldr x1, [sp, #0] // src = s ptr
|
|
163
|
-
ldr x2, [sp, #8] // n = sl
|
|
164
|
-
bl _memcpy
|
|
165
|
-
// len = len + sl
|
|
166
|
-
ldr x0, [x19, #16]
|
|
167
|
-
ldr x1, [sp, #8]
|
|
168
|
-
add x0, x0, x1
|
|
169
|
-
str x0, [x19, #16]
|
|
170
|
-
add sp, sp, #32
|
|
171
|
-
```
|
|
172
121
|
}
|
|
173
122
|
|
|
174
123
|
// Finalize: null-terminate, return the byte buffer as a string, and
|
|
@@ -188,7 +137,7 @@ pub struct StringBuilder {
|
|
|
188
137
|
((unsigned char*)(unsigned long long)self->data)[idx] = 0;
|
|
189
138
|
unsigned long long result = self->data;
|
|
190
139
|
self->data = 0;
|
|
191
|
-
return (char*)result;
|
|
140
|
+
return (nomen_string){ (char*)result, (long)idx };
|
|
192
141
|
```
|
|
193
142
|
```
|
|
194
143
|
#arch: aarch64
|
|
@@ -224,24 +173,11 @@ pub struct StringBuilder {
|
|
|
224
173
|
|
|
225
174
|
// Drop the buffer without finalizing (free the byte buffer outright).
|
|
226
175
|
func #destroy = () {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
self
|
|
232
|
-
self->len = 0;
|
|
233
|
-
self->cap = 0;
|
|
176
|
+
if self.data != 0 {
|
|
177
|
+
free(self.data)
|
|
178
|
+
self.data = 0
|
|
179
|
+
self.len = 0
|
|
180
|
+
self.cap = 0
|
|
234
181
|
}
|
|
235
|
-
```
|
|
236
|
-
```
|
|
237
|
-
#arch: aarch64
|
|
238
|
-
ldr x0, [x19, #8]
|
|
239
|
-
cbz x0, .Lsb_destroy_end
|
|
240
|
-
bl _free
|
|
241
|
-
str xzr, [x19, #8]
|
|
242
|
-
str xzr, [x19, #16]
|
|
243
|
-
str xzr, [x19, #24]
|
|
244
|
-
.Lsb_destroy_end:
|
|
245
|
-
```
|
|
246
182
|
}
|
|
247
183
|
}
|