nomen-lang 0.7.1 → 0.9.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 (80) hide show
  1. package/audit_runtime.c +12 -2
  2. package/core/System/Arena.nm +8 -1
  3. package/core/System/Buffer.nm +397 -8
  4. package/core/System/ClassBuffer.nm +156 -0
  5. package/core/System/Controls/Alert.nm +326 -0
  6. package/core/System/Controls/App.nm +147 -0
  7. package/core/System/Controls/Button.nm +119 -1
  8. package/core/System/Controls/CheckBox.nm +25 -1
  9. package/core/System/Controls/ColorPicker.nm +307 -0
  10. package/core/System/Controls/ComboBox.nm +430 -0
  11. package/core/System/Controls/Container.nm +843 -9
  12. package/core/System/Controls/Control.nm +443 -0
  13. package/core/System/Controls/DatePicker.nm +288 -0
  14. package/core/System/Controls/Form.nm +70 -0
  15. package/core/System/Controls/GroupBox.nm +267 -0
  16. package/core/System/Controls/ImageView.nm +311 -0
  17. package/core/System/Controls/LevelIndicator.nm +305 -0
  18. package/core/System/Controls/ListView.nm +633 -0
  19. package/core/System/Controls/Menu.nm +428 -0
  20. package/core/System/Controls/NumberInput.nm +56 -0
  21. package/core/System/Controls/OtpInput.nm +50 -0
  22. package/core/System/Controls/OutlineView.nm +598 -0
  23. package/core/System/Controls/Pagination.nm +142 -0
  24. package/core/System/Controls/Popover.nm +296 -0
  25. package/core/System/Controls/Progress.nm +348 -0
  26. package/core/System/Controls/Radio.nm +292 -0
  27. package/core/System/Controls/Scrollable.nm +584 -0
  28. package/core/System/Controls/SearchField.nm +326 -0
  29. package/core/System/Controls/SecureField.nm +344 -0
  30. package/core/System/Controls/Segmented.nm +380 -0
  31. package/core/System/Controls/Select.nm +340 -0
  32. package/core/System/Controls/Separator.nm +198 -0
  33. package/core/System/Controls/Sheet.nm +329 -0
  34. package/core/System/Controls/Slider.nm +315 -0
  35. package/core/System/Controls/Spinner.nm +287 -0
  36. package/core/System/Controls/SplitView.nm +509 -0
  37. package/core/System/Controls/Stepper.nm +349 -0
  38. package/core/System/Controls/Switch.nm +259 -0
  39. package/core/System/Controls/Table.nm +833 -0
  40. package/core/System/Controls/Tabs.nm +500 -0
  41. package/core/System/Controls/Text.nm +108 -11
  42. package/core/System/Controls/TextBox.nm +113 -2
  43. package/core/System/Controls/TextView.nm +329 -0
  44. package/core/System/Controls/Toggle.nm +346 -0
  45. package/core/System/Controls/Toolbar.nm +501 -0
  46. package/core/System/Controls/Tooltip.nm +76 -0
  47. package/core/System/Controls/Window.nm +1548 -91
  48. package/core/System/LinkedList.nm +8 -1
  49. package/core/System/List.nm +97 -7
  50. package/core/System/Map.nm +191 -18
  51. package/core/System/Mutex.nm +4 -4
  52. package/core/System/Random.nm +3 -4
  53. package/core/System/Set.nm +101 -13
  54. package/core/System/StableList.nm +371 -0
  55. package/core/System/Stream/Directory.nm +2 -2
  56. package/core/System/Stream/File.nm +2 -2
  57. package/core/System/Stream/Http.nm +2 -2
  58. package/core/System/String.nm +10 -5
  59. package/core/System/StringBuilder.nm +213 -2
  60. package/core/System/Text/CharIndex.nm +9 -9
  61. package/core/System/Text/Json.nm +60 -46
  62. package/core/System/Text/JsonTree.nm +120 -92
  63. package/core/System/Text/Regex.nm +23 -18
  64. package/core/System/Text/Utf8.nm +5 -5
  65. package/core/System/float.nm +36 -22
  66. package/core/System/int.nm +80 -28
  67. package/core/System/int16.nm +11 -0
  68. package/core/System/int32.nm +11 -0
  69. package/core/System/int64.nm +11 -0
  70. package/core/System/int8.nm +11 -0
  71. package/core/System/uint.nm +11 -0
  72. package/core/System/uint16.nm +11 -0
  73. package/core/System/uint32.nm +11 -0
  74. package/core/System/uint64.nm +11 -0
  75. package/core/System/uint8.nm +11 -0
  76. package/core/docs/System/StringBuilder.md +6 -0
  77. package/core/docs/System/Text/JsonTree.md +3 -2
  78. package/dist/index.mjs +38332 -30604
  79. package/package.json +1 -1
  80. package/src/bench_loop.nm +9 -9
package/audit_runtime.c CHANGED
@@ -51,13 +51,20 @@ typedef struct {
51
51
  unsigned long pad; // keeps the payload 16-byte aligned
52
52
  } nomen_audit_header;
53
53
 
54
+ // True when the NOMEN_AUDIT_TRACE env var is on. The getenv is not cached —
55
+ // the trace is a developer diagnostic, and alloc/free volumes under it are
56
+ // already dominated by the fprintf.
57
+ static int audit_trace(void) {
58
+ return getenv("NOMEN_AUDIT_TRACE") != 0;
59
+ }
60
+
54
61
  static void *audit_alloc(unsigned long size) {
55
62
  nomen_audit_header *header = (nomen_audit_header *)malloc(size + sizeof(nomen_audit_header));
56
63
  if (!header) return 0;
57
64
  header->magic = NOMEN_AUDIT_MAGIC;
58
65
  header->size = size;
59
66
  header->state = NOMEN_AUDIT_LIVE;
60
- if (getenv("NOMEN_AUDIT_TRACE")) fprintf(stderr, "ALLOC %lu\n", (unsigned long)size);
67
+ if (audit_trace()) fprintf(stderr, "ALLOC %p %lu\n", (void *)(header + 1), (unsigned long)size);
61
68
  __atomic_add_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
62
69
  return (void *)(header + 1);
63
70
  }
@@ -74,7 +81,7 @@ static nomen_audit_header *audit_header(void *ptr) {
74
81
  // Retire a block: poison the header first so a second free of the same pointer
75
82
  // is recognized as stale, then hand the memory back and drop the count.
76
83
  static void audit_release(nomen_audit_header *header) {
77
- if (getenv("NOMEN_AUDIT_TRACE")) fprintf(stderr, "FREE %lu\n", (unsigned long)header->size);
84
+ if (audit_trace()) fprintf(stderr, "FREE %p %lu\n", (void *)(header + 1), (unsigned long)header->size);
78
85
  header->magic = 0;
79
86
  header->state = NOMEN_AUDIT_DEAD;
80
87
  __atomic_sub_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
@@ -157,6 +164,9 @@ void nomen_free_wrap(void *ptr) {
157
164
  // of passing it on, and leave the count alone: an unmatched free is not
158
165
  // evidence that a live block was reclaimed.
159
166
  if (!header || header->state != NOMEN_AUDIT_LIVE) {
167
+ if (audit_trace())
168
+ fprintf(stderr, "STALE-FREE %p (%s)\n", ptr,
169
+ header ? "already freed" : "not an audit allocation");
160
170
  audit_record_stale();
161
171
  audit_report_stale(0);
162
172
  return;
@@ -77,7 +77,14 @@ pub mv struct Arena<T> {
77
77
  self.generations.store_int(index, 0)
78
78
  }
79
79
  self.values.grow(index + 1)
80
- self.values.store(index, value)
80
+ // The value arrives owned (`mv T value`): move a value-struct element
81
+ // into the fresh slot (the caller's storage zeroes, no deep copy);
82
+ // strings keep the deep-copying store (their pair arrives by value).
83
+ if T_IS_VALUE_STRUCT {
84
+ self.values.store_take(index, value)
85
+ } else {
86
+ self.values.store(index, value)
87
+ }
81
88
  var handle = ArenaRef<T>()
82
89
  handle.index = index
83
90
  handle.generation = self.generations.load_int(index)
@@ -127,7 +127,6 @@ pub struct Buffer<T> {
127
127
  self.data = calloc(size, 8)
128
128
  return self.cap
129
129
  }
130
-
131
130
  internal func grow_int = (ref self, int needed, out int: out >= needed) {
132
131
  if self.cap >= needed {
133
132
  return self.cap
@@ -171,6 +170,125 @@ pub struct Buffer<T> {
171
170
  ```
172
171
  }
173
172
 
173
+ // Labeled delegation point for raw-body callers in other containers
174
+ // (StableList's chunked storage calls this). Deliberately a RAW bitwise
175
+ // slot load — NOT a delegation to `load`: the owning-specialized `load`
176
+ // body materializes an owned copy (strdup) for string elements, while
177
+ // the accessor-family classification of every name that reaches user
178
+ // bindings (`at`, `load_tail` → this) promises a BORROW — the slot keeps
179
+ // sole ownership and the caller's binding is never freed. A strdup here
180
+ // would pair a counted allocation with a borrow binding that never frees
181
+ // it (audit LEAK).
182
+ internal func load_from = (self, int i: i >= 0 && i < self.cap, out T) {
183
+ ```
184
+ #arch: c
185
+ return ((T*)(unsigned long long)self->data)[i];
186
+ ```
187
+ ```
188
+ #arch: aarch64
189
+ // x19 = self, x1 = i. The return register convention is per type
190
+ // KIND: `string` returns the fat pair in x0/x1 (T_FAT folds to 1),
191
+ // composites larger than 8 bytes return through the x8 sret buffer,
192
+ // scalars are width-matched into x0.
193
+ mov x14, #T_FAT
194
+ cmp x14, #1
195
+ b.eq .Lbuffer_load_from_fat
196
+ ldr x0, [x19, #8]
197
+ mov x3, #T_SIZE
198
+ madd x1, x1, x3, xzr
199
+ add x0, x0, x1
200
+ cmp x3, #16
201
+ b.gt .Lbuffer_load_from_copy
202
+ cmp x3, #8
203
+ b.gt .Lbuffer_load_from_pair
204
+ cmp x3, #1
205
+ b.eq .Lbuffer_load_from_1
206
+ cmp x3, #2
207
+ b.eq .Lbuffer_load_from_2
208
+ cmp x3, #4
209
+ b.eq .Lbuffer_load_from_4
210
+ ldr x0, [x0]
211
+ b .Lbuffer_load_from_end
212
+ .Lbuffer_load_from_1:
213
+ ldrb w0, [x0]
214
+ b .Lbuffer_load_from_end
215
+ .Lbuffer_load_from_2:
216
+ ldrh w0, [x0]
217
+ b .Lbuffer_load_from_end
218
+ .Lbuffer_load_from_4:
219
+ ldr w0, [x0]
220
+ b .Lbuffer_load_from_end
221
+ .Lbuffer_load_from_pair:
222
+ // Word-pair load (T_SIZE <= 16): both words into the sret
223
+ // buffer, no call.
224
+ ldp x9, x10, [x0]
225
+ stp x9, x10, [x8]
226
+ b .Lbuffer_load_from_end
227
+ .Lbuffer_load_from_copy:
228
+ mov x1, x0
229
+ mov x0, x8
230
+ mov x2, x3
231
+ bl _memcpy
232
+ b .Lbuffer_load_from_end
233
+ .Lbuffer_load_from_fat:
234
+ ldr x0, [x19, #8]
235
+ lsl x2, x1, #4
236
+ add x0, x0, x2
237
+ ldp x0, x1, [x0]
238
+ .Lbuffer_load_from_end:
239
+ ```
240
+ }
241
+
242
+ // Initialize this buffer as a VIEW over a foreign slab (INTERNAL — the
243
+ // chunked-container primitive): `data` becomes `slab` and `cap` becomes
244
+ // `fill`, and the buffer owns NOTHING — the slab keeps its real owner
245
+ // (never grown, never freed by the view). The mono'd slot family
246
+ // (`store`/`load`/`load_from`/`shift`) then runs against the view
247
+ // unchanged, with its per-element specializations intact. This is how
248
+ // StableList's directory chunks reach the typed store without a staging
249
+ // relay — the same stack view the raw bodies build by hand, in typed
250
+ // Nomen. The holder must clear the view afterwards (see clear_view):
251
+ // an owning Buffer FIELD is destroyed by its holder's teardown.
252
+ internal func init_view = (ref self, uint64 slab, int fill: fill >= 0) {
253
+ self.data = slab
254
+ self.cap = fill
255
+ }
256
+
257
+ // Clear a view initialized by init_view: `data`/`cap` back to zero, so
258
+ // the holder's own field destroy reclaims nothing (the slab's real owner
259
+ // still owns it).
260
+ internal func clear_view = (ref self) {
261
+ self.data = 0
262
+ self.cap = 0
263
+ }
264
+
265
+ // Store `val` into slot `slot` of a foreign slab at `slab` (fill `fill`)
266
+ // through this buffer as a transient view — INTERNAL (StableList's
267
+ // directory chunks): init the view, run the mono'd `store` (the per-
268
+ // element deep-copy specialization), then clear the view so the holder's
269
+ // teardown reclaims nothing. The holder passes a Buffer FIELD, never a
270
+ // local — a local owning Buffer would be auto-destroyed at scope exit
271
+ // and free the slab it only borrows.
272
+ internal func store_in_slab = (
273
+ ref self,
274
+ uint64 slab,
275
+ int fill: fill >= 0,
276
+ int slot: slot >= 0,
277
+ T val
278
+ ) {
279
+ self.init_view(slab, fill)
280
+ // `val` arrives owned from the container's `mv T` push parameter: a
281
+ // value-struct element moves into the chunk slot (store_take — the
282
+ // caller's storage zeroes, no deep copy); strings keep the
283
+ // deep-copying store (their pair arrives by value).
284
+ if T_IS_VALUE_STRUCT {
285
+ self.store_take(slot, val)
286
+ } else {
287
+ self.store(slot, val)
288
+ }
289
+ self.clear_view()
290
+ }
291
+
174
292
  // Move a value out of a slot: return it and clear the slot to 0. This is
175
293
  // the ownership-extracting primitive (used by e.g. List.pop) — unlike
176
294
  // load_int (a borrow read), it relinquishes the slot so the buffer no
@@ -215,11 +333,20 @@ pub struct Buffer<T> {
215
333
  }
216
334
 
217
335
  // T_SIZE-aware primitives for value-struct elements. Slab is sized in
218
- // multiples of T_SIZE; load/store use memcpy so multi-word structs work
219
- // correctly. Classes route to ClassBuffer<T> (per-element destroy), so T
336
+ // multiples of T_SIZE; multi-word slots copy as one word pair at
337
+ // T_SIZE <= 16 (ldp/stp — no call) and via memcpy above that, so
338
+ // structs of any width work correctly. Classes route to ClassBuffer<T> (per-element destroy), so T
220
339
  // reaching here is always a value type (primitive or struct).
221
340
 
222
341
  func alloc = (ref self, int size, out int: out >= size) {
342
+ // Reclaim the old slab before the fresh calloc (the raw body runs
343
+ // for scalar / trivially-destructible elements — plain bytes, so a
344
+ // straight free is sound; mv element types take the specialized
345
+ // body, which per-element destroys first). The exact-cap contract
346
+ // is preserved: the fresh slab is calloc'd at exactly `size`.
347
+ if self.data != 0 {
348
+ free(self.data)
349
+ }
223
350
  self.cap = size
224
351
  self.data = calloc(size, T_SIZE)
225
352
  return self.cap
@@ -256,8 +383,10 @@ pub struct Buffer<T> {
256
383
  mov x3, #T_SIZE
257
384
  madd x1, x1, x3, xzr
258
385
  add x0, x0, x1
259
- cmp x3, #8
386
+ cmp x3, #16
260
387
  b.gt .Lbuffer_load_T_copy
388
+ cmp x3, #8
389
+ b.gt .Lbuffer_load_T_pair
261
390
  // Width-matched load (zero-extends) — an 8-byte ldr on a sub-8
262
391
  // element would read past the slab's end and leave garbage in the
263
392
  // upper bits.
@@ -278,6 +407,12 @@ pub struct Buffer<T> {
278
407
  .Lbuffer_load_T_4:
279
408
  ldr w0, [x0]
280
409
  b .Lbuffer_load_T_end
410
+ .Lbuffer_load_T_pair:
411
+ // Word-pair load (T_SIZE <= 16): both words into the sret
412
+ // buffer, no call.
413
+ ldp x9, x10, [x0]
414
+ stp x9, x10, [x8]
415
+ b .Lbuffer_load_T_end
281
416
  .Lbuffer_load_T_copy:
282
417
  mov x1, x0
283
418
  mov x0, x8
@@ -307,8 +442,10 @@ pub struct Buffer<T> {
307
442
  mov x3, #T_SIZE
308
443
  madd x1, x1, x3, xzr
309
444
  add x0, x0, x1
310
- cmp x3, #8
445
+ cmp x3, #16
311
446
  b.gt .Lbuffer_store_T_copy
447
+ cmp x3, #8
448
+ b.gt .Lbuffer_store_T_pair
312
449
  // Sub-8 element types must use a width-matched store: an 8-byte str
313
450
  // would clobber the following slots (and run past the slab's end).
314
451
  cmp x3, #1
@@ -328,6 +465,12 @@ pub struct Buffer<T> {
328
465
  .Lbuffer_store_T_4:
329
466
  str w2, [x0]
330
467
  b .Lbuffer_store_T_end
468
+ .Lbuffer_store_T_pair:
469
+ // Word-pair store (T_SIZE <= 16): the value's address (x2)
470
+ // feeds both words, no call.
471
+ ldp x9, x10, [x2]
472
+ stp x9, x10, [x0]
473
+ b .Lbuffer_store_T_end
331
474
  .Lbuffer_store_T_copy:
332
475
  mov x1, x2
333
476
  mov x2, x3
@@ -336,6 +479,94 @@ pub struct Buffer<T> {
336
479
  ```
337
480
  }
338
481
 
482
+ // TAKE a value into slot `i`. The caller's storage (`val`'s source — on
483
+ // both backends a value-struct parameter arrives as a POINTER to the
484
+ // caller's value) is byte-copied into the fresh slot and then ZEROED, so
485
+ // the buffer becomes the sole owner and the caller's own teardown frees
486
+ // nothing. The ownership-taking counterpart of `store` (which deep-copies
487
+ // a borrowed value): the `add`/`alloc` family's bodies use it when
488
+ // `T_IS_VALUE_STRUCT` folds true, eliding the per-slot deep copy.
489
+ // INTERNAL: only valid for a value-struct `T` (the only shape whose
490
+ // parameter is a pointer to the caller's storage — a `string`'s fat pair
491
+ // arrives by value, so take-store would zero the callee's copy and leave
492
+ // the caller's buffer shared).
493
+ internal inline func store_take = (ref self, int i: i >= 0 && i < self.cap, T val) {
494
+ ```
495
+ #arch: c
496
+ {
497
+ T* _st = (T*)(unsigned long long)self->data;
498
+ #if T_IS_VALUE_STRUCT
499
+ // A value-struct parameter is a POINTER to the caller's storage.
500
+ T* _src = &(*val);
501
+ _st[i] = *_src;
502
+ memset((void*)_src, 0, T_SIZE);
503
+ #else
504
+ // Never called for a non-value-struct instantiation (the container
505
+ // bodies fold their `T_IS_VALUE_STRUCT` arm); this arm only keeps the
506
+ // emitted instance compiling.
507
+ T _tmp = val;
508
+ T* _src = &_tmp;
509
+ _st[i] = *_src;
510
+ (void)_src;
511
+ #endif
512
+ }
513
+ ```
514
+ ```
515
+ #arch: aarch64
516
+ // x19 = self, x1 = i, x2 = &val (the caller's storage)
517
+ ldr x0, [x19, #8]
518
+ mov x3, #T_SIZE
519
+ madd x1, x1, x3, xzr
520
+ add x0, x0, x1
521
+ cmp x3, #16
522
+ b.gt .Lbuffer_take_T_copy
523
+ cmp x3, #8
524
+ b.gt .Lbuffer_take_T_pair
525
+ cmp x3, #1
526
+ b.eq .Lbuffer_take_T_1
527
+ cmp x3, #2
528
+ b.eq .Lbuffer_take_T_2
529
+ cmp x3, #4
530
+ b.eq .Lbuffer_take_T_4
531
+ ldr x9, [x2]
532
+ str x9, [x0]
533
+ str xzr, [x2]
534
+ b .Lbuffer_take_T_end
535
+ .Lbuffer_take_T_1:
536
+ ldrb w9, [x2]
537
+ strb w9, [x0]
538
+ strb wzr, [x2]
539
+ b .Lbuffer_take_T_end
540
+ .Lbuffer_take_T_2:
541
+ ldrh w9, [x2]
542
+ strh w9, [x0]
543
+ strh wzr, [x2]
544
+ b .Lbuffer_take_T_end
545
+ .Lbuffer_take_T_4:
546
+ ldr w9, [x2]
547
+ str w9, [x0]
548
+ str wzr, [x2]
549
+ b .Lbuffer_take_T_end
550
+ .Lbuffer_take_T_pair:
551
+ ldp x9, x10, [x2]
552
+ stp x9, x10, [x0]
553
+ stp xzr, xzr, [x2]
554
+ b .Lbuffer_take_T_end
555
+ .Lbuffer_take_T_copy:
556
+ mov x4, x2
557
+ mov x1, x2
558
+ mov x2, x3
559
+ stp x4, x3, [sp, #-16]!
560
+ bl _memcpy
561
+ ldp x4, x3, [sp], #16
562
+ mov x0, x4
563
+ mov x1, #0
564
+ mov x2, x3
565
+ bl _memset
566
+ .Lbuffer_take_T_end:
567
+ ```
568
+ }
569
+
339
570
  // Move a value out of a T_SIZE-sized slot: return it and zero the slot so the
340
571
  // buffer no longer holds a (possibly mv) copy. Declared `mv out T` so
341
572
  // the result is treated as OWNED by the borrow checker — the slot is
@@ -356,8 +587,10 @@ pub struct Buffer<T> {
356
587
  mov x3, #T_SIZE
357
588
  madd x5, x1, x3, xzr
358
589
  add x5, x4, x5
359
- cmp x3, #8
590
+ cmp x3, #16
360
591
  b.gt .Lbuffer_move_T_copy
592
+ cmp x3, #8
593
+ b.gt .Lbuffer_move_T_pair
361
594
  // Width-matched load + zero-clear: an 8-byte access on a sub-8
362
595
  // element would zero the following slots and run past the slab.
363
596
  cmp x3, #1
@@ -381,6 +614,14 @@ pub struct Buffer<T> {
381
614
  ldr w0, [x5]
382
615
  str wzr, [x5]
383
616
  b .Lbuffer_move_T_end
617
+ .Lbuffer_move_T_pair:
618
+ // Word-pair move (T_SIZE <= 16): the sret pair takes both
619
+ // words and the slot zeroes, no calls.
620
+ ldp x9, x10, [x5]
621
+ stp x9, x10, [x8]
622
+ stp xzr, xzr, [x5]
623
+ mov x0, x8
624
+ b .Lbuffer_move_T_end
384
625
  .Lbuffer_move_T_copy:
385
626
  mov x0, x8
386
627
  mov x1, x5
@@ -510,8 +751,10 @@ pub struct Buffer<T> {
510
751
  mov x3, #T_SIZE
511
752
  madd x1, x1, x3, xzr
512
753
  add x0, x0, x1
513
- cmp x3, #8
754
+ cmp x3, #16
514
755
  b.gt .Lbuffer_replace_T_copy
756
+ cmp x3, #8
757
+ b.gt .Lbuffer_replace_T_pair
515
758
  // Width-matched store: an 8-byte str on a sub-8 element would
516
759
  // clobber the following slots and run past the slab's end.
517
760
  cmp x3, #1
@@ -531,6 +774,12 @@ pub struct Buffer<T> {
531
774
  .Lbuffer_replace_T_4:
532
775
  str w2, [x0]
533
776
  b .Lbuffer_replace_T_end
777
+ .Lbuffer_replace_T_pair:
778
+ // Word-pair store (T_SIZE <= 16): the value's address (x2)
779
+ // feeds both words, no call.
780
+ ldp x9, x10, [x2]
781
+ stp x9, x10, [x0]
782
+ b .Lbuffer_replace_T_end
534
783
  .Lbuffer_replace_T_copy:
535
784
  mov x1, x2
536
785
  mov x2, x3
@@ -550,6 +799,76 @@ pub struct Buffer<T> {
550
799
  self.replace(i, value)
551
800
  }
552
801
 
802
+ // Reclaim slot i's owned heap state WITHOUT writing the slot (the raw
803
+ // no-op below is correct for trivially-destructible elements; for
804
+ // `string` and mv value-struct elements both backends specialize this
805
+ // body — free the displaced pointer / guarded T_destroy — the same
806
+ // dispatch replace_T gets). This is the DESTROY half of replace,
807
+ // separated from the copy so the in-place store lowering can reclaim
808
+ // the displaced value and then CONSTRUCT the new one straight into the
809
+ // slot (no donor, no deep copy). Contract: the slot must be
810
+ // overwritten before it is read or destroyed again — Map.set_slot_ptr
811
+ // (the probe-then-construct primitive) and the in-place `set` splice
812
+ // are the callers, and both overwrite immediately.
813
+ internal func destroy_at = (ref self, int i: i >= 0 && i < self.cap) {
814
+ ```
815
+ #arch: c
816
+ T* _slots = (T*)(unsigned long long)self->data;
817
+ #if T_NEEDS_STRDUP
818
+ // A string slot owns its strdup'd bytes — free the buffer before the
819
+ // zero. Owning value-struct elements never reach this body: their
820
+ // destroy_at is replaced at the call site by the owning-buffer
821
+ // specialization (payload reclaim + zero).
822
+ free(_slots[i].ptr);
823
+ #endif
824
+ memset((void*)&_slots[i], 0, T_SIZE);
825
+ ```
826
+ ```
827
+ #arch: aarch64
828
+ // Same contract as the C body: string slots free their strdup'd
829
+ // buffer first (the T_NEEDS_STRDUP dispatch folds away for every
830
+ // other element kind); owning value-struct elements take the
831
+ // call-site specialization instead.
832
+ ldr x4, [x19, #8]
833
+ mov x3, #T_SIZE
834
+ madd x5, x1, x3, xzr
835
+ add x5, x4, x5
836
+ mov x9, #T_NEEDS_STRDUP
837
+ cbz x9, .Lbuffer_destroy_at_T_zero
838
+ ldr x0, [x5]
839
+ bl _free
840
+ .Lbuffer_destroy_at_T_zero:
841
+ mov x0, x5
842
+ mov x1, #0
843
+ mov x2, #T_SIZE
844
+ bl _memset
845
+ ```
846
+ }
847
+
848
+ // Address of slot `i` (library-only): the ELEMENT BORROW primitive —
849
+ // `ref T x = list.at(i)` (docs/HANDLERS.md Stage 4, the dynamic-tree
850
+ // dispatch) lowers to this, binding the alias to the slot itself rather
851
+ // than to a bitwise copy. The pointer is valid while the buffer is
852
+ // exclusively borrowed and not resized; a `ref self` call on the buffer
853
+ // (push/pop/set/…) invalidates the borrow through the ordinary
854
+ // borrow-invalidation rules. Library-internal: the caller (the `List`
855
+ // wrapper, or trusted internals) bounds-checks; the body is raw-only so
856
+ // the missing-return net does not apply to the raw return.
857
+ internal func slot_ptr = (ref self, int i, out ptr T) {
858
+ ```
859
+ #arch: c
860
+ return (T*)(unsigned long long)self->data + i;
861
+ ```
862
+ ```
863
+ #arch: aarch64
864
+ // x19 = self, x1 = i — the slot address (the load primitive's
865
+ // address-of: data + i * T_SIZE), returned in x0.
866
+ ldr x8, [x19, #8]
867
+ mov x9, #T_SIZE
868
+ madd x0, x1, x9, x8
869
+ ```
870
+ }
871
+
553
872
  // Move slot src into slot dst: overwrite dst with src's bytes and zero
554
873
  // src, so exactly ONE slot owns the value afterwards. The
555
874
  // ownership-transferring primitive behind backward-shift deletion
@@ -582,8 +901,10 @@ pub struct Buffer<T> {
582
901
  madd x6, x2, x3, x4
583
902
  cmp x5, x6
584
903
  b.eq .Lbuffer_shift_T_end
585
- cmp x3, #8
904
+ cmp x3, #16
586
905
  b.gt .Lbuffer_shift_T_copy
906
+ cmp x3, #8
907
+ b.gt .Lbuffer_shift_T_pair
587
908
  // Width-matched mv: an 8-byte access on a sub-8 element would
588
909
  // clobber the following slot (and run past the slab's end).
589
910
  cmp x3, #1
@@ -611,6 +932,13 @@ pub struct Buffer<T> {
611
932
  str w7, [x5]
612
933
  str wzr, [x6]
613
934
  b .Lbuffer_shift_T_end
935
+ .Lbuffer_shift_T_pair:
936
+ // Word-pair shift (T_SIZE <= 16): both words move wholesale
937
+ // and the source pair zeroes, no calls.
938
+ ldp x7, x9, [x6]
939
+ stp x7, x9, [x5]
940
+ stp xzr, xzr, [x6]
941
+ b .Lbuffer_shift_T_end
614
942
  .Lbuffer_shift_T_copy:
615
943
  mov x0, x5
616
944
  mov x1, x6
@@ -626,6 +954,67 @@ pub struct Buffer<T> {
626
954
  ```
627
955
  }
628
956
 
957
+ // Bulk transfer for backward-shift deletion: move the `count` slots at
958
+ // `src` down to `dst` (src >= dst) as ONE block move, then zero the
959
+ // source slots the destination never covers — for the contiguous
960
+ // deletion shape (src = dst + 1) that is exactly one tail slot, so a
961
+ // whole range shift costs one memmove + one memset instead of
962
+ // count × (memcpy + memset) per-element `shift` calls. PROOF
963
+ // OBLIGATION on the caller (INTERNAL, byte-level like `shift`): every
964
+ // slot in [dst, dst + count) must be DEAD — zeroed by a move-out or
965
+ // otherwise unowned — because the block move overwrites them without
966
+ // reclaiming (the ownership proof is why the deletion algorithms zero
967
+ // and relocate in lockstep; a live displaced value belongs in `shift`,
968
+ // which reclaims dst first). The bytes transfer wholesale, so the move
969
+ // carries mv elements' heap pointers with them — correct for every
970
+ // element kind, raw for both backends (never names T).
971
+ internal inline func move_range = (
972
+ ref self,
973
+ int dst: dst >= 0,
974
+ int src: src >= dst,
975
+ int count: count >= 0 && src + count <= self.cap
976
+ ) {
977
+ ```
978
+ #arch: c
979
+ if (count > 0) {
980
+ T* _slots = (T*)(unsigned long long)self->data;
981
+ memmove((void*)&_slots[dst], (void*)&_slots[src],
982
+ (unsigned long long)count * T_SIZE);
983
+ // Zero the source slots the destination does not overwrite:
984
+ // [dst + count, src + count) — one slot for the contiguous
985
+ // deletion shape, the gap+1 slots for a strided one.
986
+ memset((void*)&_slots[dst + count], 0,
987
+ (unsigned long long)(src - dst) * T_SIZE);
988
+ }
989
+ ```
990
+ ```
991
+ #arch: aarch64
992
+ // x19 = self, x1 = dst, x2 = src, x3 = count. x0-x18 die across the
993
+ // bl calls, so the block math parks on the stack around _memmove.
994
+ cbz x3, .Lbuffer_move_range_T_end
995
+ ldr x4, [x19, #8]
996
+ mov x5, #T_SIZE
997
+ madd x6, x1, x5, x4
998
+ madd x7, x2, x5, x4
999
+ madd x9, x3, x5, xzr
1000
+ sub x10, x2, x1
1001
+ madd x10, x10, x5, xzr
1002
+ stp x6, x9, [sp, #-16]!
1003
+ str x10, [sp, #-16]!
1004
+ mov x0, x6
1005
+ mov x1, x7
1006
+ mov x2, x9
1007
+ bl _memmove
1008
+ ldr x10, [sp], #16
1009
+ ldp x6, x9, [sp], #16
1010
+ add x0, x6, x9
1011
+ mov x1, #0
1012
+ mov x2, x10
1013
+ bl _memset
1014
+ .Lbuffer_move_range_T_end:
1015
+ ```
1016
+ }
1017
+
629
1018
  // A non-mv (ptr, len) slice [start, end) over this buffer's element
630
1019
  // storage, returned as a `view T`. It borrows from self: it may not outlive
631
1020
  // self's scope and is invalidated if self is reassigned. This is the single