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.
Files changed (90) hide show
  1. package/audit_runtime.c +12 -2
  2. package/core/System/Arena.nm +14 -5
  3. package/core/System/Array.nm +1 -1
  4. package/core/System/Atomic.nm +3 -3
  5. package/core/System/BigInt.nm +182 -23
  6. package/core/System/Buffer.nm +392 -33
  7. package/core/System/ClassBuffer.nm +145 -13
  8. package/core/System/Controls/Alert.nm +3 -3
  9. package/core/System/Controls/App.nm +17 -17
  10. package/core/System/Controls/Button.nm +2 -2
  11. package/core/System/Controls/CheckBox.nm +3 -3
  12. package/core/System/Controls/ColorPicker.nm +2 -2
  13. package/core/System/Controls/ComboBox.nm +2 -2
  14. package/core/System/Controls/Container.nm +39 -33
  15. package/core/System/Controls/DatePicker.nm +1 -1
  16. package/core/System/Controls/Form.nm +2 -2
  17. package/core/System/Controls/GroupBox.nm +2 -2
  18. package/core/System/Controls/ImageView.nm +2 -2
  19. package/core/System/Controls/Layout.nm +5 -5
  20. package/core/System/Controls/LevelIndicator.nm +2 -2
  21. package/core/System/Controls/ListView.nm +2 -2
  22. package/core/System/Controls/Menu.nm +2 -2
  23. package/core/System/Controls/NumberInput.nm +3 -3
  24. package/core/System/Controls/OtpInput.nm +2 -2
  25. package/core/System/Controls/OutlineView.nm +2 -2
  26. package/core/System/Controls/Pagination.nm +14 -14
  27. package/core/System/Controls/Popover.nm +2 -2
  28. package/core/System/Controls/Progress.nm +2 -2
  29. package/core/System/Controls/Radio.nm +3 -3
  30. package/core/System/Controls/Scrollable.nm +1 -1
  31. package/core/System/Controls/SearchField.nm +2 -2
  32. package/core/System/Controls/SecureField.nm +2 -2
  33. package/core/System/Controls/Segmented.nm +2 -2
  34. package/core/System/Controls/Select.nm +2 -2
  35. package/core/System/Controls/Separator.nm +2 -2
  36. package/core/System/Controls/Sheet.nm +2 -2
  37. package/core/System/Controls/Slider.nm +2 -2
  38. package/core/System/Controls/Spinner.nm +5 -5
  39. package/core/System/Controls/SplitView.nm +3 -3
  40. package/core/System/Controls/Stepper.nm +2 -2
  41. package/core/System/Controls/Switch.nm +2 -2
  42. package/core/System/Controls/Table.nm +2 -2
  43. package/core/System/Controls/Tabs.nm +2 -2
  44. package/core/System/Controls/Text.nm +2 -2
  45. package/core/System/Controls/TextBox.nm +3 -3
  46. package/core/System/Controls/TextView.nm +2 -2
  47. package/core/System/Controls/Toggle.nm +3 -3
  48. package/core/System/Controls/Toolbar.nm +2 -2
  49. package/core/System/Controls/Window.nm +12 -5
  50. package/core/System/Fiber.nm +16 -3
  51. package/core/System/Graph.nm +2 -2
  52. package/core/System/LinkedList.nm +13 -4
  53. package/core/System/List.nm +76 -17
  54. package/core/System/Map.nm +179 -21
  55. package/core/System/Mutex.nm +4 -4
  56. package/core/System/Random.nm +6 -7
  57. package/core/System/Set.nm +99 -23
  58. package/core/System/StableList.nm +371 -0
  59. package/core/System/Stream/Directory.nm +2 -2
  60. package/core/System/Stream/File.nm +18 -18
  61. package/core/System/Stream/Http.nm +5 -5
  62. package/core/System/Stream/Tcp.nm +5 -5
  63. package/core/System/String.nm +10 -5
  64. package/core/System/StringBuilder.nm +223 -12
  65. package/core/System/Task.nm +56 -64
  66. package/core/System/Test.nm +18 -18
  67. package/core/System/Text/CharIndex.nm +9 -9
  68. package/core/System/Text/Chars.nm +1 -1
  69. package/core/System/Text/Json.nm +72 -58
  70. package/core/System/Text/JsonTree.nm +126 -98
  71. package/core/System/Text/Regex.nm +43 -38
  72. package/core/System/Text/Utf8.nm +5 -5
  73. package/core/System/Thread.nm +26 -3
  74. package/core/System/Tree.nm +19 -3
  75. package/core/System/float.nm +36 -22
  76. package/core/System/int.nm +80 -28
  77. package/core/System/int16.nm +11 -0
  78. package/core/System/int32.nm +11 -0
  79. package/core/System/int64.nm +11 -0
  80. package/core/System/int8.nm +11 -0
  81. package/core/System/uint.nm +11 -0
  82. package/core/System/uint16.nm +11 -0
  83. package/core/System/uint32.nm +11 -0
  84. package/core/System/uint64.nm +11 -0
  85. package/core/System/uint8.nm +11 -0
  86. package/core/docs/System/StringBuilder.md +6 -0
  87. package/core/docs/System/Text/JsonTree.md +3 -2
  88. package/dist/index.mjs +37622 -30603
  89. package/package.json +1 -1
  90. package/src/bench_loop.nm +9 -9
@@ -23,8 +23,6 @@
23
23
  * Conforms to `Awaitable`: `.wait()` may park the current execution context.
24
24
  **/
25
25
  pub mv struct Task<T> : Awaitable {
26
- pub var uint64 handle = 0
27
- pub var done = false
28
26
  pub var uint64 result_slot = 0
29
27
  pub var uint64 cancel_flag = 0
30
28
  pub var uint64 future = 0
@@ -46,8 +44,6 @@ pub mv struct Task<T> : Awaitable {
46
44
  // names. Struct receivers travel by address regardless (the raw body
47
45
  // reads `self->future`), so the ABI is unchanged; the bare spelling is
48
46
  // what lets the read-only `Awaitable` dispatch form typecheck.
49
- // The legacy `pthread_join` fallback is gone — `handle` has been
50
- // permanently zero since the pool migration.
51
47
  pub func wait = (self) {
52
48
  ```
53
49
  #arch: c
@@ -58,7 +54,7 @@ pub mv struct Task<T> : Awaitable {
58
54
  ```
59
55
  #arch: aarch64
60
56
  // if (self->future) __nomen_future_wait(self->future)
61
- ldr x0, [x19, #40]
57
+ ldr x0, [x19, #24]
62
58
  cbz x0, .Lwait_done
63
59
  bl ___nomen_future_wait
64
60
  .Lwait_done:
@@ -71,18 +67,22 @@ pub mv struct Task<T> : Awaitable {
71
67
  // trampoline writes the function's return value there before exiting.
72
68
  // The result is MOVED OUT: the slot is zeroed as the value leaves, and
73
69
  // `mv out T` marks the caller as the owner. Call it once; a second call
74
- // returns the zero value. For tasks with no return value (void
75
- // functions), returns the zero value.
76
- pub func result = (ref self, mv out T) {
70
+ // returns the zero value. An empty handle (never launched, machinery
71
+ // already destroyed) owns no slot — it also returns the zero value
72
+ // rather than reading through a null pointer. For tasks with no return
73
+ // value (void functions), returns the zero value.
74
+ pub func result = (ref self {}, mv out T) {
77
75
  ```
78
76
  #arch: c
79
77
  if (self->future) {
80
78
  __nomen_future_wait((struct nomen_future *)self->future);
81
- } else if (self->handle && !self->done) {
82
- void *ret;
83
- pthread_join((pthread_t)self->handle, &ret);
84
- self->handle = 0;
85
- self->done = 1;
79
+ }
80
+ // An empty or already-consumed handle owns no result cell — return
81
+ // the zero value instead of reading through a null slot.
82
+ if (!self->result_slot) {
83
+ T _z;
84
+ memset(&_z, 0, sizeof(T));
85
+ return _z;
86
86
  }
87
87
  T _r = *(T *)self->result_slot;
88
88
  memset((void *)self->result_slot, 0, sizeof(T));
@@ -91,72 +91,64 @@ pub mv struct Task<T> : Awaitable {
91
91
  ```
92
92
  #arch: aarch64
93
93
  // Wait for completion (same as wait method)
94
- ldr x0, [x19, #40]
95
- cbz x0, .Lresult_handle
96
- bl ___nomen_future_wait
97
- b .Lresult_read
98
- .Lresult_handle:
99
- ldr x0, [x19, #8]
94
+ ldr x0, [x19, #24]
100
95
  cbz x0, .Lresult_read
101
- ldrb w1, [x19, #16]
102
- cbnz w1, .Lresult_read
103
- sub sp, sp, #16
104
- mov x1, sp
105
- bl _pthread_join
106
- add sp, sp, #16
107
- str xzr, [x19, #8]
108
- mov w0, #1
109
- strb w0, [x19, #16]
96
+ bl ___nomen_future_wait
110
97
  .Lresult_read:
98
+ // An empty or already-consumed handle owns no result cell: return
99
+ // the zero T instead of reading through a null slot.
100
+ ldr x8, [x19, #8]
101
+ cbz x8, .Lresult_zero
111
102
  // The result cell is always 16 bytes (see the spawn build phase):
112
103
  // load the full pair — a fat string returns (ptr, len) in x0/x1, a
113
104
  // scalar returns its word in x0 — then zero the cell (the value is
114
105
  // moved out; a second call observes the zero).
115
- ldr x8, [x19, #24]
116
106
  ldr x0, [x8]
117
107
  ldr x1, [x8, #8]
118
108
  stp xzr, xzr, [x8]
109
+ b .Lresult_end
110
+ .Lresult_zero:
111
+ mov x0, #0
112
+ mov x1, #0
113
+ .Lresult_end:
119
114
  ```
120
115
  }
121
116
 
122
117
  // Block until the task finishes, then return its result as a uint64.
123
118
  // Convenience for the common case; same as result() for uint64 tasks.
124
- pub func result_uint64 = (ref self, out uint64) {
119
+ // Like result(), it zeroes the cell as the value leaves (one end state
120
+ // for a consumed handle), and an empty handle returns zero.
121
+ pub func result_uint64 = (ref self {}, out uint64) {
125
122
  ```
126
123
  #arch: c
127
124
  if (self->future) {
128
125
  __nomen_future_wait((struct nomen_future *)self->future);
129
- } else if (self->handle && !self->done) {
130
- void *ret;
131
- pthread_join((pthread_t)self->handle, &ret);
132
- self->handle = 0;
133
- self->done = 1;
134
126
  }
135
- return *(unsigned long long *)self->result_slot;
127
+ if (!self->result_slot) {
128
+ return 0;
129
+ }
130
+ unsigned long long _r = *(unsigned long long *)self->result_slot;
131
+ *(unsigned long long *)self->result_slot = 0;
132
+ return _r;
136
133
  ```
137
134
  ```
138
135
  #arch: aarch64
139
136
  // Wait for completion
140
- ldr x0, [x19, #40]
141
- cbz x0, .Lru64_handle
142
- bl ___nomen_future_wait
143
- b .Lru64_read
144
- .Lru64_handle:
145
- ldr x0, [x19, #8]
137
+ ldr x0, [x19, #24]
146
138
  cbz x0, .Lru64_read
147
- ldrb w1, [x19, #16]
148
- cbnz w1, .Lru64_read
149
- sub sp, sp, #16
150
- mov x1, sp
151
- bl _pthread_join
152
- add sp, sp, #16
153
- str xzr, [x19, #8]
154
- mov w0, #1
155
- strb w0, [x19, #16]
139
+ bl ___nomen_future_wait
156
140
  .Lru64_read:
157
- // return *(unsigned long long *)self->result_slot
158
- ldr x0, [x19, #24]
159
- ldr x0, [x0]
141
+ // An empty or already-consumed handle owns no result cell: return
142
+ // zero instead of reading through a null slot. Otherwise zero the
143
+ // cell as the value leaves (matching result()'s end state).
144
+ ldr x8, [x19, #8]
145
+ cbz x8, .Lru64_zero
146
+ ldr x0, [x8]
147
+ str xzr, [x8]
148
+ b .Lru64_end
149
+ .Lru64_zero:
150
+ mov x0, #0
151
+ .Lru64_end:
160
152
  ```
161
153
  }
162
154
 
@@ -165,7 +157,7 @@ pub mv struct Task<T> : Awaitable {
165
157
  // handle whose machinery was already consumed). The missing peek
166
158
  // between wait (blocking) and cancel (cooperative): event loops use it
167
159
  // to notice a finished task without parking the UI thread.
168
- pub func is_done = (ref self, out bool) {
160
+ pub func is_done = (ref self {}, out bool) {
169
161
  ```
170
162
  #arch: c
171
163
  return __nomen_future_is_done((struct nomen_future *)self->future);
@@ -173,14 +165,14 @@ pub mv struct Task<T> : Awaitable {
173
165
  ```
174
166
  #arch: aarch64
175
167
  // x0 = self->future; result in x0.
176
- ldr x0, [x19, #40]
168
+ ldr x0, [x19, #24]
177
169
  bl ___nomen_future_is_done
178
170
  ```
179
171
  }
180
172
 
181
173
  // Request that the task stop. Cooperative — the task observes this via
182
174
  // `Task.current_cancelled()` at its own checkpoints. No preemption.
183
- pub func cancel = (ref self) {
175
+ pub func cancel = (ref self {}) {
184
176
  ```
185
177
  #arch: c
186
178
  // Sets the flag and wakes any fiber parked on this task's future, so
@@ -195,12 +187,12 @@ pub mv struct Task<T> : Awaitable {
195
187
  #arch: aarch64
196
188
  // if (self->future) __nomen_future_cancel(self->future);
197
189
  // else if (self->cancel_flag) *(self->cancel_flag) = 1
198
- ldr x0, [x19, #40]
190
+ ldr x0, [x19, #24]
199
191
  cbz x0, .Lcancel_flag_path
200
192
  bl ___nomen_future_cancel
201
193
  b .Lcancel_done
202
194
  .Lcancel_flag_path:
203
- ldr x0, [x19, #32]
195
+ ldr x0, [x19, #16]
204
196
  cbz x0, .Lcancel_done
205
197
  mov x1, #1
206
198
  str x1, [x0]
@@ -393,12 +385,12 @@ pub mv struct Task<T> : Awaitable {
393
385
  ```
394
386
  #arch: aarch64
395
387
  // if (self->future) { free an unconsumed string result; release; zero fields }
396
- ldr x0, [x19, #40]
388
+ ldr x0, [x19, #24]
397
389
  cbz x0, .Ldestroy_done
398
390
  // An unconsumed string result: the cell still holds (ptr, len) —
399
391
  // result() zeroes it when it moves the value out. The T_FAT dispatch
400
392
  // folds to a constant branch after monomorphization.
401
- ldr x8, [x19, #24]
393
+ ldr x8, [x19, #8]
402
394
  mov x0, #T_FAT
403
395
  cmp x0, #0
404
396
  b.eq .Ldestroy_nofree
@@ -411,11 +403,11 @@ pub mv struct Task<T> : Awaitable {
411
403
  stp xzr, xzr, [x8]
412
404
  bl _free
413
405
  .Ldestroy_nofree:
414
- ldr x0, [x19, #40]
406
+ ldr x0, [x19, #24]
415
407
  bl ___nomen_future_release
416
- str xzr, [x19, #40]
417
408
  str xzr, [x19, #24]
418
- str xzr, [x19, #32]
409
+ str xzr, [x19, #8]
410
+ str xzr, [x19, #16]
419
411
  .Ldestroy_done:
420
412
  ```
421
413
  }
@@ -96,7 +96,7 @@ pub mv struct Tester {
96
96
 
97
97
  // (harness) Start timing `name` and clear the per-test state. Called by
98
98
  // the `main` that `nomen test` generates — not by test code.
99
- pub func begin_test = (ref self, string name) {
99
+ pub func begin_test = (ref self { current }, string name) {
100
100
  self.has_failed = false
101
101
  self.passed = 0
102
102
  self.failed = 0
@@ -106,7 +106,7 @@ pub mv struct Tester {
106
106
  }
107
107
 
108
108
  // (harness) Close the current test and emit its `done` record.
109
- pub func end_test = (ref self) {
109
+ pub func end_test = (ref self {}) {
110
110
  const uint64 elapsed = Time.now_ns() - self.started
111
111
  Console.write_line(
112
112
  "\\nomen|done|" + self.current + "|\{self.passed}|\{self.failed}|\{elapsed}",
@@ -124,7 +124,7 @@ pub mv struct Tester {
124
124
  *
125
125
  * t.expect(result == 2, "add(1, 1) should be 2")
126
126
  **/
127
- pub func expect = (ref self, bool condition, string message) {
127
+ pub func expect = (ref self {}, bool condition, string message) {
128
128
  if self.has_failed {
129
129
  return
130
130
  }
@@ -143,7 +143,7 @@ pub mv struct Tester {
143
143
  * t.fail("expected to find the key")
144
144
  * }
145
145
  **/
146
- pub func fail = (ref self, string message) {
146
+ pub func fail = (ref self {}, string message) {
147
147
  if self.has_failed {
148
148
  return
149
149
  }
@@ -154,43 +154,43 @@ pub mv struct Tester {
154
154
  //
155
155
  // Nomen has no generic methods, so there is one overload per nullable
156
156
  // type the compiler can compare against `null`.
157
- pub func assert = (ref self, int? value, string message) {
157
+ pub func assert = (ref self {}, int? value, string message) {
158
158
  self.expect(value != null, message)
159
159
  }
160
160
 
161
- pub func assert = (ref self, uint? value, string message) {
161
+ pub func assert = (ref self {}, uint? value, string message) {
162
162
  self.expect(value != null, message)
163
163
  }
164
164
 
165
- pub func assert = (ref self, int64? value, string message) {
165
+ pub func assert = (ref self {}, int64? value, string message) {
166
166
  self.expect(value != null, message)
167
167
  }
168
168
 
169
- pub func assert = (ref self, uint64? value, string message) {
169
+ pub func assert = (ref self {}, uint64? value, string message) {
170
170
  self.expect(value != null, message)
171
171
  }
172
172
 
173
- pub func assert = (ref self, int8? value, string message) {
173
+ pub func assert = (ref self {}, int8? value, string message) {
174
174
  self.expect(value != null, message)
175
175
  }
176
176
 
177
- pub func assert = (ref self, uint8? value, string message) {
177
+ pub func assert = (ref self {}, uint8? value, string message) {
178
178
  self.expect(value != null, message)
179
179
  }
180
180
 
181
- pub func assert = (ref self, float? value, string message) {
181
+ pub func assert = (ref self {}, float? value, string message) {
182
182
  self.expect(value != null, message)
183
183
  }
184
184
 
185
- pub func assert = (ref self, bool? value, string message) {
185
+ pub func assert = (ref self {}, bool? value, string message) {
186
186
  self.expect(value != null, message)
187
187
  }
188
188
 
189
- pub func assert = (ref self, char? value, string message) {
189
+ pub func assert = (ref self {}, char? value, string message) {
190
190
  self.expect(value != null, message)
191
191
  }
192
192
 
193
- pub func assert = (ref self, string? value, string message) {
193
+ pub func assert = (ref self {}, string? value, string message) {
194
194
  self.expect(value != null, message)
195
195
  }
196
196
 
@@ -212,7 +212,7 @@ pub mv struct Tester {
212
212
  * (default 1000, override with `bench_n`) and computes min / median /
213
213
  * mean / max / stddev over the per-iteration durations.
214
214
  **/
215
- pub func bench = (ref self, string label, func() body) {
215
+ pub func bench = (ref self { bench_label }, string label, func() body) {
216
216
  if self.has_failed {
217
217
  return
218
218
  }
@@ -226,7 +226,7 @@ pub mv struct Tester {
226
226
  * for slow functions (where the default 1000 would dominate the suite) or
227
227
  * for reproducible sample counts.
228
228
  **/
229
- pub func bench_n = (ref self, string label, func() body, int samples) {
229
+ pub func bench_n = (ref self { bench_label }, string label, func() body, int samples) {
230
230
  if self.has_failed {
231
231
  return
232
232
  }
@@ -244,7 +244,7 @@ pub mv struct Tester {
244
244
  * this is a no-op.
245
245
  **/
246
246
  pub func record_bench = (
247
- ref self,
247
+ ref self {},
248
248
  string label,
249
249
  int n,
250
250
  int min,
@@ -263,7 +263,7 @@ pub mv struct Tester {
263
263
  )
264
264
  }
265
265
 
266
- func record_failure = (ref self, string message) {
266
+ func record_failure = (ref self {}, string message) {
267
267
  self.failed += 1
268
268
  self.total_failed += 1
269
269
  self.has_failed = true
@@ -51,16 +51,16 @@ pub mv struct CharIndex {
51
51
  chars = chars + 1
52
52
  }
53
53
  } else {
54
- if open_at >= 0 {
55
- self.runs.push(CharRun(open_at, open_start, open_width, open_count))
56
- }
57
- open_at = pos
58
- open_start = chars
59
- open_width = w
60
- open_count = 1
61
- pos = pos + w
62
- chars = chars + 1
54
+ if open_at >= 0 {
55
+ self.runs.push(CharRun(open_at, open_start, open_width, open_count))
63
56
  }
57
+ open_at = pos
58
+ open_start = chars
59
+ open_width = w
60
+ open_count = 1
61
+ pos = pos + w
62
+ chars = chars + 1
63
+ }
64
64
  }
65
65
  if open_at >= 0 {
66
66
  self.runs.push(CharRun(open_at, open_start, open_width, open_count))
@@ -21,7 +21,7 @@ pub struct Chars {
21
21
 
22
22
  // The code point at the cursor, advancing past it. Malformed sequences
23
23
  // yield U+FFFD. Panics when exhausted — check has_next first.
24
- pub func next = (ref self, out int) {
24
+ pub func next = (ref self {}, out int) {
25
25
  if self.byte_pos >= self.source.length {
26
26
  panic("Chars exhausted; check has_next first")
27
27
  }
@@ -5,7 +5,7 @@
5
5
  // Integer (de)serialization is handled by int.to_string() and int.parse()
6
6
  // (see int.nm), so this struct only deals with JSON strings.
7
7
  //
8
- // `parse` / `stringify` use a JsonTree pool (flat Buffer-backed slot array)
8
+ // `parse` / `stringify` use a JsonTree pool (a flat StableList of nodes)
9
9
  // instead of per-node heap allocation, avoiding O(nodes) malloc/free per
10
10
  // parse cycle. The caller creates a JsonTree once and resets it per document.
11
11
 
@@ -41,8 +41,8 @@ pub struct Json {
41
41
  sb.append_string("\x5cr")
42
42
  }
43
43
  } else {
44
- sb.append_char(c)
45
- }
44
+ sb.append_char(c)
45
+ }
46
46
  }
47
47
  sb.append_char('"')
48
48
  return sb.to_string()
@@ -90,8 +90,8 @@ pub struct Json {
90
90
  sb.append_string("\x5c")
91
91
  }
92
92
  } else {
93
- sb.append_char(e)
94
- }
93
+ sb.append_char(e)
94
+ }
95
95
  }
96
96
  } else {
97
97
  sb.append_char(c)
@@ -102,7 +102,7 @@ pub struct Json {
102
102
 
103
103
  // Parse a JSON document into tree. The caller provides a JsonTree pool
104
104
  // which is reset and filled. Returns the index of the root node.
105
- pub func parse = (string s, ref JsonTree tree, out int) {
105
+ pub func parse = (string s, ref JsonTree tree { nodes }, out int) {
106
106
  tree.reset()
107
107
  var pos = Buffer<int>()
108
108
  pos.alloc_int(1)
@@ -112,14 +112,14 @@ pub struct Json {
112
112
  }
113
113
 
114
114
  // Serialize a node in the tree back to a JSON string.
115
- pub func stringify = (ref JsonTree tree, int root, out string) {
115
+ pub func stringify = (ref JsonTree tree { nodes }, int root, out string) {
116
116
  var sb = StringBuilder()
117
117
  json_ser_value(ref tree, root, ref sb)
118
118
  return sb.to_string()
119
119
  }
120
120
  }
121
121
 
122
- func json_skip_ws = (string s, ref Buffer<int> pos, int len) {
122
+ func json_skip_ws = (string s, ref Buffer<int> pos {}, int len) {
123
123
  while pos.load_int(0) < len {
124
124
  var int i = pos.load_int(0)
125
125
  var int code = s.at(i) as int
@@ -131,54 +131,69 @@ func json_skip_ws = (string s, ref Buffer<int> pos, int len) {
131
131
  }
132
132
  }
133
133
 
134
- func json_parse_string = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
134
+ func json_parse_string = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
135
135
  var int i = pos.load_int(0)
136
136
  i += 1
137
137
  var sb = StringBuilder()
138
- while i < len; i += 1 {
139
- var char c = s.at(i)
140
- var int code = c as int
141
- if code == 34 {
138
+ while i < len {
139
+ // Scan-to-next-escape fast path: copy the run of plain bytes in ONE
140
+ // bulk append instead of a per-byte append_char (each of which pays
141
+ // the builder's capacity check and length bump).
142
+ var int run_start = i
143
+ var int code = 0
144
+ while i < len {
145
+ code = s.at(i) as int
146
+ if code == 34 || code == 92 {
147
+ break
148
+ }
142
149
  i += 1
150
+ }
151
+ if i > run_start {
152
+ sb.append_string_view(s.slice(run_start, i))
153
+ }
154
+ if i >= len {
143
155
  break
144
156
  }
145
- if code == 92 {
157
+ if code == 34 {
146
158
  i += 1
147
- if i < len {
148
- var char e = s.at(i)
149
- var int ec = e as int
150
- switch {
151
- case ec == 110 {
152
- sb.append_string("\n")
153
- }
154
- case ec == 116 {
155
- sb.append_string("\t")
156
- }
157
- case ec == 114 {
158
- sb.append_string("\r")
159
- }
160
- case ec == 34 {
161
- sb.append_char('"')
162
- }
163
- case ec == 92 {
164
- sb.append_string("\x5c")
165
- }
166
- } else {
167
- sb.append_char(e)
168
- }
159
+ break
160
+ }
161
+ // code == 92 — escape sequence: consume the backslash, append the
162
+ // decoded byte (an unknown escape appends the byte verbatim).
163
+ i += 1
164
+ if i < len {
165
+ var char e = s.at(i)
166
+ var int ec = e as int
167
+ switch {
168
+ case ec == 110 {
169
+ sb.append_string("\n")
170
+ }
171
+ case ec == 116 {
172
+ sb.append_string("\t")
173
+ }
174
+ case ec == 114 {
175
+ sb.append_string("\r")
176
+ }
177
+ case ec == 34 {
178
+ sb.append_char('"')
179
+ }
180
+ case ec == 92 {
181
+ sb.append_string("\x5c")
182
+ }
183
+ } else {
184
+ sb.append_char(e)
169
185
  }
170
- } else {
171
- sb.append_char(c)
186
+ i += 1
172
187
  }
173
188
  }
174
189
  pos.store_int(0, i)
175
190
  var int n = tree.alloc_node()
176
191
  tree.set_kind(n, 3)
177
- tree.set_text(n, sb.to_string())
192
+ tree.take_text(n, ref sb)
178
193
  return n
179
194
  }
180
195
 
181
- func json_parse_number = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
196
+ func json_parse_number = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
182
197
  var int i = pos.load_int(0)
183
198
  var int start = i
184
199
  while i < len {
@@ -191,17 +206,16 @@ func json_parse_number = (string s, ref Buffer<int> pos, int len, ref JsonTree t
191
206
  }
192
207
  pos.store_int(0, i)
193
208
  var sb = StringBuilder()
194
- var int j = start
195
- while j < i; j += 1 {
196
- sb.append_char(s.at(j))
197
- }
209
+ // One bulk append for the whole run of number bytes (the scan above
210
+ // already found its end) — no per-byte append_char.
211
+ sb.append_string_view(s.slice(start, i))
198
212
  var int n = tree.alloc_node()
199
213
  tree.set_kind(n, 2)
200
- tree.set_text(n, sb.to_string())
214
+ tree.take_text(n, ref sb)
201
215
  return n
202
216
  }
203
217
 
204
- func json_parse_literal = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
218
+ func json_parse_literal = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
205
219
  var int i = pos.load_int(0)
206
220
  var int code = s.at(i) as int
207
221
  var int n = tree.alloc_node()
@@ -216,14 +230,14 @@ func json_parse_literal = (string s, ref Buffer<int> pos, int len, ref JsonTree
216
230
  adv = 5
217
231
  }
218
232
  } else {
219
- tree.set_kind(n, 0)
220
- adv = 4
221
- }
233
+ tree.set_kind(n, 0)
234
+ adv = 4
235
+ }
222
236
  pos.store_int(0, i + adv)
223
237
  return n
224
238
  }
225
239
 
226
- func json_parse_value = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
240
+ func json_parse_value = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
227
241
  json_skip_ws(s, ref pos, len)
228
242
  var int i = pos.load_int(0)
229
243
  if i >= len {
@@ -253,7 +267,7 @@ func json_parse_value = (string s, ref Buffer<int> pos, int len, ref JsonTree tr
253
267
  return json_parse_number(s, ref pos, len, ref tree)
254
268
  }
255
269
 
256
- func json_parse_elements = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
270
+ func json_parse_elements = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
257
271
  json_skip_ws(s, ref pos, len)
258
272
  var int i = pos.load_int(0)
259
273
  if i >= len {
@@ -275,7 +289,7 @@ func json_parse_elements = (string s, ref Buffer<int> pos, int len, ref JsonTree
275
289
  return elem
276
290
  }
277
291
 
278
- func json_parse_pairs = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
292
+ func json_parse_pairs = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
279
293
  json_skip_ws(s, ref pos, len)
280
294
  var int i = pos.load_int(0)
281
295
  if i >= len {
@@ -307,7 +321,7 @@ func json_parse_pairs = (string s, ref Buffer<int> pos, int len, ref JsonTree tr
307
321
  return key
308
322
  }
309
323
 
310
- func json_ser_list = (ref JsonTree tree, int first, ref StringBuilder sb) {
324
+ func json_ser_list = (ref JsonTree tree { nodes }, int first, ref StringBuilder sb {}) {
311
325
  json_ser_value(ref tree, first, ref sb)
312
326
  var int next = tree.get_next(first)
313
327
  if next >= 0 {
@@ -316,9 +330,9 @@ func json_ser_list = (ref JsonTree tree, int first, ref StringBuilder sb) {
316
330
  }
317
331
  }
318
332
 
319
- func json_ser_pairs = (ref JsonTree tree, int first, ref StringBuilder sb) {
333
+ func json_ser_pairs = (ref JsonTree tree { nodes }, int first, ref StringBuilder sb {}) {
320
334
  sb.append_char('"')
321
- sb.append_string(tree.get_text(first))
335
+ sb.append_string_view(tree.get_text_view(first))
322
336
  sb.append_string("\":")
323
337
  var int v = tree.get_val(first)
324
338
  if v < 0 {
@@ -333,7 +347,7 @@ func json_ser_pairs = (ref JsonTree tree, int first, ref StringBuilder sb) {
333
347
  }
334
348
  }
335
349
 
336
- func json_ser_value = (ref JsonTree tree, int n, ref StringBuilder sb) {
350
+ func json_ser_value = (ref JsonTree tree { nodes }, int n, ref StringBuilder sb {}) {
337
351
  var int k = tree.get_kind(n)
338
352
  if k == 0 {
339
353
  sb.append_string("null")
@@ -348,12 +362,12 @@ func json_ser_value = (ref JsonTree tree, int n, ref StringBuilder sb) {
348
362
  return
349
363
  }
350
364
  if k == 2 {
351
- sb.append_string(tree.get_text(n))
365
+ sb.append_string_view(tree.get_text_view(n))
352
366
  return
353
367
  }
354
368
  if k == 3 {
355
369
  sb.append_char('"')
356
- sb.append_string(tree.get_text(n))
370
+ sb.append_string_view(tree.get_text_view(n))
357
371
  sb.append_char('"')
358
372
  return
359
373
  }