nomen-lang 0.8.0 → 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 (73) hide show
  1. package/audit_runtime.c +12 -2
  2. package/core/System/Arena.nm +8 -1
  3. package/core/System/Buffer.nm +366 -9
  4. package/core/System/ClassBuffer.nm +133 -1
  5. package/core/System/Controls/Alert.nm +2 -2
  6. package/core/System/Controls/App.nm +12 -12
  7. package/core/System/Controls/Button.nm +1 -1
  8. package/core/System/Controls/CheckBox.nm +2 -2
  9. package/core/System/Controls/ColorPicker.nm +1 -1
  10. package/core/System/Controls/ComboBox.nm +1 -1
  11. package/core/System/Controls/Container.nm +7 -1
  12. package/core/System/Controls/GroupBox.nm +1 -1
  13. package/core/System/Controls/ImageView.nm +1 -1
  14. package/core/System/Controls/LevelIndicator.nm +1 -1
  15. package/core/System/Controls/ListView.nm +1 -1
  16. package/core/System/Controls/Menu.nm +1 -1
  17. package/core/System/Controls/OutlineView.nm +1 -1
  18. package/core/System/Controls/Pagination.nm +8 -8
  19. package/core/System/Controls/Popover.nm +1 -1
  20. package/core/System/Controls/Progress.nm +1 -1
  21. package/core/System/Controls/Radio.nm +2 -2
  22. package/core/System/Controls/SearchField.nm +1 -1
  23. package/core/System/Controls/SecureField.nm +1 -1
  24. package/core/System/Controls/Segmented.nm +1 -1
  25. package/core/System/Controls/Select.nm +1 -1
  26. package/core/System/Controls/Separator.nm +1 -1
  27. package/core/System/Controls/Sheet.nm +1 -1
  28. package/core/System/Controls/Slider.nm +1 -1
  29. package/core/System/Controls/Spinner.nm +2 -2
  30. package/core/System/Controls/SplitView.nm +1 -1
  31. package/core/System/Controls/Stepper.nm +1 -1
  32. package/core/System/Controls/Switch.nm +1 -1
  33. package/core/System/Controls/Table.nm +1 -1
  34. package/core/System/Controls/Tabs.nm +1 -1
  35. package/core/System/Controls/Text.nm +1 -1
  36. package/core/System/Controls/TextBox.nm +2 -2
  37. package/core/System/Controls/TextView.nm +1 -1
  38. package/core/System/Controls/Toggle.nm +2 -2
  39. package/core/System/Controls/Toolbar.nm +1 -1
  40. package/core/System/Controls/Window.nm +1 -2
  41. package/core/System/LinkedList.nm +8 -1
  42. package/core/System/List.nm +68 -12
  43. package/core/System/Map.nm +174 -18
  44. package/core/System/Mutex.nm +4 -4
  45. package/core/System/Random.nm +3 -4
  46. package/core/System/Set.nm +94 -20
  47. package/core/System/StableList.nm +371 -0
  48. package/core/System/Stream/Directory.nm +2 -2
  49. package/core/System/Stream/File.nm +2 -2
  50. package/core/System/Stream/Http.nm +2 -2
  51. package/core/System/String.nm +10 -5
  52. package/core/System/StringBuilder.nm +213 -2
  53. package/core/System/Text/CharIndex.nm +9 -9
  54. package/core/System/Text/Json.nm +60 -46
  55. package/core/System/Text/JsonTree.nm +120 -92
  56. package/core/System/Text/Regex.nm +23 -18
  57. package/core/System/Text/Utf8.nm +5 -5
  58. package/core/System/float.nm +36 -22
  59. package/core/System/int.nm +80 -28
  60. package/core/System/int16.nm +11 -0
  61. package/core/System/int32.nm +11 -0
  62. package/core/System/int64.nm +11 -0
  63. package/core/System/int8.nm +11 -0
  64. package/core/System/uint.nm +11 -0
  65. package/core/System/uint16.nm +11 -0
  66. package/core/System/uint32.nm +11 -0
  67. package/core/System/uint64.nm +11 -0
  68. package/core/System/uint8.nm +11 -0
  69. package/core/docs/System/StringBuilder.md +6 -0
  70. package/core/docs/System/Text/JsonTree.md +3 -2
  71. package/dist/index.mjs +36575 -30747
  72. package/package.json +1 -1
  73. package/src/bench_loop.nm +9 -9
@@ -16,7 +16,14 @@ pub mv struct LinkedList<T>: Enumerable {
16
16
  pub func add = (ref self, mv T value) {
17
17
  var int idx = self.count
18
18
  self.values.grow(idx + 1)
19
- self.values.store(idx, value)
19
+ // The value arrives owned (`mv T value`): move a value-struct element
20
+ // into the fresh slot (the caller's storage zeroes, no deep copy);
21
+ // strings keep the deep-copying store (their pair arrives by value).
22
+ if T_IS_VALUE_STRUCT {
23
+ self.values.store_take(idx, value)
24
+ } else {
25
+ self.values.store(idx, value)
26
+ }
20
27
  self.nexts.grow_int(idx + 1)
21
28
  self.nexts.store_int(idx, -1)
22
29
  self.prevs.grow_int(idx + 1)
@@ -6,18 +6,52 @@ pub mv struct List<T>: Viewable {
6
6
  var items = Buffer<T>()
7
7
 
8
8
  pub func push = (ref self, mv T value) {
9
- if self.length >= self.items.cap {
10
- var new_cap = 4
11
- var int old_cap = self.items.cap
12
- if old_cap >= 4 {
13
- new_cap = old_cap * 2
9
+ if self.length < self.items.cap {
10
+ // The value arrives owned (`mv T value`): a value-struct element
11
+ // moves into the fresh slot (store_take — the caller's storage
12
+ // zeroes, no deep copy); strings and scalars keep the
13
+ // deep-copying store.
14
+ if T_IS_VALUE_STRUCT {
15
+ self.items.store_take(self.length, value)
16
+ } else {
17
+ self.items.store(self.length, value)
14
18
  }
15
- self.items.grow(new_cap)
19
+ self.length += 1
20
+ return
21
+ }
22
+ self.reserve(1)
23
+ if T_IS_VALUE_STRUCT {
24
+ self.items.store_take(self.length, value)
25
+ } else {
26
+ self.items.store(self.length, value)
16
27
  }
17
- self.items.store(self.length, value)
18
28
  self.length += 1
19
29
  }
20
30
 
31
+ // Capacity for `extra` more elements without reallocation: the exact
32
+ // growth policy `push` uses (double from 4, so repeated single pushes
33
+ // amortize). Returns the resulting capacity. Splitting it out of `push`
34
+ // is what makes the compiler's in-place push lowering possible: the
35
+ // destination slot for a `push(parse(line))` argument must exist before
36
+ // the argument call runs, so the backend reserves first and constructs
37
+ // the callee's result directly into `items.data[length]` (the in-place
38
+ // #4). Sound for extra=1 by the same proof as push's old inline growth:
39
+ // length + 1 > cap grows to exactly the capacity push would have grown
40
+ // to.
41
+ internal func reserve = (ref self, int extra: extra >= 0, out int) {
42
+ if self.length + extra <= self.items.cap {
43
+ return self.items.cap
44
+ }
45
+ var int new_cap = 4
46
+ var int old_cap = self.items.cap
47
+ if old_cap >= 4 {
48
+ new_cap = old_cap * 2
49
+ }
50
+ while new_cap < self.length + extra; new_cap *= 2 { }
51
+ self.items.grow(new_cap)
52
+ return self.items.cap
53
+ }
54
+
21
55
  pub func pop = (ref self, mv out T) {
22
56
  if self.length <= 0 {
23
57
  return self.items.move(0)
@@ -73,7 +107,30 @@ pub mv struct List<T>: Viewable {
73
107
  panic("index out of range")
74
108
  }
75
109
 
76
- // Address of element `i` (library-only): the ELEMENT BORROW primitive —
110
+ // Address of the APPEND slot (element `length`) — the in-place push
111
+ // lowering's destination primitive, the in-place push contract). reserve must have
112
+ // run, so the slot exists. Offsets mirror the mono layout: length @8,
113
+ // items.data @24 (the Buffer mono is {_vt, data, cap} = 24 bytes).
114
+ internal func append_slot_ptr = (ref self, out ptr T) {
115
+ ```
116
+ #arch: c
117
+ // Byte math + void* — the body must compile for EVERY element kind
118
+ // (values, strings, class/trait handles), so it never names T.
119
+ // T_SIZE substitutes to the element's real stride.
120
+ return (void *)((unsigned long long)self->items.data +
121
+ (unsigned long long)self->length * T_SIZE);
122
+ ```
123
+ ```
124
+ #arch: aarch64
125
+ // x0 = self (stack-less raw: read from x0). Returns x0 = slot address.
126
+ ldr x9, [x0, #8]
127
+ ldr x10, [x0, #24]
128
+ mov x11, #T_SIZE
129
+ madd x0, x9, x11, x10
130
+ ```
131
+ }
132
+
133
+ // Address of element `i` (INTERNAL — the ELEMENT BORROW primitive):
77
134
  // `ref T x = list.at(i)` (docs/HANDLERS.md Stage 4, dynamic-tree
78
135
  // dispatch) lowers to this, binding the alias to the slot itself rather
79
136
  // than to a bitwise copy. The pointer is valid while the list is
@@ -82,7 +139,7 @@ pub mv struct List<T>: Viewable {
82
139
  // rules. The length constraint is the caller's proof obligation — the
83
140
  // handler desugar's synthesized loops satisfy it from the loop
84
141
  // condition, exactly like `.at`.
85
- pub func slot_ptr = (ref self, int i: i >= 0 && i < self.length, out ptr T) {
142
+ internal func slot_ptr = (ref self, int i: i >= 0 && i < self.length, out ptr T) {
86
143
  return self.items.slot_ptr(i)
87
144
  }
88
145
 
@@ -99,9 +156,8 @@ pub mv struct List<T>: Viewable {
99
156
  }
100
157
  var T v = self.items.move(i)
101
158
  var int last = self.length - 1
102
- var int j = i
103
- while j < last; j += 1 {
104
- self.items.shift(j, j + 1)
159
+ if i < last {
160
+ self.items.move_range(i, i + 1, last - i)
105
161
  }
106
162
  self.length -= 1
107
163
  return v
@@ -7,12 +7,21 @@
7
7
  * bucket indexing is a mask (`hash & (cap - 1)`) instead of a division — and
8
8
  * it is correct for negative hashes too: two's-complement `&` already lands
9
9
  * in `[0, cap)`, which is why no negative adjustment is needed.
10
+ *
11
+ * Each slot carries its key's hash (truncated to 32 bits) in a parallel
12
+ * `hashes` buffer. Probes compare the stored hash before the key itself, so
13
+ * a probed slot whose hash differs skips the key load and `==` entirely (the
14
+ * Swiss-table short-circuit: the full key compare only runs on a hash match,
15
+ * and a false match still falls through to `==`, so truncation costs
16
+ * nothing); rehash re-inserts hash-free, reading the stored word instead of
17
+ * calling `key.hash()` again.
10
18
  **/
11
19
  pub mv struct Map<TK: Hashable + Equatable, TV> {
12
20
  var length = 0
13
21
  var keys = Buffer<TK>()
14
22
  var values = Buffer<TV>()
15
23
  var used = Buffer<int>()
24
+ var hashes = Buffer<int>()
16
25
 
17
26
  // Variadic-tuple constructor: Map<string, int>(["a", 1], ["b", 2]) or
18
27
  // Map(["a", 1], ["b", 2]) (type args inferred from the first pair).
@@ -39,28 +48,84 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
39
48
  cap = self.keys.alloc(8)
40
49
  self.values.alloc(8)
41
50
  self.used.alloc_u32(8)
51
+ self.hashes.alloc_u32(8)
42
52
  }
43
53
  if self.length * 4 >= cap * 3 {
44
54
  self.rehash(cap * 2)
45
55
  cap = self.keys.cap
46
56
  }
47
- var int idx = self.find_slot(key, cap)
57
+ var uint32 h = key.hash() as uint32
58
+ var int idx = self.find_slot(key, h, cap)
48
59
  if self.used.load_u32(idx) == 0 {
49
60
  self.used.store_u32(idx, 1)
50
- self.keys.store(idx, key)
51
- self.values.store(idx, value)
61
+ // Both arrive owned (`mv TK key, mv TV value`): a value-struct
62
+ // key/value moves into its fresh slot (the caller's storage
63
+ // zeroes, no deep copy); strings keep the deep-copying store
64
+ // (their pair arrives by value).
65
+ if TK_IS_VALUE_STRUCT {
66
+ self.keys.store_take(idx, key)
67
+ } else {
68
+ self.keys.store(idx, key)
69
+ }
70
+ if TV_IS_VALUE_STRUCT {
71
+ self.values.store_take(idx, value)
72
+ } else {
73
+ self.values.store(idx, value)
74
+ }
75
+ self.hashes.store_u32(idx, h)
52
76
  self.length += 1
53
77
  } else {
54
78
  self.values.replace(idx, value)
55
79
  }
56
80
  }
57
81
 
82
+ // The IN-PLACE STORE primitive — the write-side sibling of `get_ptr`.
83
+ // Runs set's whole preamble (alloc-if-empty, rehash-at-load-factor) and
84
+ // probe with the KEY (which precedes the value in evaluation order at a
85
+ // `set(key, f(x))` site), then does everything set does EXCEPT write the
86
+ // value: the insert path marks the slot used, stores the key, and bumps
87
+ // length; the replace path reclaims the displaced value through
88
+ // `values.destroy_at` (the destroy half of replace, no copy). It returns
89
+ // the VALUE SLOT's address, so the compiler's in-place `set` lowering
90
+ // constructs `f(x)` straight into it — no `_param_N` temp home, no
91
+ // callee-side deep copy, no temp free. INTERNAL, like every slot
92
+ // primitive: the raw pointer must not cross the library boundary, and
93
+ // the caller must overwrite the slot before it is read or destroyed
94
+ // again (both paths leave it owning nothing).
95
+ internal func set_slot_ptr = (ref self, TK key, out ptr TV) {
96
+ var int cap = self.keys.cap
97
+ if cap == 0 {
98
+ // alloc_T: for Buffer<TK> it is T_SIZE-aware (string keys are
99
+ // 16-byte slots); for ClassBuffer<TV> it delegates to alloc_int
100
+ // (one pointer per slot). Both have it for generic parity.
101
+ cap = self.keys.alloc(8)
102
+ self.values.alloc(8)
103
+ self.used.alloc_u32(8)
104
+ self.hashes.alloc_u32(8)
105
+ }
106
+ if self.length * 4 >= cap * 3 {
107
+ self.rehash(cap * 2)
108
+ cap = self.keys.cap
109
+ }
110
+ var uint32 h = key.hash() as uint32
111
+ var int idx = self.find_slot(key, h, cap)
112
+ if self.used.load_u32(idx) == 0 {
113
+ self.used.store_u32(idx, 1)
114
+ self.keys.store(idx, key)
115
+ self.hashes.store_u32(idx, h)
116
+ self.length += 1
117
+ } else {
118
+ self.values.destroy_at(idx)
119
+ }
120
+ return self.values.slot_ptr(idx)
121
+ }
122
+
58
123
  pub func get = (self, TK key, out TV) {
59
124
  var int cap = self.keys.cap
60
125
  if cap == 0 {
61
126
  return 0
62
127
  }
63
- var int idx = self.find_slot(key, cap)
128
+ var int idx = self.find_slot(key, key.hash() as uint32, cap)
64
129
  if self.used.load_u32(idx) == 0 {
65
130
  return 0
66
131
  }
@@ -74,7 +139,7 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
74
139
  if cap == 0 {
75
140
  return fallback
76
141
  }
77
- var int idx = self.find_slot(key, cap)
142
+ var int idx = self.find_slot(key, key.hash() as uint32, cap)
78
143
  if self.used.load_u32(idx) == 0 {
79
144
  return fallback
80
145
  }
@@ -88,19 +153,45 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
88
153
  if cap == 0 {
89
154
  panic("key not found")
90
155
  }
91
- var int idx = self.find_slot(key, cap)
156
+ var int idx = self.find_slot(key, key.hash() as uint32, cap)
92
157
  if self.used.load_u32(idx) == 0 {
93
158
  panic("key not found")
94
159
  }
95
160
  return self.values.load(idx)
96
161
  }
97
162
 
163
+ // The VALUE SLOT BORROW — `Buffer.slot_ptr` for Map values. One probe
164
+ // (a `has` + `get` pair collapses into it) and the result aliases the
165
+ // slot instead of copying it, so container-bearing values stop paying
166
+ // the owning-specialized deep copy on every lookup. Traps when the key
167
+ // is absent — a missing key is a bug at a slot-borrow site (miss-as-case
168
+ // lookups use `get_or`). INTERNAL, like every slot-borrow primitive: the
169
+ // raw pointer must not cross the library boundary, so user code reaches
170
+ // it only through the element borrow, `ref TV v = map.get_or_panic(k)`,
171
+ // which the checker rewrites to this — the same lowering `ref T x =
172
+ // list.at(i)` gets from `slot_ptr`. The borrow is unpoliced inside the
173
+ // library: rehash/remove move or free slots, so do not hold it across
174
+ // one. This is also the anchor for in-place `set(key, f(x))` (probe
175
+ // first, construct into the slot).
176
+ internal func get_ptr = (self, TK key, out ptr TV) {
177
+ unsafe {
178
+ var int cap = self.keys.cap
179
+ if cap > 0 {
180
+ var int idx = self.find_slot(key, key.hash() as uint32, cap)
181
+ if self.used.load_u32(idx) != 0 {
182
+ return self.values.slot_ptr(idx)
183
+ }
184
+ }
185
+ panic("key not found")
186
+ }
187
+ }
188
+
98
189
  pub func has = (self, TK key, out bool) {
99
190
  var int cap = self.keys.cap
100
191
  if cap == 0 {
101
192
  return false
102
193
  }
103
- var int idx = self.find_slot(key, cap)
194
+ var int idx = self.find_slot(key, key.hash() as uint32, cap)
104
195
  return self.used.load_u32(idx) != 0
105
196
  }
106
197
 
@@ -109,7 +200,7 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
109
200
  if cap == 0 {
110
201
  return
111
202
  }
112
- var int idx = self.find_slot(key, cap)
203
+ var int idx = self.find_slot(key, key.hash() as uint32, cap)
113
204
  if self.used.load_u32(idx) == 0 {
114
205
  return
115
206
  }
@@ -122,7 +213,9 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
122
213
  // so entries past a non-moveable one are not stranded behind an empty
123
214
  // slot (which would make find_slot miss them). No tombstones, so
124
215
  // find_slot/get/has/set are unchanged, and the cost is the local cluster
125
- // length — not a full O(cap) rehash on every delete.
216
+ // length — not a full O(cap) rehash on every delete. `home` reads the
217
+ // stored hash word (no re-hash per shifted entry), and the word moves
218
+ // with the entry — find_slot's hash short-circuit trusts it.
126
219
  var int gap = idx
127
220
  var int k = idx
128
221
  while true {
@@ -133,7 +226,7 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
133
226
  if self.used.load_u32(k) == 0 {
134
227
  break
135
228
  }
136
- var int home = (self.keys.load(k).hash() as int) & (cap - 1)
229
+ var int home = (self.hashes.load_u32(k) as int) & (cap - 1)
137
230
  var int dgap = gap - home
138
231
  if dgap < 0 {
139
232
  dgap = dgap + cap
@@ -150,34 +243,77 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
150
243
  // used.store marks it empty (the buffer destroy skips it).
151
244
  self.keys.shift(gap, k)
152
245
  self.values.shift(gap, k)
246
+ self.hashes.store_u32(gap, self.hashes.load_u32(k))
153
247
  self.used.store_u32(gap, 1)
154
248
  gap = k
155
249
  }
156
250
  }
251
+ if TK_NEEDS_STRDUP {
252
+ if gap == idx {
253
+ // No entry shifted: the removed entry still owns its strdup'd
254
+ // bytes in its slot (a shift's first move would have
255
+ // reclaimed them — see shift_T above). destroy_at frees the
256
+ // buffer and zeroes the slot: a later set() that lands in
257
+ // this hole stores over the bytes assuming a fresh slot, and
258
+ // a stranded-then-overwritten entry leaks.
259
+ self.keys.destroy_at(idx)
260
+ }
261
+ }
262
+ if TK_IS_VALUE_STRUCT {
263
+ if gap == idx {
264
+ // Same hole for a value-struct key: reclaim the slot's owned
265
+ // fields (destroy_at frees and zeroes; scalars-only structs
266
+ // reclaim nothing and the call is a no-op).
267
+ self.keys.destroy_at(idx)
268
+ }
269
+ }
270
+ if TV_NEEDS_STRDUP {
271
+ if gap == idx {
272
+ self.values.destroy_at(idx)
273
+ }
274
+ }
275
+ if TV_IS_VALUE_STRUCT {
276
+ if gap == idx {
277
+ self.values.destroy_at(idx)
278
+ }
279
+ }
157
280
  self.used.store_u32(gap, 0)
158
281
  self.length = self.length - 1
159
282
  }
160
283
 
161
- // An independent copy: a fresh Map whose every member slab is a fresh
162
- // `Buffer.copy` (which deep-copies mv key/value elements), with the same
163
- // length and entries. Not available for class/trait keys or values (the
284
+ // An independent copy: a fresh Map whose every member slab is fresh
285
+ // (keys/values deep-copied per element, `used`/`hashes` copied word by
286
+ // word), with the same length and entries. The u32-word buffers are
287
+ // copied through their own primitives — `Buffer.copy` is T_SIZE-aware
288
+ // (8-byte loads for `int`), which would read past a 4-byte-per-slot
289
+ // `alloc_u32` slab. Not available for class/trait keys or values (the
164
290
  // member routes to ClassBuffer, which has no copy — the checker rejects
165
291
  // the call; see the copy gate in check_access_node).
166
292
  pub func copy = (self, out Map<TK, TV>) {
167
293
  var dst = Map<TK, TV>()
168
294
  dst.keys = self.keys.copy()
169
295
  dst.values = self.values.copy()
170
- dst.used = self.used.copy()
296
+ dst.used.alloc_u32(self.keys.cap)
297
+ dst.hashes.alloc_u32(self.keys.cap)
298
+ var i = 0
299
+ while i < self.keys.cap; i += 1 {
300
+ dst.used.store_u32(i, self.used.load_u32(i))
301
+ dst.hashes.store_u32(i, self.hashes.load_u32(i))
302
+ }
171
303
  dst.length = self.length
172
304
  return dst
173
305
  }
174
306
 
175
- func find_slot = (self, TK key, int cap, out int: out >= 0 && out < cap) {
307
+ func find_slot = (self, TK key, uint32 h, int cap, out int: out >= 0 && out < cap) {
176
308
  // cap is a power of two, so this is `hash mod cap` via a mask —
177
309
  // non-negative for any hash (two's complement), no adjustment.
178
- var int idx = (key.hash() as int) & (cap - 1)
310
+ var int idx = (h as int) & (cap - 1)
179
311
  while self.used.load_u32(idx) != 0 {
180
- if self.keys.load(idx) == key {
312
+ // The stored-hash word short-circuits the key compare: a probed
313
+ // slot whose hash differs (every step of a collision walk past
314
+ // the home slot) skips the key load and `==`. A hash match still
315
+ // falls through to `==`, so the 32-bit truncation costs nothing.
316
+ if self.hashes.load_u32(idx) == h && self.keys.load(idx) == key {
181
317
  return idx
182
318
  }
183
319
  idx += 1
@@ -188,15 +324,23 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
188
324
  return idx
189
325
  }
190
326
 
327
+ // Rehash hash-free: the stored per-slot hash words move with the entries,
328
+ // so no `key.hash()` runs here and the insertion probe needs no key
329
+ // compares (keys arrive unique — they came from a valid table). This is
330
+ // the other half of the Swiss-table trick; the old form re-inserted via
331
+ // `self.set(mv k, mv v)`, re-hashing every key and re-comparing keys on
332
+ // every probed slot.
191
333
  func rehash = (ref self, int new_cap) {
192
334
  var Buffer<TK> old_keys = mv self.keys swap Buffer<TK>()
193
335
  var Buffer<TV> old_values = mv self.values swap Buffer<TV>()
194
336
  var Buffer<int> old_used = mv self.used swap Buffer<int>()
337
+ var Buffer<int> old_hashes = mv self.hashes swap Buffer<int>()
195
338
  var int old_cap = old_keys.cap
196
339
 
197
340
  self.keys.alloc(new_cap)
198
341
  self.values.alloc(new_cap)
199
342
  self.used.alloc_u32(new_cap)
343
+ self.hashes.alloc_u32(new_cap)
200
344
  self.length = 0
201
345
 
202
346
  var i = 0
@@ -213,7 +357,19 @@ pub mv struct Map<TK: Hashable + Equatable, TV> {
213
357
  // move_T results.
214
358
  var TK k = old_keys.move(i)
215
359
  var TV v = old_values.move(i)
216
- self.set(mv k, mv v)
360
+ var uint32 h = old_hashes.load_u32(i)
361
+ var int idx = (h as int) & (new_cap - 1)
362
+ while self.used.load_u32(idx) != 0 {
363
+ idx += 1
364
+ if idx >= new_cap {
365
+ idx = 0
366
+ }
367
+ }
368
+ self.used.store_u32(idx, 1)
369
+ self.keys.store(idx, k)
370
+ self.values.store(idx, v)
371
+ self.hashes.store_u32(idx, h)
372
+ self.length += 1
217
373
  }
218
374
  }
219
375
  }
@@ -25,10 +25,10 @@ pub mv struct Mutex {
25
25
  pub var uint64 handle = 0
26
26
 
27
27
  pub func #init = (self) {
28
- // The constructor's `self` is the INLINE instance (a local value in the
29
- // C lowering — methods take the pointer form, the ctor does not), so the
30
- // C body uses the `.` accessor; the aarch64 body writes through x19 =
31
- // &instance as always.
28
+ // The constructor's `self` is the INLINE instance (a local value in the
29
+ // C lowering — methods take the pointer form, the ctor does not), so the
30
+ // C body uses the `.` accessor; the aarch64 body writes through x19 =
31
+ // &instance as always.
32
32
  ```
33
33
  #arch: c
34
34
  self.handle = (unsigned long long)__nomen_mutex_create();
@@ -41,10 +41,9 @@ pub mv struct Random {
41
41
 
42
42
  // The next 64-bit value (splitmix64: golden-ratio step, then two
43
43
  // multiply-xor rounds). Pure Nomen — the 64-bit arithmetic, bitwise
44
- // operators, and hex literals all lower directly. The aarch64 codegen is
45
- // a little looser than a hand-written `#arch` body would be (constant
46
- // materialization, field reloads, non-immediate shift counts); see
47
- // FOLLOWUP.md if this ever needs to be hot.
44
+ // operators, and hex literals all lower directly; the aarch64 64-bit
45
+ // arithmetic folds (PERF_PLAN item 15) keep the emission within a few
46
+ // instructions of a hand-written `#arch` body.
48
47
  pub func next = (ref self, out uint64) {
49
48
  self.state = self.state + 0x9E3779B97F4A7C15
50
49
  var uint64 z = self.state