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.
Files changed (49) hide show
  1. package/NOMEN_AGENTS.md +7 -1
  2. package/core/System/Arena.nm +166 -0
  3. package/core/System/Array.nm +30 -161
  4. package/core/System/BigInt.nm +36 -57
  5. package/core/System/Buffer.nm +197 -279
  6. package/core/System/ClassBuffer.nm +120 -130
  7. package/core/System/Console.nm +4 -2
  8. package/core/System/Controls/Button.nm +4 -4
  9. package/core/System/Controls/CheckBox.nm +1 -1
  10. package/core/System/Controls/LayoutParams.nm +1 -1
  11. package/core/System/Controls/Text.nm +2 -2
  12. package/core/System/Controls/TextBox.nm +6 -6
  13. package/core/System/Controls/Window.nm +2 -2
  14. package/core/System/Graph.nm +4 -4
  15. package/core/System/LinkedList.nm +4 -4
  16. package/core/System/List.nm +6 -6
  17. package/core/System/Map.nm +33 -30
  18. package/core/System/Set.nm +24 -24
  19. package/core/System/Stream/Directory.nm +4 -4
  20. package/core/System/Stream/File.nm +19 -12
  21. package/core/System/String.nm +300 -34
  22. package/core/System/StringBuilder.nm +53 -117
  23. package/core/System/Text/CharIndex.nm +144 -0
  24. package/core/System/Text/Chars.nm +32 -0
  25. package/core/System/Text/JsonTree.nm +48 -96
  26. package/core/System/Text/Regex.nm +726 -40
  27. package/core/System/Text/Utf8.nm +163 -0
  28. package/core/System/bool.nm +2 -1
  29. package/core/System/char.nm +27 -1
  30. package/core/System/float.nm +2 -1
  31. package/core/System/float32.nm +2 -1
  32. package/core/System/float64.nm +2 -1
  33. package/core/System/int.nm +4 -2
  34. package/core/System/int16.nm +2 -1
  35. package/core/System/int32.nm +2 -1
  36. package/core/System/int64.nm +2 -1
  37. package/core/System/int8.nm +2 -1
  38. package/core/System/ufloat.nm +2 -1
  39. package/core/System/ufloat32.nm +2 -1
  40. package/core/System/ufloat64.nm +2 -1
  41. package/core/System/uint.nm +2 -1
  42. package/core/System/uint16.nm +2 -1
  43. package/core/System/uint32.nm +2 -1
  44. package/core/System/uint64.nm +2 -1
  45. package/core/System/uint8.nm +2 -1
  46. package/core/docs/System/BigInt.md +0 -1
  47. package/dist/index.mjs +5273 -1339
  48. package/package.json +1 -1
  49. package/src/index.ts +58 -7
@@ -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`). Both backends hand
22
- // back a fresh allocation the caller frees — never an alias of self's
23
- // storage — so the result can outlive the receiver (e.g. escape a match
24
- // branch that reclaims the scrutinee temp). Goes through the `strdup`
25
- // extern: the adapter marshals self.ptr to libc and re-wraps the copy
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
- return self[index];
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
- // Fat-string ABI: x19 = self ptr, x20 = self len; index in x2
39
- // (self's pair occupies slots 0-1).
40
- ldrb w0, [x19, x2]
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
- #arch: c
92
- (*self)[index] = value;
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
- return strcmp(self, other) == 0;
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
- int left_len = strlen(self);
131
- int right_len = strlen(other);
132
- char* result = malloc(left_len + right_len + 1);
133
- strcpy(result, self);
134
- strcat(result, other);
135
- return result;
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
- size_t str_len = strlen(self);
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
- #arch: c
20
- if (self->cap >= needed) return;
21
- int new_cap = self->cap;
22
- if (new_cap == 0) new_cap = 16;
23
- while (new_cap < needed) new_cap *= 2;
24
- self->data = (unsigned long long)realloc((void*)(unsigned long long)self->data, new_cap);
25
- self->cap = new_cap;
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
- #arch: c
117
- int sl = strlen(s);
118
- int needed = self->len + sl + 1;
119
- if (needed > self->cap) {
120
- int nc = self->cap;
121
- if (nc == 0) nc = 16;
122
- while (nc < needed) nc *= 2;
123
- self->data = (unsigned long long)realloc((void*)(unsigned long long)self->data, nc);
124
- self->cap = nc;
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
- #arch: c
229
- if (self->data) {
230
- free((void*)(unsigned long long)self->data);
231
- self->data = 0;
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
  }