nomen-lang 0.8.0 → 0.10.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/audit_runtime.c +12 -2
- package/core/System/Arena.nm +14 -5
- package/core/System/Array.nm +1 -1
- package/core/System/Atomic.nm +3 -3
- package/core/System/BigInt.nm +182 -23
- package/core/System/Buffer.nm +392 -33
- package/core/System/ClassBuffer.nm +145 -13
- package/core/System/Controls/Alert.nm +3 -3
- package/core/System/Controls/App.nm +17 -17
- package/core/System/Controls/Button.nm +2 -2
- package/core/System/Controls/CheckBox.nm +3 -3
- package/core/System/Controls/ColorPicker.nm +2 -2
- package/core/System/Controls/ComboBox.nm +2 -2
- package/core/System/Controls/Container.nm +39 -33
- package/core/System/Controls/DatePicker.nm +1 -1
- package/core/System/Controls/Form.nm +2 -2
- package/core/System/Controls/GroupBox.nm +2 -2
- package/core/System/Controls/ImageView.nm +2 -2
- package/core/System/Controls/Layout.nm +5 -5
- package/core/System/Controls/LevelIndicator.nm +2 -2
- package/core/System/Controls/ListView.nm +2 -2
- package/core/System/Controls/Menu.nm +2 -2
- package/core/System/Controls/NumberInput.nm +3 -3
- package/core/System/Controls/OtpInput.nm +2 -2
- package/core/System/Controls/OutlineView.nm +2 -2
- package/core/System/Controls/Pagination.nm +14 -14
- package/core/System/Controls/Popover.nm +2 -2
- package/core/System/Controls/Progress.nm +2 -2
- package/core/System/Controls/Radio.nm +3 -3
- package/core/System/Controls/Scrollable.nm +1 -1
- package/core/System/Controls/SearchField.nm +2 -2
- package/core/System/Controls/SecureField.nm +2 -2
- package/core/System/Controls/Segmented.nm +2 -2
- package/core/System/Controls/Select.nm +2 -2
- package/core/System/Controls/Separator.nm +2 -2
- package/core/System/Controls/Sheet.nm +2 -2
- package/core/System/Controls/Slider.nm +2 -2
- package/core/System/Controls/Spinner.nm +5 -5
- package/core/System/Controls/SplitView.nm +3 -3
- package/core/System/Controls/Stepper.nm +2 -2
- package/core/System/Controls/Switch.nm +2 -2
- package/core/System/Controls/Table.nm +2 -2
- package/core/System/Controls/Tabs.nm +2 -2
- package/core/System/Controls/Text.nm +2 -2
- package/core/System/Controls/TextBox.nm +3 -3
- package/core/System/Controls/TextView.nm +2 -2
- package/core/System/Controls/Toggle.nm +3 -3
- package/core/System/Controls/Toolbar.nm +2 -2
- package/core/System/Controls/Window.nm +12 -5
- package/core/System/Fiber.nm +16 -3
- package/core/System/Graph.nm +2 -2
- package/core/System/LinkedList.nm +13 -4
- package/core/System/List.nm +76 -17
- package/core/System/Map.nm +179 -21
- package/core/System/Mutex.nm +4 -4
- package/core/System/Random.nm +6 -7
- package/core/System/Set.nm +99 -23
- package/core/System/StableList.nm +371 -0
- package/core/System/Stream/Directory.nm +2 -2
- package/core/System/Stream/File.nm +18 -18
- package/core/System/Stream/Http.nm +5 -5
- package/core/System/Stream/Tcp.nm +5 -5
- package/core/System/String.nm +10 -5
- package/core/System/StringBuilder.nm +223 -12
- package/core/System/Task.nm +56 -64
- package/core/System/Test.nm +18 -18
- package/core/System/Text/CharIndex.nm +9 -9
- package/core/System/Text/Chars.nm +1 -1
- package/core/System/Text/Json.nm +72 -58
- package/core/System/Text/JsonTree.nm +126 -98
- package/core/System/Text/Regex.nm +43 -38
- package/core/System/Text/Utf8.nm +5 -5
- package/core/System/Thread.nm +26 -3
- package/core/System/Tree.nm +19 -3
- package/core/System/float.nm +36 -22
- package/core/System/int.nm +80 -28
- package/core/System/int16.nm +11 -0
- package/core/System/int32.nm +11 -0
- package/core/System/int64.nm +11 -0
- package/core/System/int8.nm +11 -0
- package/core/System/uint.nm +11 -0
- package/core/System/uint16.nm +11 -0
- package/core/System/uint32.nm +11 -0
- package/core/System/uint64.nm +11 -0
- package/core/System/uint8.nm +11 -0
- package/core/docs/System/StringBuilder.md +6 -0
- package/core/docs/System/Text/JsonTree.md +3 -2
- package/dist/index.mjs +37622 -30603
- package/package.json +1 -1
- package/src/bench_loop.nm +9 -9
package/core/System/Buffer.nm
CHANGED
|
@@ -23,13 +23,13 @@ pub struct Buffer<T> {
|
|
|
23
23
|
// both backends. They run once per container op (never per element), so
|
|
24
24
|
// the aarch64 inline-method ABI cost that keeps the per-limb load/store
|
|
25
25
|
// primitives raw (see BigInt.nm) doesn't apply here.
|
|
26
|
-
internal func alloc_u32 = (ref self, int size, out int: out >= size) {
|
|
26
|
+
internal func alloc_u32 = (ref self {}, int size, out int: out >= size) {
|
|
27
27
|
self.cap = size
|
|
28
28
|
self.data = calloc(size, 4)
|
|
29
29
|
return self.cap
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
internal func grow_u32 = (ref self, int needed, out int: out >= needed) {
|
|
32
|
+
internal func grow_u32 = (ref self {}, int needed, out int: out >= needed) {
|
|
33
33
|
if self.cap >= needed {
|
|
34
34
|
return self.cap
|
|
35
35
|
}
|
|
@@ -44,7 +44,7 @@ pub struct Buffer<T> {
|
|
|
44
44
|
return self.cap
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
internal func zero_u32 = (ref self, int len) {
|
|
47
|
+
internal func zero_u32 = (ref self {}, int len) {
|
|
48
48
|
memset(self.data, 0, len * 4)
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -63,7 +63,7 @@ pub struct Buffer<T> {
|
|
|
63
63
|
```
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
internal inline func store_u32 = (ref self, int i: i >= 0 && i < self.cap, uint32 val) {
|
|
66
|
+
internal inline func store_u32 = (ref self {}, int i: i >= 0 && i < self.cap, uint32 val) {
|
|
67
67
|
```
|
|
68
68
|
#arch: c
|
|
69
69
|
((unsigned int*)(unsigned long long)self->data)[i] = val;
|
|
@@ -77,7 +77,7 @@ pub struct Buffer<T> {
|
|
|
77
77
|
```
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
internal inline func store_or_u32 = (ref self, int i: i >= 0 && i < self.cap, uint32 val) {
|
|
80
|
+
internal inline func store_or_u32 = (ref self {}, int i: i >= 0 && i < self.cap, uint32 val) {
|
|
81
81
|
```
|
|
82
82
|
#arch: c
|
|
83
83
|
((unsigned int*)(unsigned long long)self->data)[i] |= val;
|
|
@@ -109,7 +109,7 @@ pub struct Buffer<T> {
|
|
|
109
109
|
```
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
internal inline func store_u8 = (ref self, int i: i >= 0 && i < self.cap, uint8 val) {
|
|
112
|
+
internal inline func store_u8 = (ref self {}, int i: i >= 0 && i < self.cap, uint8 val) {
|
|
113
113
|
```
|
|
114
114
|
#arch: c
|
|
115
115
|
((unsigned char*)(unsigned long long)self->data)[i] = val;
|
|
@@ -122,13 +122,12 @@ pub struct Buffer<T> {
|
|
|
122
122
|
```
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
internal func alloc_int = (ref self, int size, out int: out >= size) {
|
|
125
|
+
internal func alloc_int = (ref self {}, int size, out int: out >= size) {
|
|
126
126
|
self.cap = size
|
|
127
127
|
self.data = calloc(size, 8)
|
|
128
128
|
return self.cap
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
internal func grow_int = (ref self, int needed, out int: out >= needed) {
|
|
130
|
+
internal func grow_int = (ref self {}, int needed, out int: out >= needed) {
|
|
132
131
|
if self.cap >= needed {
|
|
133
132
|
return self.cap
|
|
134
133
|
}
|
|
@@ -157,7 +156,7 @@ pub struct Buffer<T> {
|
|
|
157
156
|
```
|
|
158
157
|
}
|
|
159
158
|
|
|
160
|
-
internal inline func store_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
159
|
+
internal inline func store_int = (ref self {}, int i: i >= 0 && i < self.cap, int val) {
|
|
161
160
|
```
|
|
162
161
|
#arch: c
|
|
163
162
|
((long*)(unsigned long long)self->data)[i] = val;
|
|
@@ -171,11 +170,132 @@ 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 that OWNS resources moves into the chunk slot
|
|
282
|
+
// (store_take — the caller's storage zeroes, no deep copy). A
|
|
283
|
+
// non-owning value struct is a legal bare argument, so it keeps the
|
|
284
|
+
// copying store (taking it would zero the caller's copy); strings
|
|
285
|
+
// keep the deep-copying store too (their pair arrives by value).
|
|
286
|
+
if T_TAKES_OWNERSHIP {
|
|
287
|
+
self.store_take(slot, val)
|
|
288
|
+
} else {
|
|
289
|
+
self.store(slot, val)
|
|
290
|
+
}
|
|
291
|
+
self.clear_view()
|
|
292
|
+
}
|
|
293
|
+
|
|
174
294
|
// Move a value out of a slot: return it and clear the slot to 0. This is
|
|
175
295
|
// the ownership-extracting primitive (used by e.g. List.pop) — unlike
|
|
176
296
|
// load_int (a borrow read), it relinquishes the slot so the buffer no
|
|
177
297
|
// longer owns the value.
|
|
178
|
-
internal inline func move_int = (ref self, int i: i >= 0 && i < self.cap, out int) {
|
|
298
|
+
internal inline func move_int = (ref self {}, int i: i >= 0 && i < self.cap, out int) {
|
|
179
299
|
```
|
|
180
300
|
#arch: c
|
|
181
301
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -196,7 +316,7 @@ pub struct Buffer<T> {
|
|
|
196
316
|
// Store into a slot, freeing the previous value when the buffer holds
|
|
197
317
|
// owned class pointers (has_class_refs). For non-class buffers this is a
|
|
198
318
|
// plain store, so it is safe to use anywhere store_int would be.
|
|
199
|
-
internal inline func replace_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
319
|
+
internal inline func replace_int = (ref self {}, int i: i >= 0 && i < self.cap, int val) {
|
|
200
320
|
```
|
|
201
321
|
#arch: c
|
|
202
322
|
((long*)(unsigned long long)self->data)[i] = val;
|
|
@@ -210,16 +330,17 @@ pub struct Buffer<T> {
|
|
|
210
330
|
```
|
|
211
331
|
}
|
|
212
332
|
|
|
213
|
-
internal inline func zero_int = (ref self, int len) {
|
|
333
|
+
internal inline func zero_int = (ref self {}, int len) {
|
|
214
334
|
memset(self.data, 0, len * 8)
|
|
215
335
|
}
|
|
216
336
|
|
|
217
337
|
// T_SIZE-aware primitives for value-struct elements. Slab is sized in
|
|
218
|
-
// multiples of T_SIZE;
|
|
219
|
-
//
|
|
338
|
+
// multiples of T_SIZE; multi-word slots copy as one word pair at
|
|
339
|
+
// T_SIZE <= 16 (ldp/stp — no call) and via memcpy above that, so
|
|
340
|
+
// structs of any width work correctly. Classes route to ClassBuffer<T> (per-element destroy), so T
|
|
220
341
|
// reaching here is always a value type (primitive or struct).
|
|
221
342
|
|
|
222
|
-
func alloc = (ref self, int size, out int: out >= size) {
|
|
343
|
+
func alloc = (ref self {}, int size, out int: out >= size) {
|
|
223
344
|
// Reclaim the old slab before the fresh calloc (the raw body runs
|
|
224
345
|
// for scalar / trivially-destructible elements — plain bytes, so a
|
|
225
346
|
// straight free is sound; mv element types take the specialized
|
|
@@ -233,7 +354,7 @@ pub struct Buffer<T> {
|
|
|
233
354
|
return self.cap
|
|
234
355
|
}
|
|
235
356
|
|
|
236
|
-
func grow = (ref self, int needed, out int: out >= needed) {
|
|
357
|
+
func grow = (ref self {}, int needed, out int: out >= needed) {
|
|
237
358
|
if self.cap >= needed {
|
|
238
359
|
return self.cap
|
|
239
360
|
}
|
|
@@ -264,8 +385,10 @@ pub struct Buffer<T> {
|
|
|
264
385
|
mov x3, #T_SIZE
|
|
265
386
|
madd x1, x1, x3, xzr
|
|
266
387
|
add x0, x0, x1
|
|
267
|
-
cmp x3, #
|
|
388
|
+
cmp x3, #16
|
|
268
389
|
b.gt .Lbuffer_load_T_copy
|
|
390
|
+
cmp x3, #8
|
|
391
|
+
b.gt .Lbuffer_load_T_pair
|
|
269
392
|
// Width-matched load (zero-extends) — an 8-byte ldr on a sub-8
|
|
270
393
|
// element would read past the slab's end and leave garbage in the
|
|
271
394
|
// upper bits.
|
|
@@ -286,6 +409,12 @@ pub struct Buffer<T> {
|
|
|
286
409
|
.Lbuffer_load_T_4:
|
|
287
410
|
ldr w0, [x0]
|
|
288
411
|
b .Lbuffer_load_T_end
|
|
412
|
+
.Lbuffer_load_T_pair:
|
|
413
|
+
// Word-pair load (T_SIZE <= 16): both words into the sret
|
|
414
|
+
// buffer, no call.
|
|
415
|
+
ldp x9, x10, [x0]
|
|
416
|
+
stp x9, x10, [x8]
|
|
417
|
+
b .Lbuffer_load_T_end
|
|
289
418
|
.Lbuffer_load_T_copy:
|
|
290
419
|
mov x1, x0
|
|
291
420
|
mov x0, x8
|
|
@@ -303,7 +432,7 @@ pub struct Buffer<T> {
|
|
|
303
432
|
// value) — this is the primitive every container's `set` uses.
|
|
304
433
|
// Internal: assumes a FRESH slot (see the contract comment). The public
|
|
305
434
|
// write is `replace`, which is correct on fresh and occupied slots alike.
|
|
306
|
-
internal inline func store = (ref self, int i: i >= 0 && i < self.cap, T val) {
|
|
435
|
+
internal inline func store = (ref self {}, int i: i >= 0 && i < self.cap, T val) {
|
|
307
436
|
```
|
|
308
437
|
#arch: c
|
|
309
438
|
((T*)(unsigned long long)self->data)[i] = val;
|
|
@@ -315,8 +444,10 @@ pub struct Buffer<T> {
|
|
|
315
444
|
mov x3, #T_SIZE
|
|
316
445
|
madd x1, x1, x3, xzr
|
|
317
446
|
add x0, x0, x1
|
|
318
|
-
cmp x3, #
|
|
447
|
+
cmp x3, #16
|
|
319
448
|
b.gt .Lbuffer_store_T_copy
|
|
449
|
+
cmp x3, #8
|
|
450
|
+
b.gt .Lbuffer_store_T_pair
|
|
320
451
|
// Sub-8 element types must use a width-matched store: an 8-byte str
|
|
321
452
|
// would clobber the following slots (and run past the slab's end).
|
|
322
453
|
cmp x3, #1
|
|
@@ -336,6 +467,12 @@ pub struct Buffer<T> {
|
|
|
336
467
|
.Lbuffer_store_T_4:
|
|
337
468
|
str w2, [x0]
|
|
338
469
|
b .Lbuffer_store_T_end
|
|
470
|
+
.Lbuffer_store_T_pair:
|
|
471
|
+
// Word-pair store (T_SIZE <= 16): the value's address (x2)
|
|
472
|
+
// feeds both words, no call.
|
|
473
|
+
ldp x9, x10, [x2]
|
|
474
|
+
stp x9, x10, [x0]
|
|
475
|
+
b .Lbuffer_store_T_end
|
|
339
476
|
.Lbuffer_store_T_copy:
|
|
340
477
|
mov x1, x2
|
|
341
478
|
mov x2, x3
|
|
@@ -344,12 +481,100 @@ pub struct Buffer<T> {
|
|
|
344
481
|
```
|
|
345
482
|
}
|
|
346
483
|
|
|
484
|
+
// TAKE a value into slot `i`. The caller's storage (`val`'s source — on
|
|
485
|
+
// both backends a value-struct parameter arrives as a POINTER to the
|
|
486
|
+
// caller's value) is byte-copied into the fresh slot and then ZEROED, so
|
|
487
|
+
// the buffer becomes the sole owner and the caller's own teardown frees
|
|
488
|
+
// nothing. The ownership-taking counterpart of `store` (which deep-copies
|
|
489
|
+
// a borrowed value): the `add`/`alloc` family's bodies use it when
|
|
490
|
+
// `T_IS_VALUE_STRUCT` folds true, eliding the per-slot deep copy.
|
|
491
|
+
// INTERNAL: only valid for a value-struct `T` (the only shape whose
|
|
492
|
+
// parameter is a pointer to the caller's storage — a `string`'s fat pair
|
|
493
|
+
// arrives by value, so take-store would zero the callee's copy and leave
|
|
494
|
+
// the caller's buffer shared).
|
|
495
|
+
internal inline func store_take = (ref self {}, int i: i >= 0 && i < self.cap, T val) {
|
|
496
|
+
```
|
|
497
|
+
#arch: c
|
|
498
|
+
{
|
|
499
|
+
T* _st = (T*)(unsigned long long)self->data;
|
|
500
|
+
#if T_IS_VALUE_STRUCT
|
|
501
|
+
// A value-struct parameter is a POINTER to the caller's storage.
|
|
502
|
+
T* _src = &(*val);
|
|
503
|
+
_st[i] = *_src;
|
|
504
|
+
memset((void*)_src, 0, T_SIZE);
|
|
505
|
+
#else
|
|
506
|
+
// Never called for a non-value-struct instantiation (the container
|
|
507
|
+
// bodies fold their `T_IS_VALUE_STRUCT` arm); this arm only keeps the
|
|
508
|
+
// emitted instance compiling.
|
|
509
|
+
T _tmp = val;
|
|
510
|
+
T* _src = &_tmp;
|
|
511
|
+
_st[i] = *_src;
|
|
512
|
+
(void)_src;
|
|
513
|
+
#endif
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
```
|
|
517
|
+
#arch: aarch64
|
|
518
|
+
// x19 = self, x1 = i, x2 = &val (the caller's storage)
|
|
519
|
+
ldr x0, [x19, #8]
|
|
520
|
+
mov x3, #T_SIZE
|
|
521
|
+
madd x1, x1, x3, xzr
|
|
522
|
+
add x0, x0, x1
|
|
523
|
+
cmp x3, #16
|
|
524
|
+
b.gt .Lbuffer_take_T_copy
|
|
525
|
+
cmp x3, #8
|
|
526
|
+
b.gt .Lbuffer_take_T_pair
|
|
527
|
+
cmp x3, #1
|
|
528
|
+
b.eq .Lbuffer_take_T_1
|
|
529
|
+
cmp x3, #2
|
|
530
|
+
b.eq .Lbuffer_take_T_2
|
|
531
|
+
cmp x3, #4
|
|
532
|
+
b.eq .Lbuffer_take_T_4
|
|
533
|
+
ldr x9, [x2]
|
|
534
|
+
str x9, [x0]
|
|
535
|
+
str xzr, [x2]
|
|
536
|
+
b .Lbuffer_take_T_end
|
|
537
|
+
.Lbuffer_take_T_1:
|
|
538
|
+
ldrb w9, [x2]
|
|
539
|
+
strb w9, [x0]
|
|
540
|
+
strb wzr, [x2]
|
|
541
|
+
b .Lbuffer_take_T_end
|
|
542
|
+
.Lbuffer_take_T_2:
|
|
543
|
+
ldrh w9, [x2]
|
|
544
|
+
strh w9, [x0]
|
|
545
|
+
strh wzr, [x2]
|
|
546
|
+
b .Lbuffer_take_T_end
|
|
547
|
+
.Lbuffer_take_T_4:
|
|
548
|
+
ldr w9, [x2]
|
|
549
|
+
str w9, [x0]
|
|
550
|
+
str wzr, [x2]
|
|
551
|
+
b .Lbuffer_take_T_end
|
|
552
|
+
.Lbuffer_take_T_pair:
|
|
553
|
+
ldp x9, x10, [x2]
|
|
554
|
+
stp x9, x10, [x0]
|
|
555
|
+
stp xzr, xzr, [x2]
|
|
556
|
+
b .Lbuffer_take_T_end
|
|
557
|
+
.Lbuffer_take_T_copy:
|
|
558
|
+
mov x4, x2
|
|
559
|
+
mov x1, x2
|
|
560
|
+
mov x2, x3
|
|
561
|
+
stp x4, x3, [sp, #-16]!
|
|
562
|
+
bl _memcpy
|
|
563
|
+
ldp x4, x3, [sp], #16
|
|
564
|
+
mov x0, x4
|
|
565
|
+
mov x1, #0
|
|
566
|
+
mov x2, x3
|
|
567
|
+
bl _memset
|
|
568
|
+
.Lbuffer_take_T_end:
|
|
569
|
+
```
|
|
570
|
+
}
|
|
571
|
+
|
|
347
572
|
// Move a value out of a T_SIZE-sized slot: return it and zero the slot so the
|
|
348
573
|
// buffer no longer holds a (possibly mv) copy. Declared `mv out T` so
|
|
349
574
|
// the result is treated as OWNED by the borrow checker — the slot is
|
|
350
575
|
// relinquished, so the caller is the sole remaining owner. The value
|
|
351
576
|
// counterpart of ClassBuffer.move_int — used by List.pop for any value T.
|
|
352
|
-
inline func move = (ref self, int i: i >= 0 && i < self.cap, mv out T) {
|
|
577
|
+
inline func move = (ref self {}, int i: i >= 0 && i < self.cap, mv out T) {
|
|
353
578
|
```
|
|
354
579
|
#arch: c
|
|
355
580
|
T* _slots = (T*)(unsigned long long)self->data;
|
|
@@ -364,8 +589,10 @@ pub struct Buffer<T> {
|
|
|
364
589
|
mov x3, #T_SIZE
|
|
365
590
|
madd x5, x1, x3, xzr
|
|
366
591
|
add x5, x4, x5
|
|
367
|
-
cmp x3, #
|
|
592
|
+
cmp x3, #16
|
|
368
593
|
b.gt .Lbuffer_move_T_copy
|
|
594
|
+
cmp x3, #8
|
|
595
|
+
b.gt .Lbuffer_move_T_pair
|
|
369
596
|
// Width-matched load + zero-clear: an 8-byte access on a sub-8
|
|
370
597
|
// element would zero the following slots and run past the slab.
|
|
371
598
|
cmp x3, #1
|
|
@@ -389,6 +616,14 @@ pub struct Buffer<T> {
|
|
|
389
616
|
ldr w0, [x5]
|
|
390
617
|
str wzr, [x5]
|
|
391
618
|
b .Lbuffer_move_T_end
|
|
619
|
+
.Lbuffer_move_T_pair:
|
|
620
|
+
// Word-pair move (T_SIZE <= 16): the sret pair takes both
|
|
621
|
+
// words and the slot zeroes, no calls.
|
|
622
|
+
ldp x9, x10, [x5]
|
|
623
|
+
stp x9, x10, [x8]
|
|
624
|
+
stp xzr, xzr, [x5]
|
|
625
|
+
mov x0, x8
|
|
626
|
+
b .Lbuffer_move_T_end
|
|
392
627
|
.Lbuffer_move_T_copy:
|
|
393
628
|
mov x0, x8
|
|
394
629
|
mov x1, x5
|
|
@@ -419,7 +654,7 @@ pub struct Buffer<T> {
|
|
|
419
654
|
// this raw form is correct for trivially-copyable elements only. NOT
|
|
420
655
|
// `inline`: the body calls through the fn pointer, so splicing the raw
|
|
421
656
|
// block into a caller would bypass the per-element specializations.
|
|
422
|
-
func modify = (ref self, int i: i >= 0 && i < self.cap, func(T, out T) f) {
|
|
657
|
+
func modify = (ref self {}, int i: i >= 0 && i < self.cap, func(T, out T) f) {
|
|
423
658
|
```
|
|
424
659
|
#arch: c
|
|
425
660
|
((T*)(unsigned long long)self->data)[i] = ((T (*)(void *, T))f->code)(f->env, ((T*)(unsigned long long)self->data)[i]);
|
|
@@ -506,7 +741,7 @@ pub struct Buffer<T> {
|
|
|
506
741
|
// value, then deep-copies (strdup per string field). See
|
|
507
742
|
// owning_buffer_specialize.ts (both backends). ClassBuffer.replace
|
|
508
743
|
// destroys+frees class slots soundly.
|
|
509
|
-
inline func replace = (ref self, int i: i >= 0 && i < self.cap, T val) {
|
|
744
|
+
inline func replace = (ref self {}, int i: i >= 0 && i < self.cap, T val) {
|
|
510
745
|
```
|
|
511
746
|
#arch: c
|
|
512
747
|
((T*)(unsigned long long)self->data)[i] = val;
|
|
@@ -518,8 +753,10 @@ pub struct Buffer<T> {
|
|
|
518
753
|
mov x3, #T_SIZE
|
|
519
754
|
madd x1, x1, x3, xzr
|
|
520
755
|
add x0, x0, x1
|
|
521
|
-
cmp x3, #
|
|
756
|
+
cmp x3, #16
|
|
522
757
|
b.gt .Lbuffer_replace_T_copy
|
|
758
|
+
cmp x3, #8
|
|
759
|
+
b.gt .Lbuffer_replace_T_pair
|
|
523
760
|
// Width-matched store: an 8-byte str on a sub-8 element would
|
|
524
761
|
// clobber the following slots and run past the slab's end.
|
|
525
762
|
cmp x3, #1
|
|
@@ -539,6 +776,12 @@ pub struct Buffer<T> {
|
|
|
539
776
|
.Lbuffer_replace_T_4:
|
|
540
777
|
str w2, [x0]
|
|
541
778
|
b .Lbuffer_replace_T_end
|
|
779
|
+
.Lbuffer_replace_T_pair:
|
|
780
|
+
// Word-pair store (T_SIZE <= 16): the value's address (x2)
|
|
781
|
+
// feeds both words, no call.
|
|
782
|
+
ldp x9, x10, [x2]
|
|
783
|
+
stp x9, x10, [x0]
|
|
784
|
+
b .Lbuffer_replace_T_end
|
|
542
785
|
.Lbuffer_replace_T_copy:
|
|
543
786
|
mov x1, x2
|
|
544
787
|
mov x2, x3
|
|
@@ -554,10 +797,56 @@ pub struct Buffer<T> {
|
|
|
554
797
|
// cannot accept the `mv` keyword, so a borrow being transferred
|
|
555
798
|
// (e.g. a loaded element) must go through here rather than be bound to
|
|
556
799
|
// a freed local.
|
|
557
|
-
pub func set = (ref self, int i: i >= 0 && i < self.cap, mv T value) {
|
|
800
|
+
pub func set = (ref self {}, int i: i >= 0 && i < self.cap, mv T value) {
|
|
558
801
|
self.replace(i, value)
|
|
559
802
|
}
|
|
560
803
|
|
|
804
|
+
// Reclaim slot i's owned heap state WITHOUT writing the slot (the raw
|
|
805
|
+
// no-op below is correct for trivially-destructible elements; for
|
|
806
|
+
// `string` and mv value-struct elements both backends specialize this
|
|
807
|
+
// body — free the displaced pointer / guarded T_destroy — the same
|
|
808
|
+
// dispatch replace_T gets). This is the DESTROY half of replace,
|
|
809
|
+
// separated from the copy so the in-place store lowering can reclaim
|
|
810
|
+
// the displaced value and then CONSTRUCT the new one straight into the
|
|
811
|
+
// slot (no donor, no deep copy). Contract: the slot must be
|
|
812
|
+
// overwritten before it is read or destroyed again — Map.set_slot_ptr
|
|
813
|
+
// (the probe-then-construct primitive) and the in-place `set` splice
|
|
814
|
+
// are the callers, and both overwrite immediately.
|
|
815
|
+
internal func destroy_at = (ref self {}, int i: i >= 0 && i < self.cap) {
|
|
816
|
+
```
|
|
817
|
+
#arch: c
|
|
818
|
+
T* _slots = (T*)(unsigned long long)self->data;
|
|
819
|
+
#if T_NEEDS_STRDUP
|
|
820
|
+
// A string slot owns its strdup'd bytes — free the buffer before the
|
|
821
|
+
// zero. Owning value-struct elements never reach this body: their
|
|
822
|
+
// destroy_at is replaced at the call site by the owning-buffer
|
|
823
|
+
// specialization (payload reclaim + zero).
|
|
824
|
+
free(_slots[i].ptr);
|
|
825
|
+
#endif
|
|
826
|
+
memset((void*)&_slots[i], 0, T_SIZE);
|
|
827
|
+
```
|
|
828
|
+
```
|
|
829
|
+
#arch: aarch64
|
|
830
|
+
// Same contract as the C body: string slots free their strdup'd
|
|
831
|
+
// buffer first (the T_NEEDS_STRDUP dispatch folds away for every
|
|
832
|
+
// other element kind); owning value-struct elements take the
|
|
833
|
+
// call-site specialization instead.
|
|
834
|
+
ldr x4, [x19, #8]
|
|
835
|
+
mov x3, #T_SIZE
|
|
836
|
+
madd x5, x1, x3, xzr
|
|
837
|
+
add x5, x4, x5
|
|
838
|
+
mov x9, #T_NEEDS_STRDUP
|
|
839
|
+
cbz x9, .Lbuffer_destroy_at_T_zero
|
|
840
|
+
ldr x0, [x5]
|
|
841
|
+
bl _free
|
|
842
|
+
.Lbuffer_destroy_at_T_zero:
|
|
843
|
+
mov x0, x5
|
|
844
|
+
mov x1, #0
|
|
845
|
+
mov x2, #T_SIZE
|
|
846
|
+
bl _memset
|
|
847
|
+
```
|
|
848
|
+
}
|
|
849
|
+
|
|
561
850
|
// Address of slot `i` (library-only): the ELEMENT BORROW primitive —
|
|
562
851
|
// `ref T x = list.at(i)` (docs/HANDLERS.md Stage 4, the dynamic-tree
|
|
563
852
|
// dispatch) lowers to this, binding the alias to the slot itself rather
|
|
@@ -567,7 +856,7 @@ pub struct Buffer<T> {
|
|
|
567
856
|
// borrow-invalidation rules. Library-internal: the caller (the `List`
|
|
568
857
|
// wrapper, or trusted internals) bounds-checks; the body is raw-only so
|
|
569
858
|
// the missing-return net does not apply to the raw return.
|
|
570
|
-
|
|
859
|
+
internal func slot_ptr = (ref self {}, int i, out ptr T) {
|
|
571
860
|
```
|
|
572
861
|
#arch: c
|
|
573
862
|
return (T*)(unsigned long long)self->data + i;
|
|
@@ -593,7 +882,7 @@ pub struct Buffer<T> {
|
|
|
593
882
|
// owning_buffer_specialize.ts); this raw form is correct for
|
|
594
883
|
// trivially-destructible elements only.
|
|
595
884
|
inline func shift = (
|
|
596
|
-
ref self,
|
|
885
|
+
ref self {},
|
|
597
886
|
int dst: dst >= 0 && dst < self.cap,
|
|
598
887
|
int src: src >= 0 && src < self.cap
|
|
599
888
|
) {
|
|
@@ -614,8 +903,10 @@ pub struct Buffer<T> {
|
|
|
614
903
|
madd x6, x2, x3, x4
|
|
615
904
|
cmp x5, x6
|
|
616
905
|
b.eq .Lbuffer_shift_T_end
|
|
617
|
-
cmp x3, #
|
|
906
|
+
cmp x3, #16
|
|
618
907
|
b.gt .Lbuffer_shift_T_copy
|
|
908
|
+
cmp x3, #8
|
|
909
|
+
b.gt .Lbuffer_shift_T_pair
|
|
619
910
|
// Width-matched mv: an 8-byte access on a sub-8 element would
|
|
620
911
|
// clobber the following slot (and run past the slab's end).
|
|
621
912
|
cmp x3, #1
|
|
@@ -643,6 +934,13 @@ pub struct Buffer<T> {
|
|
|
643
934
|
str w7, [x5]
|
|
644
935
|
str wzr, [x6]
|
|
645
936
|
b .Lbuffer_shift_T_end
|
|
937
|
+
.Lbuffer_shift_T_pair:
|
|
938
|
+
// Word-pair shift (T_SIZE <= 16): both words move wholesale
|
|
939
|
+
// and the source pair zeroes, no calls.
|
|
940
|
+
ldp x7, x9, [x6]
|
|
941
|
+
stp x7, x9, [x5]
|
|
942
|
+
stp xzr, xzr, [x6]
|
|
943
|
+
b .Lbuffer_shift_T_end
|
|
646
944
|
.Lbuffer_shift_T_copy:
|
|
647
945
|
mov x0, x5
|
|
648
946
|
mov x1, x6
|
|
@@ -658,6 +956,67 @@ pub struct Buffer<T> {
|
|
|
658
956
|
```
|
|
659
957
|
}
|
|
660
958
|
|
|
959
|
+
// Bulk transfer for backward-shift deletion: move the `count` slots at
|
|
960
|
+
// `src` down to `dst` (src >= dst) as ONE block move, then zero the
|
|
961
|
+
// source slots the destination never covers — for the contiguous
|
|
962
|
+
// deletion shape (src = dst + 1) that is exactly one tail slot, so a
|
|
963
|
+
// whole range shift costs one memmove + one memset instead of
|
|
964
|
+
// count × (memcpy + memset) per-element `shift` calls. PROOF
|
|
965
|
+
// OBLIGATION on the caller (INTERNAL, byte-level like `shift`): every
|
|
966
|
+
// slot in [dst, dst + count) must be DEAD — zeroed by a move-out or
|
|
967
|
+
// otherwise unowned — because the block move overwrites them without
|
|
968
|
+
// reclaiming (the ownership proof is why the deletion algorithms zero
|
|
969
|
+
// and relocate in lockstep; a live displaced value belongs in `shift`,
|
|
970
|
+
// which reclaims dst first). The bytes transfer wholesale, so the move
|
|
971
|
+
// carries mv elements' heap pointers with them — correct for every
|
|
972
|
+
// element kind, raw for both backends (never names T).
|
|
973
|
+
internal inline func move_range = (
|
|
974
|
+
ref self {},
|
|
975
|
+
int dst: dst >= 0,
|
|
976
|
+
int src: src >= dst,
|
|
977
|
+
int count: count >= 0 && src + count <= self.cap
|
|
978
|
+
) {
|
|
979
|
+
```
|
|
980
|
+
#arch: c
|
|
981
|
+
if (count > 0) {
|
|
982
|
+
T* _slots = (T*)(unsigned long long)self->data;
|
|
983
|
+
memmove((void*)&_slots[dst], (void*)&_slots[src],
|
|
984
|
+
(unsigned long long)count * T_SIZE);
|
|
985
|
+
// Zero the source slots the destination does not overwrite:
|
|
986
|
+
// [dst + count, src + count) — one slot for the contiguous
|
|
987
|
+
// deletion shape, the gap+1 slots for a strided one.
|
|
988
|
+
memset((void*)&_slots[dst + count], 0,
|
|
989
|
+
(unsigned long long)(src - dst) * T_SIZE);
|
|
990
|
+
}
|
|
991
|
+
```
|
|
992
|
+
```
|
|
993
|
+
#arch: aarch64
|
|
994
|
+
// x19 = self, x1 = dst, x2 = src, x3 = count. x0-x18 die across the
|
|
995
|
+
// bl calls, so the block math parks on the stack around _memmove.
|
|
996
|
+
cbz x3, .Lbuffer_move_range_T_end
|
|
997
|
+
ldr x4, [x19, #8]
|
|
998
|
+
mov x5, #T_SIZE
|
|
999
|
+
madd x6, x1, x5, x4
|
|
1000
|
+
madd x7, x2, x5, x4
|
|
1001
|
+
madd x9, x3, x5, xzr
|
|
1002
|
+
sub x10, x2, x1
|
|
1003
|
+
madd x10, x10, x5, xzr
|
|
1004
|
+
stp x6, x9, [sp, #-16]!
|
|
1005
|
+
str x10, [sp, #-16]!
|
|
1006
|
+
mov x0, x6
|
|
1007
|
+
mov x1, x7
|
|
1008
|
+
mov x2, x9
|
|
1009
|
+
bl _memmove
|
|
1010
|
+
ldr x10, [sp], #16
|
|
1011
|
+
ldp x6, x9, [sp], #16
|
|
1012
|
+
add x0, x6, x9
|
|
1013
|
+
mov x1, #0
|
|
1014
|
+
mov x2, x10
|
|
1015
|
+
bl _memset
|
|
1016
|
+
.Lbuffer_move_range_T_end:
|
|
1017
|
+
```
|
|
1018
|
+
}
|
|
1019
|
+
|
|
661
1020
|
// A non-mv (ptr, len) slice [start, end) over this buffer's element
|
|
662
1021
|
// storage, returned as a `view T`. It borrows from self: it may not outlive
|
|
663
1022
|
// self's scope and is invalidated if self is reassigned. This is the single
|
|
@@ -688,7 +1047,7 @@ pub struct Buffer<T> {
|
|
|
688
1047
|
}
|
|
689
1048
|
|
|
690
1049
|
// Internal: zeroes slots without reclaiming mv elements.
|
|
691
|
-
internal inline func zero = (ref self, int len) {
|
|
1050
|
+
internal inline func zero = (ref self {}, int len) {
|
|
692
1051
|
memset(self.data, 0, len * T_SIZE)
|
|
693
1052
|
}
|
|
694
1053
|
|
|
@@ -719,7 +1078,7 @@ pub struct Buffer<T> {
|
|
|
719
1078
|
return dst
|
|
720
1079
|
}
|
|
721
1080
|
|
|
722
|
-
internal inline func store_or_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
1081
|
+
internal inline func store_or_int = (ref self {}, int i: i >= 0 && i < self.cap, int val) {
|
|
723
1082
|
```
|
|
724
1083
|
#arch: c
|
|
725
1084
|
((long*)(unsigned long long)self->data)[i] |= val;
|
|
@@ -735,7 +1094,7 @@ pub struct Buffer<T> {
|
|
|
735
1094
|
```
|
|
736
1095
|
}
|
|
737
1096
|
|
|
738
|
-
internal func alloc_float = (ref self, int size, out int: out >= size) {
|
|
1097
|
+
internal func alloc_float = (ref self {}, int size, out int: out >= size) {
|
|
739
1098
|
self.cap = size
|
|
740
1099
|
self.data = calloc(size, 8)
|
|
741
1100
|
return self.cap
|
|
@@ -755,7 +1114,7 @@ pub struct Buffer<T> {
|
|
|
755
1114
|
```
|
|
756
1115
|
}
|
|
757
1116
|
|
|
758
|
-
internal inline func store_float = (ref self, int i: i >= 0 && i < self.cap, float val) {
|
|
1117
|
+
internal inline func store_float = (ref self {}, int i: i >= 0 && i < self.cap, float val) {
|
|
759
1118
|
```
|
|
760
1119
|
#arch: c
|
|
761
1120
|
((double*)(unsigned long long)self->data)[i] = val;
|