@scriptc/cli-darwin-arm64 0.2.3 → 0.2.4
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/dist/THIRD_PARTY_NOTICES +1 -1
- package/dist/bin/scriptc +0 -0
- package/dist/bin/scriptc.json +1 -1
- package/dist/lib/llvm/bin/scriptc-llvm-codegen +0 -0
- package/dist/lib/llvm/package.json +1 -1
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/package.json +1 -1
- package/dist/lib/runtime-darwin-arm64/runtime-pack.json +129 -129
- package/dist/lib/runtime-sources/package.json +1 -1
- package/dist/lib/runtime-sources/src/scr_array.c +50 -34
- package/dist/lib/runtime-sources/src/scr_bigint.c +15 -0
- package/dist/lib/runtime-sources/src/scr_cycle.c +84 -60
- package/dist/lib/runtime-sources/src/scr_json.c +15 -11
- package/dist/lib/runtime-sources/src/scr_lib.c +45 -16
- package/dist/lib/runtime-sources/src/scr_map.c +228 -40
- package/dist/lib/runtime-sources/src/scr_regex.c +47 -8
- package/dist/lib/runtime-sources/src/scr_runtime.h +39 -13
- package/dist/lib/runtime-sources/src/scr_string.c +29 -0
- package/package.json +1 -1
|
@@ -59,6 +59,16 @@ static uint64_t scr_map_hash_str(const ScrStr *k) {
|
|
|
59
59
|
return scr_map_fnv1a((const unsigned char *)k->data, k->len);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/* Mix the complete normalized number or identity word. Float exponents
|
|
63
|
+
* and aligned pointers need high bits to reach the low bucket-index bits. */
|
|
64
|
+
static uint64_t scr_map_hash_word(uint64_t value) {
|
|
65
|
+
value ^= value >> 30;
|
|
66
|
+
value *= UINT64_C(0xbf58476d1ce4e5b9);
|
|
67
|
+
value ^= value >> 27;
|
|
68
|
+
value *= UINT64_C(0x94d049bb133111eb);
|
|
69
|
+
return value ^ (value >> 31);
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
/* ── slot packing (8-byte slots, like ScrArr) ──────────────────────────── */
|
|
63
73
|
|
|
64
74
|
static uint64_t scr_map_slot_from_f64(double v) {
|
|
@@ -98,12 +108,7 @@ static uint64_t scr_map_hash_dyn(const ScrDyn *d) {
|
|
|
98
108
|
case SCR_DYN_NULL: value = 0; break;
|
|
99
109
|
case SCR_DYN_BOOL: value = d->v.b; break;
|
|
100
110
|
case SCR_DYN_NUM: value = scr_map_f64_bits(d->v.num); break;
|
|
101
|
-
case SCR_DYN_BIGINT:
|
|
102
|
-
ScrStr *text = scr_bigint_to_string(d->v.bigint, 10);
|
|
103
|
-
uint64_t hash = scr_map_hash_str(text);
|
|
104
|
-
scr_str_release(text);
|
|
105
|
-
return hash;
|
|
106
|
-
}
|
|
111
|
+
case SCR_DYN_BIGINT: return scr_bigint_hash(d->v.bigint);
|
|
107
112
|
case SCR_DYN_STR: return scr_map_hash_str(d->v.str);
|
|
108
113
|
case SCR_DYN_SYMBOL: value = scr_map_slot_from_ptr(d->v.symbol.value); break;
|
|
109
114
|
case SCR_DYN_FUNC: value = scr_map_slot_from_ptr(d->v.fn.class_obj ? (void *)d->v.fn.class_obj : (void *)d->v.fn.clo); break;
|
|
@@ -122,7 +127,7 @@ static uint64_t scr_map_hash_dyn(const ScrDyn *d) {
|
|
|
122
127
|
case SCR_DYN_JSVAL: value = 0; break;
|
|
123
128
|
default: value = scr_map_slot_from_ptr((void *)d); break;
|
|
124
129
|
}
|
|
125
|
-
return
|
|
130
|
+
return scr_map_hash_word(value) ^ kind;
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
static bool scr_map_dyn_eq(const ScrDyn *a, const ScrDyn *b) {
|
|
@@ -135,16 +140,50 @@ static bool scr_map_dyn_eq(const ScrDyn *a, const ScrDyn *b) {
|
|
|
135
140
|
return scr_dyn_strict_eq(a, b);
|
|
136
141
|
}
|
|
137
142
|
|
|
143
|
+
static uint64_t scr_map_hash_union(const ScrMap *m, const ScrUnion *key) {
|
|
144
|
+
ScrMapKeyKind kind = (ScrMapKeyKind)m->union_keys[key->tag];
|
|
145
|
+
switch (kind) {
|
|
146
|
+
case SCR_MAP_KEY_STR: return scr_map_hash_str(scr_union_peek(key));
|
|
147
|
+
case SCR_MAP_KEY_BIGINT: return scr_bigint_hash(scr_union_peek(key));
|
|
148
|
+
case SCR_MAP_KEY_F64: return scr_map_hash_word(scr_map_f64_bits(scr_map_slot_to_f64(key->slot)));
|
|
149
|
+
case SCR_MAP_KEY_NULL:
|
|
150
|
+
case SCR_MAP_KEY_UNDEFINED: return scr_map_hash_word((uint64_t)kind);
|
|
151
|
+
default: return scr_map_hash_word(key->slot);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
static bool scr_map_union_eq(const ScrMap *m, const ScrUnion *a, const ScrUnion *b) {
|
|
156
|
+
ScrMapKeyKind kind = (ScrMapKeyKind)m->union_keys[a->tag];
|
|
157
|
+
if (kind != (ScrMapKeyKind)m->union_keys[b->tag]) return false;
|
|
158
|
+
switch (kind) {
|
|
159
|
+
case SCR_MAP_KEY_STR: return scr_str_eq(scr_union_peek(a), scr_union_peek(b));
|
|
160
|
+
case SCR_MAP_KEY_BIGINT: return scr_bigint_eq(scr_union_peek(a), scr_union_peek(b));
|
|
161
|
+
case SCR_MAP_KEY_F64:
|
|
162
|
+
return scr_map_f64_bits(scr_map_slot_to_f64(a->slot)) == scr_map_f64_bits(scr_map_slot_to_f64(b->slot));
|
|
163
|
+
case SCR_MAP_KEY_NULL:
|
|
164
|
+
case SCR_MAP_KEY_UNDEFINED: return true;
|
|
165
|
+
default: return a->slot == b->slot;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
138
169
|
static uint64_t scr_map_hash_key(const ScrMap *m, uint64_t key) {
|
|
139
170
|
if (m->key_kind == SCR_MAP_KEY_STR) return scr_map_hash_str((ScrStr *)scr_map_slot_to_ptr(key));
|
|
140
171
|
if (m->key_kind == SCR_MAP_KEY_DYN) return scr_map_hash_dyn((ScrDyn *)scr_map_slot_to_ptr(key));
|
|
172
|
+
if (m->key_kind == SCR_MAP_KEY_BIGINT) return scr_bigint_hash(scr_map_slot_to_ptr(key));
|
|
173
|
+
if (m->key_kind == SCR_MAP_KEY_UNION_VALUE) return scr_map_hash_union(m, scr_map_slot_to_ptr(key));
|
|
141
174
|
uint64_t identity = scr_map_identity(m, key);
|
|
142
|
-
return
|
|
175
|
+
return scr_map_hash_word(identity);
|
|
143
176
|
}
|
|
144
177
|
|
|
145
178
|
/* Stored keys are pre-normalized, so bit equality IS SameValueZero for f64
|
|
146
179
|
* keys (probe keys normalize through the same function). */
|
|
147
180
|
static bool scr_map_key_eq(const ScrMap *m, uint64_t stored, uint64_t probe) {
|
|
181
|
+
if (m->key_kind == SCR_MAP_KEY_UNION_VALUE) {
|
|
182
|
+
return scr_map_union_eq(m, scr_map_slot_to_ptr(stored), scr_map_slot_to_ptr(probe));
|
|
183
|
+
}
|
|
184
|
+
if (m->key_kind == SCR_MAP_KEY_BIGINT) {
|
|
185
|
+
return scr_bigint_eq(scr_map_slot_to_ptr(stored), scr_map_slot_to_ptr(probe));
|
|
186
|
+
}
|
|
148
187
|
if (m->key_kind == SCR_MAP_KEY_DYN) {
|
|
149
188
|
return scr_map_dyn_eq((ScrDyn *)scr_map_slot_to_ptr(stored), (ScrDyn *)scr_map_slot_to_ptr(probe));
|
|
150
189
|
}
|
|
@@ -156,18 +195,25 @@ static bool scr_map_key_eq(const ScrMap *m, uint64_t stored, uint64_t probe) {
|
|
|
156
195
|
}
|
|
157
196
|
|
|
158
197
|
/* ── lookup ────────────────────────────────────────────────────────────
|
|
159
|
-
* Returns the
|
|
160
|
-
*
|
|
161
|
-
|
|
198
|
+
* Returns the matching bucket, or the empty bucket where a new key belongs.
|
|
199
|
+
* A zero entry hash marks a tombstone; cached hashes reject unrelated keys
|
|
200
|
+
* before equality and survive table growth without hashing the keys again.
|
|
201
|
+
* Remap zero to one so every live entry has a nonzero hash. */
|
|
202
|
+
static size_t scr_map_probe(const ScrMap *m, uint64_t hash, uint64_t key) {
|
|
162
203
|
if (m->nbuckets == 0) return SCR_MAP_EMPTY;
|
|
163
204
|
size_t mask = m->nbuckets - 1;
|
|
164
205
|
for (size_t i = hash & mask;; i = (i + 1) & mask) {
|
|
165
206
|
size_t b = m->buckets[i];
|
|
166
|
-
if (b == SCR_MAP_EMPTY) return
|
|
167
|
-
if (m->entries[b].
|
|
207
|
+
if (b == SCR_MAP_EMPTY) return i;
|
|
208
|
+
if (m->entries[b].hash == hash && scr_map_key_eq(m, m->entries[b].key, key)) return i;
|
|
168
209
|
}
|
|
169
210
|
}
|
|
170
211
|
|
|
212
|
+
static size_t scr_map_find(const ScrMap *m, uint64_t hash, uint64_t key) {
|
|
213
|
+
size_t slot = scr_map_probe(m, hash ? hash : 1, key);
|
|
214
|
+
return slot == SCR_MAP_EMPTY ? SCR_MAP_EMPTY : m->buckets[slot];
|
|
215
|
+
}
|
|
216
|
+
|
|
171
217
|
/* Rebuild the bucket table (size must be a power of two >= 2 * nentries):
|
|
172
218
|
* only live entries are inserted, in dense order — dead markers vanish. */
|
|
173
219
|
static void scr_map_rebuild_buckets(ScrMap *m, size_t nbuckets) {
|
|
@@ -179,8 +225,8 @@ static void scr_map_rebuild_buckets(ScrMap *m, size_t nbuckets) {
|
|
|
179
225
|
m->nbuckets = nbuckets;
|
|
180
226
|
size_t mask = nbuckets - 1;
|
|
181
227
|
for (size_t e = 0; e < m->nentries; e++) {
|
|
182
|
-
|
|
183
|
-
|
|
228
|
+
uint64_t hash = m->entries[e].hash;
|
|
229
|
+
if (!hash) continue;
|
|
184
230
|
size_t i = hash & mask;
|
|
185
231
|
while (buckets[i] != SCR_MAP_EMPTY) i = (i + 1) & mask;
|
|
186
232
|
buckets[i] = e;
|
|
@@ -192,7 +238,7 @@ static void scr_map_rebuild_buckets(ScrMap *m, size_t nbuckets) {
|
|
|
192
238
|
static void scr_map_compact(ScrMap *m) {
|
|
193
239
|
size_t w = 0;
|
|
194
240
|
for (size_t r = 0; r < m->nentries; r++) {
|
|
195
|
-
if (m->entries[r].
|
|
241
|
+
if (m->entries[r].hash) m->entries[w++] = m->entries[r];
|
|
196
242
|
}
|
|
197
243
|
m->nentries = w;
|
|
198
244
|
if (m->nbuckets > 0) scr_map_rebuild_buckets(m, m->nbuckets);
|
|
@@ -229,10 +275,15 @@ static void scr_map_reserve_append(ScrMap *m) {
|
|
|
229
275
|
|
|
230
276
|
/* ── entry release helpers ─────────────────────────────────────────────── */
|
|
231
277
|
|
|
278
|
+
static bool scr_map_ref_key(ScrMapKeyKind kind) {
|
|
279
|
+
return kind == SCR_MAP_KEY_REF || kind == SCR_MAP_KEY_UNION_REF || kind == SCR_MAP_KEY_DYN ||
|
|
280
|
+
kind == SCR_MAP_KEY_BIGINT || kind == SCR_MAP_KEY_UNION_VALUE;
|
|
281
|
+
}
|
|
282
|
+
|
|
232
283
|
static void scr_map_release_key(ScrMap *m, uint64_t key) {
|
|
233
284
|
if (m->key_kind == SCR_MAP_KEY_STR) {
|
|
234
285
|
scr_str_release((ScrStr *)scr_map_slot_to_ptr(key));
|
|
235
|
-
} else if (m->key_kind
|
|
286
|
+
} else if (scr_map_ref_key(m->key_kind)) {
|
|
236
287
|
m->key_release(scr_map_slot_to_ptr(key));
|
|
237
288
|
}
|
|
238
289
|
}
|
|
@@ -243,10 +294,14 @@ static void scr_map_release_val(ScrMap *m, uint64_t val) {
|
|
|
243
294
|
|
|
244
295
|
/* ── lifecycle ─────────────────────────────────────────────────────────── */
|
|
245
296
|
|
|
297
|
+
void scr_map_union_keys(ScrMap *map, const uint8_t *kinds) {
|
|
298
|
+
map->union_keys = kinds;
|
|
299
|
+
}
|
|
300
|
+
|
|
246
301
|
static void scr_map_trace(void *o, ScrTraceVisit visit, void *ctx) {
|
|
247
302
|
ScrMap *m = (ScrMap *)o;
|
|
248
303
|
for (size_t e = 0; e < m->nentries; e++) {
|
|
249
|
-
if (!m->entries[e].
|
|
304
|
+
if (!m->entries[e].hash) continue;
|
|
250
305
|
if (m->key_trace) visit(scr_map_slot_to_ptr(m->entries[e].key), ctx);
|
|
251
306
|
if (m->val_trace) visit(scr_map_slot_to_ptr(m->entries[e].val), ctx);
|
|
252
307
|
}
|
|
@@ -258,7 +313,7 @@ static void scr_map_gcfree(void *o) {
|
|
|
258
313
|
ScrMap *m = (ScrMap *)o;
|
|
259
314
|
scr_weak_dispose(m);
|
|
260
315
|
for (size_t e = 0; e < m->nentries; e++) {
|
|
261
|
-
if (!m->entries[e].
|
|
316
|
+
if (!m->entries[e].hash) continue;
|
|
262
317
|
if (!m->key_trace) scr_map_release_key(m, m->entries[e].key);
|
|
263
318
|
if (!m->val_trace) scr_map_release_val(m, m->entries[e].val);
|
|
264
319
|
}
|
|
@@ -318,7 +373,7 @@ void scr_map_release(ScrMap *m) {
|
|
|
318
373
|
scr_weak_dispose(m);
|
|
319
374
|
if (m->key_trace || m->val_trace) scr_cyc_on_dead(m);
|
|
320
375
|
for (size_t e = 0; e < m->nentries; e++) {
|
|
321
|
-
if (!m->entries[e].
|
|
376
|
+
if (!m->entries[e].hash) continue;
|
|
322
377
|
scr_map_release_key(m, m->entries[e].key);
|
|
323
378
|
scr_map_release_val(m, m->entries[e].val);
|
|
324
379
|
}
|
|
@@ -344,8 +399,8 @@ double scr_map_size(const ScrMap *m) { return (double)m->nlive; }
|
|
|
344
399
|
|
|
345
400
|
void scr_map_clear(ScrMap *m) {
|
|
346
401
|
for (size_t e = 0; e < m->nentries; e++) {
|
|
347
|
-
if (!m->entries[e].
|
|
348
|
-
m->entries[e].
|
|
402
|
+
if (!m->entries[e].hash) continue;
|
|
403
|
+
m->entries[e].hash = 0;
|
|
349
404
|
scr_map_release_key(m, m->entries[e].key);
|
|
350
405
|
scr_map_release_val(m, m->entries[e].val);
|
|
351
406
|
}
|
|
@@ -363,7 +418,7 @@ void scr_map_clear(ScrMap *m) {
|
|
|
363
418
|
|
|
364
419
|
bool scr_map_has_f64(const ScrMap *m, double key) {
|
|
365
420
|
uint64_t k = scr_map_f64_bits(key);
|
|
366
|
-
return scr_map_find(m,
|
|
421
|
+
return scr_map_find(m, scr_map_hash_word(k), k) != SCR_MAP_EMPTY;
|
|
367
422
|
}
|
|
368
423
|
|
|
369
424
|
bool scr_map_has_str(const ScrMap *m, const ScrStr *key) {
|
|
@@ -373,7 +428,7 @@ bool scr_map_has_str(const ScrMap *m, const ScrStr *key) {
|
|
|
373
428
|
|
|
374
429
|
static bool scr_map_delete_found(ScrMap *m, size_t e) {
|
|
375
430
|
if (e == SCR_MAP_EMPTY) return false;
|
|
376
|
-
m->entries[e].
|
|
431
|
+
m->entries[e].hash = 0; /* bucket slot stays: probe chains intact */
|
|
377
432
|
m->nlive--;
|
|
378
433
|
scr_map_release_key(m, m->entries[e].key);
|
|
379
434
|
scr_map_release_val(m, m->entries[e].val);
|
|
@@ -382,7 +437,7 @@ static bool scr_map_delete_found(ScrMap *m, size_t e) {
|
|
|
382
437
|
|
|
383
438
|
bool scr_map_delete_f64(ScrMap *m, double key) {
|
|
384
439
|
uint64_t k = scr_map_f64_bits(key);
|
|
385
|
-
return scr_map_delete_found(m, scr_map_find(m,
|
|
440
|
+
return scr_map_delete_found(m, scr_map_find(m, scr_map_hash_word(k), k));
|
|
386
441
|
}
|
|
387
442
|
|
|
388
443
|
bool scr_map_delete_str(ScrMap *m, const ScrStr *key) {
|
|
@@ -412,18 +467,28 @@ bool scr_map_delete_ref(ScrMap *m, const void *key) {
|
|
|
412
467
|
* insertion slot. */
|
|
413
468
|
|
|
414
469
|
static void scr_map_set(ScrMap *m, uint64_t hash, uint64_t key, uint64_t val) {
|
|
415
|
-
|
|
470
|
+
hash = hash ? hash : 1;
|
|
471
|
+
size_t slot = scr_map_probe(m, hash, key);
|
|
472
|
+
size_t e = slot == SCR_MAP_EMPTY ? SCR_MAP_EMPTY : m->buckets[slot];
|
|
416
473
|
if (e != SCR_MAP_EMPTY) {
|
|
417
474
|
uint64_t old = m->entries[e].val;
|
|
418
475
|
m->entries[e].val = val; /* unlink before releasing (cycle collector) */
|
|
419
476
|
scr_map_release_val(m, old);
|
|
420
477
|
return;
|
|
421
478
|
}
|
|
479
|
+
size_t old_entries = m->nentries, old_buckets = m->nbuckets;
|
|
422
480
|
scr_map_reserve_append(m);
|
|
481
|
+
/* Growth or compaction can move the insertion bucket. Otherwise keep the
|
|
482
|
+
* empty slot from the first probe instead of walking the chain twice. */
|
|
483
|
+
if (m->nentries != old_entries || m->nbuckets != old_buckets) {
|
|
484
|
+
size_t mask = m->nbuckets - 1;
|
|
485
|
+
slot = hash & mask;
|
|
486
|
+
while (m->buckets[slot] != SCR_MAP_EMPTY) slot = (slot + 1) & mask;
|
|
487
|
+
}
|
|
423
488
|
size_t idx = m->nentries++;
|
|
424
489
|
m->entries[idx].key = key;
|
|
425
490
|
m->entries[idx].val = val;
|
|
426
|
-
m->entries[idx].
|
|
491
|
+
m->entries[idx].hash = hash;
|
|
427
492
|
m->nlive++;
|
|
428
493
|
if (m->key_kind == SCR_MAP_KEY_STR) {
|
|
429
494
|
scr_str_retain((ScrStr *)scr_map_slot_to_ptr(key)); /* key is borrowed */
|
|
@@ -436,18 +501,21 @@ static void scr_map_set(ScrMap *m, uint64_t hash, uint64_t key, uint64_t val) {
|
|
|
436
501
|
} else {
|
|
437
502
|
m->key_retain(scr_map_slot_to_ptr(key));
|
|
438
503
|
}
|
|
439
|
-
} else if (m->key_kind ==
|
|
504
|
+
} else if (m->key_kind == SCR_MAP_KEY_UNION_VALUE &&
|
|
505
|
+
m->union_keys[((ScrUnion *)scr_map_slot_to_ptr(key))->tag] == SCR_MAP_KEY_F64 &&
|
|
506
|
+
((ScrUnion *)scr_map_slot_to_ptr(key))->slot == UINT64_C(0x8000000000000000)) {
|
|
507
|
+
/* Normalize stored -0 without mutating the caller's immutable box. */
|
|
508
|
+
ScrUnion *u = scr_map_slot_to_ptr(key);
|
|
509
|
+
m->entries[idx].key = scr_map_slot_from_ptr(scr_union_new_f64(u->tag, 0));
|
|
510
|
+
} else if (scr_map_ref_key(m->key_kind)) {
|
|
440
511
|
m->key_retain(scr_map_slot_to_ptr(key)); /* key is borrowed */
|
|
441
512
|
}
|
|
442
|
-
|
|
443
|
-
size_t i = hash & mask;
|
|
444
|
-
while (m->buckets[i] != SCR_MAP_EMPTY) i = (i + 1) & mask;
|
|
445
|
-
m->buckets[i] = idx;
|
|
513
|
+
m->buckets[slot] = idx;
|
|
446
514
|
}
|
|
447
515
|
|
|
448
516
|
static void scr_map_set_f64_key(ScrMap *m, double key, uint64_t val) {
|
|
449
517
|
uint64_t k = scr_map_f64_bits(key); /* stores +0 for -0, canonical NaN */
|
|
450
|
-
scr_map_set(m,
|
|
518
|
+
scr_map_set(m, scr_map_hash_word(k), k, val);
|
|
451
519
|
}
|
|
452
520
|
|
|
453
521
|
static void scr_map_set_str_key(ScrMap *m, ScrStr *key, uint64_t val) {
|
|
@@ -501,7 +569,7 @@ ScrMap *scr_set_new_ref(void *(*elem_retain)(void *), void (*elem_release)(void
|
|
|
501
569
|
|
|
502
570
|
static size_t scr_map_find_f64(const ScrMap *m, double key) {
|
|
503
571
|
uint64_t k = scr_map_f64_bits(key);
|
|
504
|
-
return scr_map_find(m,
|
|
572
|
+
return scr_map_find(m, scr_map_hash_word(k), k);
|
|
505
573
|
}
|
|
506
574
|
|
|
507
575
|
static size_t scr_map_find_str(const ScrMap *m, const ScrStr *key) {
|
|
@@ -568,17 +636,63 @@ void *scr_map_get_ref_ref(const ScrMap *m, const void *key) {
|
|
|
568
636
|
return m->val_retain(scr_map_slot_to_ptr(m->entries[e].val));
|
|
569
637
|
}
|
|
570
638
|
|
|
639
|
+
/* Boolean keys use unboxed 0/1 slots and the same probe path. */
|
|
640
|
+
static size_t scr_map_find_bool(const ScrMap *m, bool key) {
|
|
641
|
+
uint64_t slot = key ? 1 : 0;
|
|
642
|
+
return scr_map_find(m, scr_map_hash_word(slot), slot);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
bool scr_map_has_bool(const ScrMap *m, bool key) {
|
|
646
|
+
return scr_map_find_bool(m, key) != SCR_MAP_EMPTY;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
bool scr_map_delete_bool(ScrMap *m, bool key) {
|
|
650
|
+
return scr_map_delete_found(m, scr_map_find_bool(m, key));
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
void scr_map_set_bool_f64(ScrMap *m, bool key, double value) {
|
|
654
|
+
scr_map_set(m, scr_map_hash_word(key ? 1 : 0), key ? 1 : 0, scr_map_slot_from_f64(value));
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
void scr_map_set_bool_bool(ScrMap *m, bool key, bool value) {
|
|
658
|
+
scr_map_set(m, scr_map_hash_word(key ? 1 : 0), key ? 1 : 0, value ? 1 : 0);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
void scr_map_set_bool_ref(ScrMap *m, bool key, void *value) {
|
|
662
|
+
scr_map_set(m, scr_map_hash_word(key ? 1 : 0), key ? 1 : 0, scr_map_slot_from_ptr(value));
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
bool scr_map_get_bool_f64(const ScrMap *m, bool key, double *out) {
|
|
666
|
+
size_t entry = scr_map_find_bool(m, key);
|
|
667
|
+
if (entry == SCR_MAP_EMPTY) return false;
|
|
668
|
+
*out = scr_map_slot_to_f64(m->entries[entry].val);
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
bool scr_map_get_bool_bool(const ScrMap *m, bool key, bool *out) {
|
|
673
|
+
size_t entry = scr_map_find_bool(m, key);
|
|
674
|
+
if (entry == SCR_MAP_EMPTY) return false;
|
|
675
|
+
*out = m->entries[entry].val != 0;
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
void *scr_map_get_bool_ref(const ScrMap *m, bool key) {
|
|
680
|
+
size_t entry = scr_map_find_bool(m, key);
|
|
681
|
+
if (entry == SCR_MAP_EMPTY) return NULL;
|
|
682
|
+
return m->val_retain(scr_map_slot_to_ptr(m->entries[entry].val));
|
|
683
|
+
}
|
|
684
|
+
|
|
571
685
|
/* ── iteration primitives (the forEach desugar) ────────────────────────── */
|
|
572
686
|
|
|
573
687
|
double scr_map_iter_count(const ScrMap *m) { return (double)m->nentries; }
|
|
574
688
|
|
|
575
689
|
bool scr_map_iter_live(const ScrMap *m, double i) {
|
|
576
690
|
if (!(i >= 0) || i >= (double)m->nentries) return false;
|
|
577
|
-
return m->entries[(size_t)i].
|
|
691
|
+
return m->entries[(size_t)i].hash;
|
|
578
692
|
}
|
|
579
693
|
|
|
580
694
|
static const ScrMapEntry *scr_map_iter_at(const ScrMap *m, double i) {
|
|
581
|
-
if (!(i >= 0) || i >= (double)m->nentries || !m->entries[(size_t)i].
|
|
695
|
+
if (!(i >= 0) || i >= (double)m->nentries || !m->entries[(size_t)i].hash) {
|
|
582
696
|
scr_trap("scriptc: internal error: map iteration index out of range\n");
|
|
583
697
|
}
|
|
584
698
|
return &m->entries[(size_t)i];
|
|
@@ -588,6 +702,10 @@ double scr_map_iter_key_f64(const ScrMap *m, double i) {
|
|
|
588
702
|
return scr_map_slot_to_f64(scr_map_iter_at(m, i)->key);
|
|
589
703
|
}
|
|
590
704
|
|
|
705
|
+
bool scr_map_iter_key_bool(const ScrMap *m, double i) {
|
|
706
|
+
return scr_map_iter_at(m, i)->key != 0;
|
|
707
|
+
}
|
|
708
|
+
|
|
591
709
|
ScrStr *scr_map_iter_key_str(const ScrMap *m, double i) {
|
|
592
710
|
return scr_str_retain((ScrStr *)scr_map_slot_to_ptr(scr_map_iter_at(m, i)->key));
|
|
593
711
|
}
|
|
@@ -649,6 +767,67 @@ void scr_map_dyn_set(ScrMap *map, const ScrDyn *key, const ScrDyn *value, bool s
|
|
|
649
767
|
scr_map_release_key(map, key_slot);
|
|
650
768
|
}
|
|
651
769
|
|
|
770
|
+
/* Copy only live entries. The source is borrowed, contains unique keys,
|
|
771
|
+
* and runs no user code while traversed, so cached hashes remain valid. */
|
|
772
|
+
ScrMap *scr_map_clone(const ScrMap *source, bool keys_only) {
|
|
773
|
+
ScrMap *out = scr_map_new_typed(source->key_kind, keys_only ? SCR_MAP_VAL_F64 : source->val_kind,
|
|
774
|
+
source->key_retain, source->key_release, source->key_trace,
|
|
775
|
+
keys_only ? NULL : source->val_retain, keys_only ? NULL : source->val_release,
|
|
776
|
+
keys_only ? NULL : source->val_trace);
|
|
777
|
+
out->union_keys = source->union_keys;
|
|
778
|
+
size_t count = source->nlive;
|
|
779
|
+
if (!count) return out;
|
|
780
|
+
if (count > SIZE_MAX / sizeof(ScrMapEntry) || count > SIZE_MAX / 2 / sizeof(size_t)) scr_map_oom();
|
|
781
|
+
out->entries = malloc(count * sizeof(ScrMapEntry));
|
|
782
|
+
if (!out->entries) scr_map_oom();
|
|
783
|
+
out->ecap = count;
|
|
784
|
+
for (size_t i = 0; i < source->nentries; i++) {
|
|
785
|
+
const ScrMapEntry *entry = &source->entries[i];
|
|
786
|
+
if (!entry->hash) continue;
|
|
787
|
+
ScrMapEntry *copy = &out->entries[out->nentries++];
|
|
788
|
+
*copy = *entry;
|
|
789
|
+
if (out->key_kind == SCR_MAP_KEY_STR) scr_str_retain(scr_map_slot_to_ptr(copy->key));
|
|
790
|
+
else if (scr_map_ref_key(out->key_kind)) out->key_retain(scr_map_slot_to_ptr(copy->key));
|
|
791
|
+
if (keys_only) copy->val = 0;
|
|
792
|
+
else if (out->val_kind == SCR_MAP_VAL_REF) out->val_retain(scr_map_slot_to_ptr(copy->val));
|
|
793
|
+
}
|
|
794
|
+
out->nlive = count;
|
|
795
|
+
size_t buckets = 16;
|
|
796
|
+
while (buckets < 2 * count) {
|
|
797
|
+
if (buckets > SIZE_MAX / 2 / sizeof(size_t)) scr_map_oom();
|
|
798
|
+
buckets *= 2;
|
|
799
|
+
}
|
|
800
|
+
scr_map_rebuild_buckets(out, buckets);
|
|
801
|
+
return out;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/* A generic view can be backed by typed slots. Its copy must accept new
|
|
805
|
+
* JavaScript values independently of that backing's narrower adapters. */
|
|
806
|
+
ScrMap *scr_map_clone_dyn(const ScrMap *source, bool keys_only) {
|
|
807
|
+
ScrMap *out = scr_map_new_typed(SCR_MAP_KEY_DYN, keys_only ? SCR_MAP_VAL_F64 : SCR_MAP_VAL_REF,
|
|
808
|
+
scr_dyn_retain_v, scr_dyn_release_v, scr_dyn_trace_v,
|
|
809
|
+
keys_only ? NULL : scr_dyn_retain_v, keys_only ? NULL : scr_dyn_release_v,
|
|
810
|
+
keys_only ? NULL : scr_dyn_trace_v);
|
|
811
|
+
for (size_t i = 0; i < source->nentries; i++) {
|
|
812
|
+
if (!source->entries[i].hash) continue;
|
|
813
|
+
ScrDyn *key = scr_map_dyn_key(source, (double)i);
|
|
814
|
+
uint64_t value = keys_only ? 0 : scr_map_slot_from_ptr(scr_map_dyn_value(source, (double)i));
|
|
815
|
+
scr_map_set(out, scr_map_hash_dyn(key), scr_map_slot_from_ptr(key), value);
|
|
816
|
+
scr_dyn_release(key);
|
|
817
|
+
}
|
|
818
|
+
return out;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
void scr_map_values_into(ScrMap *set, const ScrMap *source, bool checked) {
|
|
822
|
+
for (size_t i = 0; i < source->nentries; i++) {
|
|
823
|
+
if (!source->entries[i].hash) continue;
|
|
824
|
+
uint64_t value = checked ? scr_map_slot_from_ptr(scr_map_dyn_value(source, (double)i)) : source->entries[i].val;
|
|
825
|
+
if (set->key_kind == SCR_MAP_KEY_F64) value = scr_map_f64_bits(scr_map_slot_to_f64(value));
|
|
826
|
+
scr_map_set(set, scr_map_hash_key(set, value), value, 0);
|
|
827
|
+
if (checked) scr_dyn_release(scr_map_slot_to_ptr(value));
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
652
831
|
ScrArr *scr_set_to_arr_dyn(const ScrMap *map) {
|
|
653
832
|
ScrArr *out = scr_arr_new_ref(scr_dyn_retain_v, scr_dyn_release_v, scr_dyn_trace_v, map->nlive);
|
|
654
833
|
for (size_t i = 0; i < map->nentries; i++) {
|
|
@@ -683,14 +862,23 @@ void scr_map_iter_exit(ScrMap *m) {
|
|
|
683
862
|
void scr_set_add_all(ScrMap *set, ScrArr *values) {
|
|
684
863
|
size_t n = values->len;
|
|
685
864
|
for (size_t i = 0; i < n; i++) {
|
|
686
|
-
|
|
865
|
+
/* Packed values stay owned by the seed array throughout construction.
|
|
866
|
+
* Avoid taking a temporary owner only to release it after insertion.
|
|
867
|
+
* Sparse/missing positions retain the existing accessor semantics. */
|
|
868
|
+
if (i < values->cap && values->present[i] == SCR_ARR_VALUE) {
|
|
869
|
+
uint64_t key = values->data[i];
|
|
870
|
+
if (set->key_kind == SCR_MAP_KEY_F64) key = scr_map_f64_bits(scr_map_slot_to_f64(key));
|
|
871
|
+
scr_map_set(set, scr_map_hash_key(set, key), key, 0);
|
|
872
|
+
} else if (values->elem == SCR_ELEM_STR) {
|
|
687
873
|
ScrStr *s = (ScrStr *)scr_arr_get_ref(values, (double)i); /* +1 */
|
|
688
874
|
scr_map_set_str_f64(set, s, 0); /* borrows; retains stored copy */
|
|
689
875
|
scr_str_release(s);
|
|
690
|
-
} else if (set->key_kind
|
|
876
|
+
} else if (scr_map_ref_key(set->key_kind)) {
|
|
691
877
|
void *p = scr_arr_get_ref(values, (double)i); /* +1 */
|
|
692
878
|
scr_map_set_ref_f64(set, p, 0); /* borrows; retains stored copy */
|
|
693
879
|
set->key_release(p);
|
|
880
|
+
} else if (set->key_kind == SCR_MAP_KEY_BOOL) {
|
|
881
|
+
scr_map_set_bool_f64(set, scr_arr_get_bool(values, (double)i), 0);
|
|
694
882
|
} else {
|
|
695
883
|
scr_map_set_f64_f64(set, scr_arr_get_f64(values, (double)i), 0);
|
|
696
884
|
}
|
|
@@ -729,7 +917,7 @@ ScrArr *scr_map_keys_js_order(const ScrMap *m) {
|
|
|
729
917
|
size_t nidx = 0;
|
|
730
918
|
struct { uint32_t v; ScrStr *k; } *idx = NULL;
|
|
731
919
|
for (size_t i = 0; i < n; i++) {
|
|
732
|
-
if (!m->entries[i].
|
|
920
|
+
if (!m->entries[i].hash) continue;
|
|
733
921
|
ScrStr *k = (ScrStr *)scr_map_slot_to_ptr(m->entries[i].key);
|
|
734
922
|
uint32_t v;
|
|
735
923
|
if (!scr_map_key_array_index(k, &v)) continue;
|
|
@@ -750,7 +938,7 @@ ScrArr *scr_map_keys_js_order(const ScrMap *m) {
|
|
|
750
938
|
}
|
|
751
939
|
free(idx);
|
|
752
940
|
for (size_t i = 0; i < n; i++) {
|
|
753
|
-
if (!m->entries[i].
|
|
941
|
+
if (!m->entries[i].hash) continue;
|
|
754
942
|
ScrStr *k = (ScrStr *)scr_map_slot_to_ptr(m->entries[i].key);
|
|
755
943
|
uint32_t v;
|
|
756
944
|
if (scr_map_key_array_index(k, &v)) continue;
|
|
@@ -420,13 +420,6 @@ void scr_regex_reset_last_index(ScrRegex *re, double index) {
|
|
|
420
420
|
re->last_index = index;
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
-
bool scr_regex_test(ScrRegex *re, ScrStr *s) {
|
|
424
|
-
ScrArr *result = scr_regex_exec(s, re);
|
|
425
|
-
bool matched = result != NULL;
|
|
426
|
-
scr_arr_release(result);
|
|
427
|
-
return matched;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
423
|
static int scr_regex_start(ScrRegex *re, int len) {
|
|
431
424
|
double index = re->last_index;
|
|
432
425
|
if (isnan(index) || index <= 0) return 0;
|
|
@@ -434,6 +427,43 @@ static int scr_regex_start(ScrRegex *re, int len) {
|
|
|
434
427
|
return (int)floor(index);
|
|
435
428
|
}
|
|
436
429
|
|
|
430
|
+
/* A predicate needs only the match boundary, not the captured strings or
|
|
431
|
+
* result metadata. Short ASCII subjects already have the engine's one-byte
|
|
432
|
+
* representation. Bound that scan so a long mixed-text subject does not
|
|
433
|
+
* traverse an ASCII prefix twice before matching. Other subjects use exec's
|
|
434
|
+
* UTF-16 conversion. Register storage is bounded by alloc_count, not captures. */
|
|
435
|
+
bool scr_regex_test(ScrRegex *re, ScrStr *s) {
|
|
436
|
+
uint8_t *bc = scr_regex_bc(re);
|
|
437
|
+
bool ascii = s->len <= 256;
|
|
438
|
+
size_t i = 0;
|
|
439
|
+
for (; ascii && s->len - i >= sizeof(uint64_t); i += sizeof(uint64_t)) {
|
|
440
|
+
uint64_t word;
|
|
441
|
+
memcpy(&word, s->data + i, sizeof word);
|
|
442
|
+
if (word & UINT64_C(0x8080808080808080)) ascii = false;
|
|
443
|
+
}
|
|
444
|
+
for (; ascii && i < s->len; i++) {
|
|
445
|
+
if ((unsigned char)s->data[i] >= 0x80) ascii = false;
|
|
446
|
+
}
|
|
447
|
+
int len;
|
|
448
|
+
uint16_t *converted = ascii ? NULL : scr_to_utf16(s, &len);
|
|
449
|
+
const uint8_t *subject = ascii ? (const uint8_t *)s->data : (const uint8_t *)converted;
|
|
450
|
+
if (ascii) len = (int)s->len;
|
|
451
|
+
uint8_t *registers[32];
|
|
452
|
+
uint8_t **capture = lre_get_alloc_count(bc) <= 32 ? registers : scr_capture_alloc(bc);
|
|
453
|
+
bool stateful = (lre_get_flags(bc) & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) != 0;
|
|
454
|
+
int pos = stateful ? scr_regex_start(re, len) : 0;
|
|
455
|
+
int rc = pos < 0 ? 0 : lre_exec(capture, bc, subject, pos, len, ascii ? 0 : 1, lre_opaque());
|
|
456
|
+
if (rc < 0) {
|
|
457
|
+
fflush(stdout);
|
|
458
|
+
scr_trap("scriptc: regular expression execution failed\n");
|
|
459
|
+
}
|
|
460
|
+
if (stateful) re->last_index = rc == 1
|
|
461
|
+
? (double)((capture[1] - subject) >> (ascii ? 0 : 1)) : 0;
|
|
462
|
+
if (capture != registers) free(capture);
|
|
463
|
+
free(converted);
|
|
464
|
+
return rc == 1;
|
|
465
|
+
}
|
|
466
|
+
|
|
437
467
|
/* ── match ────────────────────────────────────────────────────────────── */
|
|
438
468
|
|
|
439
469
|
/* re.exec(s): a fresh string[] of [whole match, ...captures], or NULL
|
|
@@ -1170,7 +1200,16 @@ ScrRegex *scr_regex_new(ScrStr *pattern, ScrStr *flags) {
|
|
|
1170
1200
|
}
|
|
1171
1201
|
re->rc = 1;
|
|
1172
1202
|
re->source = pattern->len > 0 ? scr_str_retain(pattern) : scr_str_new("(?:)", 4);
|
|
1173
|
-
|
|
1203
|
+
/* Public flags use getter order, independently of constructor spelling.
|
|
1204
|
+
* Validate first so duplicates and invalid flags retain their errors. */
|
|
1205
|
+
char canonical[6];
|
|
1206
|
+
size_t flag_count = 0;
|
|
1207
|
+
const char *order = "gimsuy";
|
|
1208
|
+
for (unsigned i = 0; i < 6; i++) {
|
|
1209
|
+
if (seen_flags & (1u << i)) canonical[flag_count++] = order[i];
|
|
1210
|
+
}
|
|
1211
|
+
re->flags = flag_count == flags->len && memcmp(canonical, flags->data, flag_count) == 0
|
|
1212
|
+
? scr_str_retain(flags) : scr_str_new(canonical, flag_count);
|
|
1174
1213
|
/* Eager compile — the literal path stays lazy (its failure is an
|
|
1175
1214
|
* abort; tsc already parsed those patterns). */
|
|
1176
1215
|
int lre_flags = scr_lre_flags(re->flags);
|