nomen-lang 0.3.0 → 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 +7 -7
- package/core/System/BigInt.nm +5 -15
- package/core/System/Buffer.nm +133 -33
- package/core/System/ClassBuffer.nm +84 -32
- 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/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 +30 -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 +47 -18
- package/core/System/Text/JsonTree.nm +15 -15
- package/core/System/bool.nm +2 -1
- package/core/System/char.nm +2 -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 +2002 -1140
- package/package.json +1 -1
package/NOMEN_AGENTS.md
CHANGED
|
@@ -128,7 +128,13 @@ const y = x + 1 // Error: 'x' is null
|
|
|
128
128
|
|
|
129
129
|
### Visibility
|
|
130
130
|
|
|
131
|
-
`pub` exports
|
|
131
|
+
`pub` exports. `internal` is visible only within the declaring library/module
|
|
132
|
+
(the System library uses it to hide implementation details). Everything else is
|
|
133
|
+
private to its declaring scope.
|
|
134
|
+
|
|
135
|
+
`readonly` is a field-write modifier, not a visibility: the field reads at its
|
|
136
|
+
declared visibility but can only be assigned inside its declaring struct/class
|
|
137
|
+
(and its `extend`s). `const` fields are immutable everywhere.
|
|
132
138
|
|
|
133
139
|
### Imports
|
|
134
140
|
|
package/core/System/Arena.nm
CHANGED
|
@@ -76,8 +76,8 @@ pub struct Arena<T> {
|
|
|
76
76
|
self.generations.grow_int(index + 1)
|
|
77
77
|
self.generations.store_int(index, 0)
|
|
78
78
|
}
|
|
79
|
-
self.values.
|
|
80
|
-
self.values.
|
|
79
|
+
self.values.grow(index + 1)
|
|
80
|
+
self.values.store(index, value)
|
|
81
81
|
var ArenaRef<T> handle = ArenaRef<T>()
|
|
82
82
|
handle.index = index
|
|
83
83
|
handle.generation = self.generations.load_int(index)
|
|
@@ -92,7 +92,7 @@ pub struct Arena<T> {
|
|
|
92
92
|
if !self.is_valid(handle) {
|
|
93
93
|
panic("arena: free of a stale handle")
|
|
94
94
|
}
|
|
95
|
-
var T discarded = self.values.
|
|
95
|
+
var T discarded = self.values.move(handle.index)
|
|
96
96
|
self.generations.store_int(handle.index, handle.generation + 1)
|
|
97
97
|
self.free_indices.grow_int(self.free_count + 1)
|
|
98
98
|
self.free_indices.store_int(self.free_count, handle.index)
|
|
@@ -124,7 +124,7 @@ pub struct Arena<T> {
|
|
|
124
124
|
if !self.is_valid(handle) {
|
|
125
125
|
panic("arena: get of a stale handle")
|
|
126
126
|
}
|
|
127
|
-
return self.values.
|
|
127
|
+
return self.values.load(handle.index)
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/**
|
|
@@ -134,7 +134,7 @@ pub struct Arena<T> {
|
|
|
134
134
|
if !self.is_valid(handle) {
|
|
135
135
|
return fallback
|
|
136
136
|
}
|
|
137
|
-
return self.values.
|
|
137
|
+
return self.values.load(handle.index)
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/**
|
|
@@ -144,7 +144,7 @@ pub struct Arena<T> {
|
|
|
144
144
|
if !self.is_valid(handle) {
|
|
145
145
|
panic("arena: set through a stale handle")
|
|
146
146
|
}
|
|
147
|
-
self.values.
|
|
147
|
+
self.values.replace(handle.index, value)
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/**
|
|
@@ -161,6 +161,6 @@ pub struct Arena<T> {
|
|
|
161
161
|
* generation-check; callers own the index bookkeeping.
|
|
162
162
|
**/
|
|
163
163
|
pub func get_index = (self, int index: index >= 0 && index < self.count, out T) {
|
|
164
|
-
return self.values.
|
|
164
|
+
return self.values.load(index)
|
|
165
165
|
}
|
|
166
166
|
}
|
package/core/System/BigInt.nm
CHANGED
|
@@ -6,7 +6,10 @@ pub struct BigInt {
|
|
|
6
6
|
var len = 0
|
|
7
7
|
var digits = Buffer<int>()
|
|
8
8
|
|
|
9
|
-
pub func
|
|
9
|
+
pub func #init = (ref self) {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
pub func #init = (ref self, int val) {
|
|
10
13
|
self.sign = 1
|
|
11
14
|
var int v = val
|
|
12
15
|
if v < 0 {
|
|
@@ -17,7 +20,6 @@ pub struct BigInt {
|
|
|
17
20
|
self.digits.zero_int(self.digits.cap)
|
|
18
21
|
self.digits.store_int(0, v)
|
|
19
22
|
self.len = 1
|
|
20
|
-
return self
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
// Per-limb get/set stay raw: they are `inline` and splice into the limb
|
|
@@ -528,20 +530,8 @@ pub struct BigInt {
|
|
|
528
530
|
}
|
|
529
531
|
self.len = self.len - 1
|
|
530
532
|
}
|
|
531
|
-
|
|
532
|
-
a0.digits.destroy()
|
|
533
|
-
a1.digits.destroy()
|
|
534
|
-
b0.digits.destroy()
|
|
535
|
-
b1.digits.destroy()
|
|
536
|
-
z0.digits.destroy()
|
|
537
|
-
z2.digits.destroy()
|
|
538
|
-
aa.digits.destroy()
|
|
539
|
-
bb.digits.destroy()
|
|
540
|
-
z1.digits.destroy()
|
|
541
|
-
s0.digits.destroy()
|
|
542
|
-
s1.digits.destroy()
|
|
543
|
-
s2.digits.destroy()
|
|
544
533
|
}
|
|
534
|
+
|
|
545
535
|
// self = quotient, a = dividend, b = divisor, remainder = remainder
|
|
546
536
|
// Uses remainder's internal buffer as working space
|
|
547
537
|
pub func div_to = (ref self, BigInt a, BigInt b, ref BigInt remainder) {
|
package/core/System/Buffer.nm
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
// Buffer<T>: a flat buffer of value slots of type T. The value counterpart to
|
|
2
2
|
// ClassBuffer<T>: no per-element freeing, store_int unconstrained. When T is a
|
|
3
3
|
// class, the monomorphizer rewrites a Buffer<T> field to ClassBuffer<T>.
|
|
4
|
+
//
|
|
5
|
+
// Public API is the size-aware family (`alloc`/`grow`/`load`/`replace`/
|
|
6
|
+
// `move`/`modify`/`shift`/`slice` plus the readonly `cap`). `replace` is the
|
|
7
|
+
// public write — it reclaims the displaced element, so it is correct on both
|
|
8
|
+
// fresh and occupied slots. `store` (which assumes a fresh slot and leaks on
|
|
9
|
+
// overwrite), `zero`, the fixed-width `*_u32`/`*_int`/`*_float` primitives,
|
|
10
|
+
// and the raw `data` pointer are `internal` and reserved for the System
|
|
11
|
+
// containers.
|
|
4
12
|
|
|
5
13
|
/**
|
|
6
14
|
* A flat, low-level buffer of value slots of type T — the backing store for arrays and collections
|
|
7
15
|
**/
|
|
8
16
|
pub struct Buffer<T> {
|
|
9
|
-
var uint64 data = 0
|
|
10
|
-
|
|
17
|
+
internal var uint64 data = 0
|
|
18
|
+
// Publicly readable capacity (`i < self.cap` guards every accessor), but
|
|
19
|
+
// only Buffer's own methods may grow it — `readonly`.
|
|
20
|
+
readonly cap = 0
|
|
11
21
|
|
|
12
22
|
// Allocation / sizing primitives are `unsafe` Nomen single-sourced for
|
|
13
23
|
// both backends. They run once per container op (never per element), so
|
|
14
24
|
// the aarch64 inline-method ABI cost that keeps the per-limb load/store
|
|
15
25
|
// primitives raw (see BigInt.nm) doesn't apply here.
|
|
16
|
-
func
|
|
26
|
+
internal func alloc_u32 = (ref self, int size, out int: out >= size) {
|
|
17
27
|
self.cap = size
|
|
18
28
|
self.data = calloc(size, 4)
|
|
19
29
|
return self.cap
|
|
20
30
|
}
|
|
21
31
|
|
|
22
|
-
func
|
|
32
|
+
internal func grow_u32 = (ref self, int needed, out int: out >= needed) {
|
|
23
33
|
if self.cap >= needed {
|
|
24
34
|
return self.cap
|
|
25
35
|
}
|
|
@@ -34,11 +44,11 @@ pub struct Buffer<T> {
|
|
|
34
44
|
return self.cap
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
func
|
|
47
|
+
internal func zero_u32 = (ref self, int len) {
|
|
38
48
|
memset(self.data, 0, len * 4)
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
inline func
|
|
51
|
+
internal inline func load_u32 = (self, int i: i >= 0 && i < self.cap, out uint32) {
|
|
42
52
|
```
|
|
43
53
|
#arch: c
|
|
44
54
|
return ((unsigned int*)(unsigned long long)self->data)[i];
|
|
@@ -53,7 +63,7 @@ pub struct Buffer<T> {
|
|
|
53
63
|
```
|
|
54
64
|
}
|
|
55
65
|
|
|
56
|
-
inline func
|
|
66
|
+
internal inline func store_u32 = (ref self, int i: i >= 0 && i < self.cap, uint32 val) {
|
|
57
67
|
```
|
|
58
68
|
#arch: c
|
|
59
69
|
((unsigned int*)(unsigned long long)self->data)[i] = val;
|
|
@@ -67,7 +77,7 @@ pub struct Buffer<T> {
|
|
|
67
77
|
```
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
inline func
|
|
80
|
+
internal inline func store_or_u32 = (ref self, int i: i >= 0 && i < self.cap, uint32 val) {
|
|
71
81
|
```
|
|
72
82
|
#arch: c
|
|
73
83
|
((unsigned int*)(unsigned long long)self->data)[i] |= val;
|
|
@@ -83,13 +93,13 @@ pub struct Buffer<T> {
|
|
|
83
93
|
```
|
|
84
94
|
}
|
|
85
95
|
|
|
86
|
-
func alloc_int = (ref self, int size, out int: out >= size) {
|
|
96
|
+
internal func alloc_int = (ref self, int size, out int: out >= size) {
|
|
87
97
|
self.cap = size
|
|
88
98
|
self.data = calloc(size, 8)
|
|
89
99
|
return self.cap
|
|
90
100
|
}
|
|
91
101
|
|
|
92
|
-
func grow_int = (ref self, int needed, out int: out >= needed) {
|
|
102
|
+
internal func grow_int = (ref self, int needed, out int: out >= needed) {
|
|
93
103
|
if self.cap >= needed {
|
|
94
104
|
return self.cap
|
|
95
105
|
}
|
|
@@ -104,7 +114,7 @@ pub struct Buffer<T> {
|
|
|
104
114
|
return self.cap
|
|
105
115
|
}
|
|
106
116
|
|
|
107
|
-
inline func load_int = (self, int i: i >= 0 && i < self.cap, out int) {
|
|
117
|
+
internal inline func load_int = (self, int i: i >= 0 && i < self.cap, out int) {
|
|
108
118
|
```
|
|
109
119
|
#arch: c
|
|
110
120
|
return ((long*)(unsigned long long)self->data)[i];
|
|
@@ -118,7 +128,7 @@ pub struct Buffer<T> {
|
|
|
118
128
|
```
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
inline func store_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
131
|
+
internal inline func store_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
122
132
|
```
|
|
123
133
|
#arch: c
|
|
124
134
|
((long*)(unsigned long long)self->data)[i] = val;
|
|
@@ -136,7 +146,7 @@ pub struct Buffer<T> {
|
|
|
136
146
|
// the ownership-extracting primitive (used by e.g. List.pop) — unlike
|
|
137
147
|
// load_int (a borrow read), it relinquishes the slot so the buffer no
|
|
138
148
|
// longer owns the value.
|
|
139
|
-
inline func move_int = (ref self, int i: i >= 0 && i < self.cap, out int) {
|
|
149
|
+
internal inline func move_int = (ref self, int i: i >= 0 && i < self.cap, out int) {
|
|
140
150
|
```
|
|
141
151
|
#arch: c
|
|
142
152
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -157,7 +167,7 @@ pub struct Buffer<T> {
|
|
|
157
167
|
// Store into a slot, freeing the previous value when the buffer holds
|
|
158
168
|
// owned class pointers (has_class_refs). For non-class buffers this is a
|
|
159
169
|
// plain store, so it is safe to use anywhere store_int would be.
|
|
160
|
-
inline func replace_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
170
|
+
internal inline func replace_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
161
171
|
```
|
|
162
172
|
#arch: c
|
|
163
173
|
((long*)(unsigned long long)self->data)[i] = val;
|
|
@@ -171,7 +181,7 @@ pub struct Buffer<T> {
|
|
|
171
181
|
```
|
|
172
182
|
}
|
|
173
183
|
|
|
174
|
-
inline func zero_int = (ref self, int len) {
|
|
184
|
+
internal inline func zero_int = (ref self, int len) {
|
|
175
185
|
memset(self.data, 0, len * 8)
|
|
176
186
|
}
|
|
177
187
|
|
|
@@ -180,13 +190,13 @@ pub struct Buffer<T> {
|
|
|
180
190
|
// correctly. Classes route to ClassBuffer<T> (per-element destroy), so T
|
|
181
191
|
// reaching here is always a value type (primitive or struct).
|
|
182
192
|
|
|
183
|
-
func
|
|
193
|
+
func alloc = (ref self, int size, out int: out >= size) {
|
|
184
194
|
self.cap = size
|
|
185
195
|
self.data = calloc(size, T_SIZE)
|
|
186
196
|
return self.cap
|
|
187
197
|
}
|
|
188
198
|
|
|
189
|
-
func
|
|
199
|
+
func grow = (ref self, int needed, out int: out >= needed) {
|
|
190
200
|
if self.cap >= needed {
|
|
191
201
|
return self.cap
|
|
192
202
|
}
|
|
@@ -205,7 +215,7 @@ pub struct Buffer<T> {
|
|
|
205
215
|
// call sites — the load/store pair behind every container `.at`/`set` is
|
|
206
216
|
// the hottest path in data-heavy code, and the naked-inline path keeps it
|
|
207
217
|
// to a handful of instructions with no call overhead.
|
|
208
|
-
inline func
|
|
218
|
+
inline func load = (self, int i: i >= 0 && i < self.cap, out T) {
|
|
209
219
|
```
|
|
210
220
|
#arch: c
|
|
211
221
|
return ((T*)(unsigned long long)self->data)[i];
|
|
@@ -249,12 +259,14 @@ pub struct Buffer<T> {
|
|
|
249
259
|
}
|
|
250
260
|
|
|
251
261
|
// Store `val` into slot `i`, which MUST be fresh (calloc'd, or zeroed by
|
|
252
|
-
//
|
|
262
|
+
// move). For an owning T (string, or a struct with string fields) the
|
|
253
263
|
// specialised body deep-copies the incoming value but does NOT free the
|
|
254
|
-
// slot's previous occupant — a second `
|
|
255
|
-
// leaks it. To overwrite a live slot use
|
|
264
|
+
// slot's previous occupant — a second `store` at an occupied index
|
|
265
|
+
// leaks it. To overwrite a live slot use replace (frees the displaced
|
|
256
266
|
// value) — this is the primitive every container's `set` uses.
|
|
257
|
-
|
|
267
|
+
// Internal: assumes a FRESH slot (see the contract comment). The public
|
|
268
|
+
// write is `replace`, which is correct on fresh and occupied slots alike.
|
|
269
|
+
internal inline func store = (ref self, int i: i >= 0 && i < self.cap, T val) {
|
|
258
270
|
```
|
|
259
271
|
#arch: c
|
|
260
272
|
((T*)(unsigned long long)self->data)[i] = val;
|
|
@@ -300,7 +312,7 @@ pub struct Buffer<T> {
|
|
|
300
312
|
// the result is treated as OWNED by the borrow checker — the slot is
|
|
301
313
|
// relinquished, so the caller is the sole remaining owner. The value
|
|
302
314
|
// counterpart of ClassBuffer.move_int — used by List.pop for any value T.
|
|
303
|
-
inline func
|
|
315
|
+
inline func move = (ref self, int i: i >= 0 && i < self.cap, move out T) {
|
|
304
316
|
```
|
|
305
317
|
#arch: c
|
|
306
318
|
T* _slots = (T*)(unsigned long long)self->data;
|
|
@@ -356,14 +368,101 @@ pub struct Buffer<T> {
|
|
|
356
368
|
```
|
|
357
369
|
}
|
|
358
370
|
|
|
371
|
+
// Apply `f` to slot `i` in place: the safe way to do a load→modify→store
|
|
372
|
+
// round-trip on a live slot. `f` receives the current value as a borrow
|
|
373
|
+
// (by value) and returns the replacement; the primitive itself handles
|
|
374
|
+
// ownership — the displaced value is freed, and a returned field that
|
|
375
|
+
// aliases the slot's own copy (the round-trip case) is kept, not freed or
|
|
376
|
+
// re-copied. Unlike store this never leaks on overwrite, and unlike
|
|
377
|
+
// replace it is safe to derive the new value from the old one. The
|
|
378
|
+
// returned struct's owning fields must be fresh, null, or identical to
|
|
379
|
+
// the corresponding input fields (no cross-field aliasing). For owning
|
|
380
|
+
// elements (string / structs with string fields) the build phase replaces
|
|
381
|
+
// this body with a specialized one (see owning_buffer_specialize.ts);
|
|
382
|
+
// this raw form is correct for trivially-copyable elements only. NOT
|
|
383
|
+
// `inline`: the body calls through the fn pointer, so splicing the raw
|
|
384
|
+
// block into a caller would bypass the per-element specializations.
|
|
385
|
+
func modify = (ref self, int i: i >= 0 && i < self.cap, func (T, out T) f) {
|
|
386
|
+
```
|
|
387
|
+
#arch: c
|
|
388
|
+
((T*)(unsigned long long)self->data)[i] = f(((T*)(unsigned long long)self->data)[i]);
|
|
389
|
+
```
|
|
390
|
+
```
|
|
391
|
+
#arch: aarch64
|
|
392
|
+
// x19 = self, x1 = i, x2 = f. Scalar elements round-trip through x0
|
|
393
|
+
// (width-matched); larger trivially-copyable structs pass the slot
|
|
394
|
+
// address and an sret pointer (x8) — the fn writes the replacement
|
|
395
|
+
// straight into the slot from a stack-stage copy of the old value.
|
|
396
|
+
stp x20, x21, [sp, #-16]!
|
|
397
|
+
stp x22, x23, [sp, #-16]!
|
|
398
|
+
mov x22, x2
|
|
399
|
+
ldr x9, [x19, #8]
|
|
400
|
+
mov x3, #T_SIZE
|
|
401
|
+
madd x20, x1, x3, xzr
|
|
402
|
+
add x20, x20, x9
|
|
403
|
+
cmp x3, #8
|
|
404
|
+
b.gt .Lbuffer_modify_T_big
|
|
405
|
+
cmp x3, #1
|
|
406
|
+
b.eq .Lbuffer_modify_T_ld1
|
|
407
|
+
cmp x3, #2
|
|
408
|
+
b.eq .Lbuffer_modify_T_ld2
|
|
409
|
+
cmp x3, #4
|
|
410
|
+
b.eq .Lbuffer_modify_T_ld4
|
|
411
|
+
ldr x0, [x20]
|
|
412
|
+
b .Lbuffer_modify_T_call
|
|
413
|
+
.Lbuffer_modify_T_ld1:
|
|
414
|
+
ldrb w0, [x20]
|
|
415
|
+
b .Lbuffer_modify_T_call
|
|
416
|
+
.Lbuffer_modify_T_ld2:
|
|
417
|
+
ldrh w0, [x20]
|
|
418
|
+
b .Lbuffer_modify_T_call
|
|
419
|
+
.Lbuffer_modify_T_ld4:
|
|
420
|
+
ldr w0, [x20]
|
|
421
|
+
.Lbuffer_modify_T_call:
|
|
422
|
+
blr x22
|
|
423
|
+
cmp x3, #1
|
|
424
|
+
b.eq .Lbuffer_modify_T_st1
|
|
425
|
+
cmp x3, #2
|
|
426
|
+
b.eq .Lbuffer_modify_T_st2
|
|
427
|
+
cmp x3, #4
|
|
428
|
+
b.eq .Lbuffer_modify_T_st4
|
|
429
|
+
str x0, [x20]
|
|
430
|
+
b .Lbuffer_modify_T_end
|
|
431
|
+
.Lbuffer_modify_T_st1:
|
|
432
|
+
strb w0, [x20]
|
|
433
|
+
b .Lbuffer_modify_T_end
|
|
434
|
+
.Lbuffer_modify_T_st2:
|
|
435
|
+
strh w0, [x20]
|
|
436
|
+
b .Lbuffer_modify_T_end
|
|
437
|
+
.Lbuffer_modify_T_st4:
|
|
438
|
+
str w0, [x20]
|
|
439
|
+
b .Lbuffer_modify_T_end
|
|
440
|
+
.Lbuffer_modify_T_big:
|
|
441
|
+
add x23, x3, #15
|
|
442
|
+
bic x23, x23, #15
|
|
443
|
+
sub sp, sp, x23
|
|
444
|
+
mov x0, sp
|
|
445
|
+
mov x1, x20
|
|
446
|
+
mov x2, x3
|
|
447
|
+
bl _memcpy
|
|
448
|
+
mov x0, sp
|
|
449
|
+
mov x8, x20
|
|
450
|
+
blr x22
|
|
451
|
+
add sp, sp, x23
|
|
452
|
+
.Lbuffer_modify_T_end:
|
|
453
|
+
ldp x22, x23, [sp], #16
|
|
454
|
+
ldp x20, x21, [sp], #16
|
|
455
|
+
```
|
|
456
|
+
}
|
|
457
|
+
|
|
359
458
|
// Store into a T_SIZE-sized slot, overwriting the previous value. For
|
|
360
459
|
// trivially-destructible value structs this is a plain overwrite. For
|
|
361
460
|
// owning value structs (with string fields), the build phase replaces this
|
|
362
461
|
// body with a specialised version that calls T_destroy on the old slot
|
|
363
462
|
// value, then deep-copies (strdup per string field). See
|
|
364
|
-
// owning_buffer_specialize.ts (both backends). ClassBuffer.
|
|
463
|
+
// owning_buffer_specialize.ts (both backends). ClassBuffer.replace
|
|
365
464
|
// destroys+frees class slots soundly.
|
|
366
|
-
inline func
|
|
465
|
+
inline func replace = (ref self, int i: i >= 0 && i < self.cap, T val) {
|
|
367
466
|
```
|
|
368
467
|
#arch: c
|
|
369
468
|
((T*)(unsigned long long)self->data)[i] = val;
|
|
@@ -407,14 +506,14 @@ pub struct Buffer<T> {
|
|
|
407
506
|
// Move slot src into slot dst: overwrite dst with src's bytes and zero
|
|
408
507
|
// src, so exactly ONE slot owns the value afterwards. The
|
|
409
508
|
// ownership-transferring primitive behind backward-shift deletion
|
|
410
|
-
// (Map/Set.remove) — unlike
|
|
509
|
+
// (Map/Set.remove) — unlike store, which gives the slot its own deep
|
|
411
510
|
// copy and leaves the source slot intact, a shift transfers the source's
|
|
412
511
|
// heap pointers wholesale. For owning elements (string / value structs
|
|
413
512
|
// with string fields) the build replaces this body with a specialized
|
|
414
513
|
// version that also reclaims dst's previous value first (see
|
|
415
514
|
// owning_buffer_specialize.ts); this raw form is correct for
|
|
416
515
|
// trivially-destructible elements only.
|
|
417
|
-
inline func
|
|
516
|
+
inline func shift = (ref self, int dst: dst >= 0 && dst < self.cap, int src: src >= 0 && src < self.cap) {
|
|
418
517
|
```
|
|
419
518
|
#arch: c
|
|
420
519
|
T* _slots = (T*)(unsigned long long)self->data;
|
|
@@ -481,7 +580,7 @@ pub struct Buffer<T> {
|
|
|
481
580
|
// self's scope and is invalidated if self is reassigned. This is the single
|
|
482
581
|
// primitive every other slice (Array<T>, List<T>, user containers) builds
|
|
483
582
|
// on — higher layers just `return self.items.slice(start, end)`.
|
|
484
|
-
func slice = (self, int start: start >= 0, int end: end >= start, out view T) {
|
|
583
|
+
func slice = (self, int start: start >= 0, int end: end >= start && end <= self.cap, out view T) {
|
|
485
584
|
```
|
|
486
585
|
#arch: c
|
|
487
586
|
nomen_view _r;
|
|
@@ -500,11 +599,12 @@ pub struct Buffer<T> {
|
|
|
500
599
|
```
|
|
501
600
|
}
|
|
502
601
|
|
|
503
|
-
|
|
602
|
+
// Internal: zeroes slots without reclaiming owning elements.
|
|
603
|
+
internal inline func zero = (ref self, int len) {
|
|
504
604
|
memset(self.data, 0, len * T_SIZE)
|
|
505
605
|
}
|
|
506
606
|
|
|
507
|
-
inline func store_or_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
607
|
+
internal inline func store_or_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
508
608
|
```
|
|
509
609
|
#arch: c
|
|
510
610
|
((long*)(unsigned long long)self->data)[i] |= val;
|
|
@@ -520,13 +620,13 @@ pub struct Buffer<T> {
|
|
|
520
620
|
```
|
|
521
621
|
}
|
|
522
622
|
|
|
523
|
-
func alloc_float = (ref self, int size, out int: out >= size) {
|
|
623
|
+
internal func alloc_float = (ref self, int size, out int: out >= size) {
|
|
524
624
|
self.cap = size
|
|
525
625
|
self.data = calloc(size, 8)
|
|
526
626
|
return self.cap
|
|
527
627
|
}
|
|
528
628
|
|
|
529
|
-
inline func load_float = (self, int i: i >= 0 && i < self.cap, out float) {
|
|
629
|
+
internal inline func load_float = (self, int i: i >= 0 && i < self.cap, out float) {
|
|
530
630
|
```
|
|
531
631
|
#arch: c
|
|
532
632
|
return ((double*)(unsigned long long)self->data)[i];
|
|
@@ -540,7 +640,7 @@ pub struct Buffer<T> {
|
|
|
540
640
|
```
|
|
541
641
|
}
|
|
542
642
|
|
|
543
|
-
inline func store_float = (ref self, int i: i >= 0 && i < self.cap, float val) {
|
|
643
|
+
internal inline func store_float = (ref self, int i: i >= 0 && i < self.cap, float val) {
|
|
544
644
|
```
|
|
545
645
|
#arch: c
|
|
546
646
|
((double*)(unsigned long long)self->data)[i] = val;
|
|
@@ -8,27 +8,28 @@
|
|
|
8
8
|
* A flat buffer of owned class pointers of type T — the backing store for collections of classes
|
|
9
9
|
**/
|
|
10
10
|
pub struct ClassBuffer<T> {
|
|
11
|
-
var uint64 data = 0
|
|
12
|
-
|
|
11
|
+
internal var uint64 data = 0
|
|
12
|
+
// Publicly readable; grown only by ClassBuffer's own methods (`readonly`).
|
|
13
|
+
readonly cap = 0
|
|
13
14
|
|
|
14
|
-
func alloc_int = (ref self, int size, out int: out >= size) {
|
|
15
|
+
internal func alloc_int = (ref self, int size, out int: out >= size) {
|
|
15
16
|
self.cap = size
|
|
16
17
|
self.data = calloc(size, 8)
|
|
17
18
|
return self.cap
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
// Semantic parity with Buffer<T>.
|
|
21
|
-
// `self.values.
|
|
21
|
+
// Semantic parity with Buffer<T>.alloc: generic code (e.g. Map#init's
|
|
22
|
+
// `self.values.alloc(N)`) forwards to either Buffer — where alloc is
|
|
22
23
|
// T_SIZE-aware (string slots are 16 bytes) — or this ClassBuffer, whose
|
|
23
24
|
// slots are always one pointer wide. The compiler routes value-struct
|
|
24
25
|
// element types to Buffer and class/trait elements to ClassBuffer
|
|
25
26
|
// (monomorphize's field rewrite), so a call arriving HERE is always
|
|
26
27
|
// pointer-sized: delegate to alloc_int.
|
|
27
|
-
func
|
|
28
|
+
func alloc = (ref self, int size, out int: out >= size) {
|
|
28
29
|
return self.alloc_int(size)
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
func grow_int = (ref self, int needed, out int: out >= needed) {
|
|
32
|
+
internal func grow_int = (ref self, int needed, out int: out >= needed) {
|
|
32
33
|
if self.cap >= needed {
|
|
33
34
|
return self.cap
|
|
34
35
|
}
|
|
@@ -43,7 +44,7 @@ pub struct ClassBuffer<T> {
|
|
|
43
44
|
return self.cap
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
inline func load_int = (self, int i: i >= 0 && i < self.cap, out int) {
|
|
47
|
+
internal inline func load_int = (self, int i: i >= 0 && i < self.cap, out int) {
|
|
47
48
|
```
|
|
48
49
|
#arch: c
|
|
49
50
|
return ((long*)(unsigned long long)self->data)[i];
|
|
@@ -57,14 +58,14 @@ pub struct ClassBuffer<T> {
|
|
|
57
58
|
```
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
//
|
|
61
|
-
// 8-byte class pointers (T_SIZE is 8), so these are
|
|
62
|
-
// to their _int counterparts — they exist so
|
|
63
|
-
// uniform
|
|
64
|
-
// (ClassBuffer, after the Buffer<T> field is
|
|
65
|
-
// slots, matching _int.
|
|
61
|
+
// Generic (T-typed) counterparts of the _int primitives. ClassBuffer only
|
|
62
|
+
// ever holds 8-byte class pointers (T_SIZE is 8), so these are
|
|
63
|
+
// behaviorally identical to their _int counterparts — they exist so
|
|
64
|
+
// List<T> can call a single uniform API whether T is a value struct
|
|
65
|
+
// (Buffer) or a class (ClassBuffer, after the Buffer<T> field is
|
|
66
|
+
// rewritten). Bodies use 8-byte slots, matching _int.
|
|
66
67
|
|
|
67
|
-
func
|
|
68
|
+
func grow = (ref self, int needed, out int: out >= needed) {
|
|
68
69
|
if self.cap >= needed {
|
|
69
70
|
return self.cap
|
|
70
71
|
}
|
|
@@ -79,7 +80,7 @@ pub struct ClassBuffer<T> {
|
|
|
79
80
|
return self.cap
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
inline func
|
|
83
|
+
inline func load = (self, int i: i >= 0 && i < self.cap, out T) {
|
|
83
84
|
```
|
|
84
85
|
#arch: c
|
|
85
86
|
return (void*)((long*)(unsigned long long)self->data)[i];
|
|
@@ -94,11 +95,15 @@ pub struct ClassBuffer<T> {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
// Store the owned class pointer `val` into slot `i`, which MUST be fresh
|
|
97
|
-
// (calloc'd, or zeroed by
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
98
|
+
// (calloc'd, or zeroed by move/move_int). Takes OWNERSHIP of `val`
|
|
99
|
+
// (`move`): the slot's #destroy reclaims the instance, so the caller must
|
|
100
|
+
// not free its copy — a fresh constructor result transfers here directly,
|
|
101
|
+
// and a local must be moved (its own cleanup is suppressed at the call
|
|
102
|
+
// site). This is a plain pointer store — it does NOT destroy/free the
|
|
103
|
+
// slot's previous occupant, so a second `store` at an occupied index
|
|
104
|
+
// leaks that instance. Use replace (or replace_int) to overwrite a live
|
|
105
|
+
// slot.
|
|
106
|
+
internal inline func store = (ref self, int i: i >= 0 && i < self.cap, move T val: val != 0) {
|
|
102
107
|
```
|
|
103
108
|
#arch: c
|
|
104
109
|
((long*)(unsigned long long)self->data)[i] = (long)val;
|
|
@@ -116,11 +121,11 @@ pub struct ClassBuffer<T> {
|
|
|
116
121
|
// buffer no longer references it. Declared `move out T` so the borrow
|
|
117
122
|
// checker treats the result as OWNED — the slot is relinquished, so the
|
|
118
123
|
// caller is the sole remaining owner. This is the owning-extract primitive
|
|
119
|
-
// (parallel to List.pop); use it (not
|
|
124
|
+
// (parallel to List.pop); use it (not load) when transferring ownership
|
|
120
125
|
// out of a ClassBuffer slot, or moving a value into a fresh owning
|
|
121
|
-
// container —
|
|
126
|
+
// container — load returns a BORROW (the slot is unchanged) and storing
|
|
122
127
|
// it elsewhere would create shared ownership (double-free at destroy).
|
|
123
|
-
inline func
|
|
128
|
+
inline func move = (ref self, int i: i >= 0 && i < self.cap, move out T) {
|
|
124
129
|
```
|
|
125
130
|
#arch: c
|
|
126
131
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -138,7 +143,52 @@ pub struct ClassBuffer<T> {
|
|
|
138
143
|
```
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
|
|
146
|
+
// Apply `f` to slot `i` in place — the owning-counterpart of Buffer's
|
|
147
|
+
// modify for class elements. `f` receives the current instance pointer
|
|
148
|
+
// as a borrow and returns the replacement instance (fresh, or the same
|
|
149
|
+
// pointer); the primitive frees the displaced instance (destroy + free)
|
|
150
|
+
// unless `f` returned it. Storing the returned instance raw is sound:
|
|
151
|
+
// the slot takes sole ownership and `f`'s return is not retained
|
|
152
|
+
// elsewhere. NOT `inline`: the body calls through the fn pointer.
|
|
153
|
+
func modify = (ref self, int i: i >= 0 && i < self.cap, func (T, out T) f) {
|
|
154
|
+
```
|
|
155
|
+
#arch: c
|
|
156
|
+
long *slots = (long*)(unsigned long long)self->data;
|
|
157
|
+
void *old = (void*)slots[i];
|
|
158
|
+
void *new_value = (void*)f(old);
|
|
159
|
+
slots[i] = (long)new_value;
|
|
160
|
+
if (old != new_value) {
|
|
161
|
+
T_destroy(old);
|
|
162
|
+
free(old);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
```
|
|
166
|
+
#arch: aarch64
|
|
167
|
+
// x19 = self, x1 = i, x2 = f. Class slots are one pointer wide.
|
|
168
|
+
stp x20, x21, [sp, #-16]!
|
|
169
|
+
stp x22, x23, [sp, #-16]!
|
|
170
|
+
mov x22, x2
|
|
171
|
+
ldr x9, [x19, #8]
|
|
172
|
+
lsl x3, x1, #3
|
|
173
|
+
add x20, x9, x3
|
|
174
|
+
ldr x0, [x20]
|
|
175
|
+
blr x22
|
|
176
|
+
mov x23, x0
|
|
177
|
+
ldr x9, [x20]
|
|
178
|
+
str x23, [x20]
|
|
179
|
+
cmp x9, x23
|
|
180
|
+
b.eq .Lcb_modify_T_done
|
|
181
|
+
mov x0, x9
|
|
182
|
+
bl T_destroy
|
|
183
|
+
mov x0, x9
|
|
184
|
+
bl _free
|
|
185
|
+
.Lcb_modify_T_done:
|
|
186
|
+
ldp x22, x23, [sp], #16
|
|
187
|
+
ldp x20, x21, [sp], #16
|
|
188
|
+
```
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
internal inline func store_int = (ref self, int i: i >= 0 && i < self.cap, int val: val != 0) {
|
|
142
192
|
```
|
|
143
193
|
#arch: c
|
|
144
194
|
((long*)(unsigned long long)self->data)[i] = val;
|
|
@@ -152,7 +202,7 @@ pub struct ClassBuffer<T> {
|
|
|
152
202
|
```
|
|
153
203
|
}
|
|
154
204
|
|
|
155
|
-
inline func move_int = (ref self, int i: i >= 0 && i < self.cap, out int) {
|
|
205
|
+
internal inline func move_int = (ref self, int i: i >= 0 && i < self.cap, out int) {
|
|
156
206
|
```
|
|
157
207
|
#arch: c
|
|
158
208
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -172,7 +222,7 @@ pub struct ClassBuffer<T> {
|
|
|
172
222
|
|
|
173
223
|
// Store into a slot, freeing the previous value (it is an owned class
|
|
174
224
|
// pointer): run its destroy then free it before overwriting.
|
|
175
|
-
func replace_int = (ref self, int i: i >= 0 && i < self.cap, int val: val != 0) {
|
|
225
|
+
internal func replace_int = (ref self, int i: i >= 0 && i < self.cap, int val: val != 0) {
|
|
176
226
|
```
|
|
177
227
|
#arch: c
|
|
178
228
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -204,9 +254,11 @@ pub struct ClassBuffer<T> {
|
|
|
204
254
|
}
|
|
205
255
|
|
|
206
256
|
// T-typed alias of replace_int: free the outgoing class pointer (destroy +
|
|
207
|
-
// free) then store the new one. Used by List.set so the uniform
|
|
257
|
+
// free) then store the new one. Used by List.set so the uniform API
|
|
208
258
|
// preserves ownership for class elements.
|
|
209
|
-
|
|
259
|
+
// Overwrite a live slot: destroy + free the displaced instance, then take
|
|
260
|
+
// OWNERSHIP of `val` (`move`) — same transfer rule as store.
|
|
261
|
+
func replace = (ref self, int i: i >= 0 && i < self.cap, move T val: val != 0) {
|
|
210
262
|
```
|
|
211
263
|
#arch: c
|
|
212
264
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -239,10 +291,10 @@ pub struct ClassBuffer<T> {
|
|
|
239
291
|
|
|
240
292
|
// Move slot src into slot dst: reclaim dst's previous instance (destroy +
|
|
241
293
|
// free), take over src's pointer, and zero src — exactly one slot owns the
|
|
242
|
-
// instance afterwards. The class-buffer counterpart of Buffer.
|
|
294
|
+
// instance afterwards. The class-buffer counterpart of Buffer.shift,
|
|
243
295
|
// used by Map/Set.remove's backward-shift so shifting entries neither
|
|
244
296
|
// duplicates nor orphans owned instances.
|
|
245
|
-
func
|
|
297
|
+
func shift = (ref self, int dst: dst >= 0 && dst < self.cap, int src: src >= 0 && src < self.cap) {
|
|
246
298
|
```
|
|
247
299
|
#arch: c
|
|
248
300
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -286,7 +338,7 @@ pub struct ClassBuffer<T> {
|
|
|
286
338
|
// pointers, returned as a `view T` (T is a class, so each element is an
|
|
287
339
|
// 8-byte pointer). Borrows from self — invalidated on reassignment. Mirrors
|
|
288
340
|
// Buffer.slice; ClassBuffer stores 8-byte slots, so the element width is 8.
|
|
289
|
-
func slice = (self, int start: start >= 0, int end: end >= start, out view T) {
|
|
341
|
+
func slice = (self, int start: start >= 0, int end: end >= start && end <= self.cap, out view T) {
|
|
290
342
|
```
|
|
291
343
|
#arch: c
|
|
292
344
|
nomen_view _r;
|