xbintsc 0.3.79 → 0.3.87
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/README.md +13 -5
- package/README.zh-CN.md +13 -6
- package/dist/src/ast/common.d.ts +23 -0
- package/dist/src/cli/main.js +68 -24
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +5 -1
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +50 -0
- package/dist/src/codegen/generator/context.js +124 -13
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/debug.d.ts +33 -0
- package/dist/src/codegen/generator/debug.js +97 -0
- package/dist/src/codegen/generator/debug.js.map +1 -0
- package/dist/src/codegen/generator/expressions/assignment.js +9 -5
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/operators.js +35 -9
- package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.js +5 -2
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.d.ts +3 -1
- package/dist/src/codegen/generator/generator.js +3 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +1 -0
- package/dist/src/codegen/generator/module.js +76 -6
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/numeric.d.ts +26 -0
- package/dist/src/codegen/generator/numeric.js +127 -0
- package/dist/src/codegen/generator/numeric.js.map +1 -0
- package/dist/src/codegen/generator/state.d.ts +17 -0
- package/dist/src/codegen/generator/state.js +5 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +3 -0
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/variables.js +8 -2
- package/dist/src/codegen/generator/statements/variables.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +7 -0
- package/dist/src/codegen/generator/tables.js +25 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/merge.js +41 -5
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +22 -3
- package/dist/src/driver/compiler.js +49 -58
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/fingerprint.d.ts +27 -0
- package/dist/src/driver/fingerprint.js +88 -0
- package/dist/src/driver/fingerprint.js.map +1 -0
- package/dist/src/driver/toolchain-provider.d.ts +9 -2
- package/dist/src/driver/toolchain-provider.js +15 -15
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.d.ts +58 -1
- package/dist/src/driver/toolchain.js +169 -22
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/extensions/gui/index.js +10 -2
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/parser/expressions.js +20 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/cli/main.test.js +41 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/expressions.test.js +14 -3
- package/dist/tests/codegen/expressions.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +23 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +49 -2
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +62 -3
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/child-process.test.d.ts +10 -0
- package/dist/tests/e2e/child-process.test.js +59 -0
- package/dist/tests/e2e/child-process.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/error-stack.test.d.ts +9 -0
- package/dist/tests/e2e/error-stack.test.js +94 -0
- package/dist/tests/e2e/error-stack.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-svg.test.d.ts +21 -0
- package/dist/tests/e2e/gui-example-svg.test.js +105 -0
- package/dist/tests/e2e/gui-example-svg.test.js.map +1 -0
- package/dist/tests/e2e/gui-transform.test.d.ts +7 -0
- package/dist/tests/e2e/gui-transform.test.js +198 -0
- package/dist/tests/e2e/gui-transform.test.js.map +1 -0
- package/dist/tests/e2e/string-concat.test.d.ts +9 -0
- package/dist/tests/e2e/string-concat.test.js +102 -0
- package/dist/tests/e2e/string-concat.test.js.map +1 -0
- package/dist/tests/helpers.d.ts +9 -0
- package/dist/tests/helpers.js +26 -3
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +7 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/version.test.d.ts +1 -0
- package/dist/tests/version.test.js +24 -0
- package/dist/tests/version.test.js.map +1 -0
- package/doc/DESIGN.md +20 -12
- package/doc/ai/cli.md +7 -0
- package/doc/ai/contributing.md +7 -1
- package/doc/ai/extensions.md +5 -1
- package/doc/ai/language-support.md +4 -4
- package/doc/ai/troubleshooting.md +39 -2
- package/doc/ai/zh-CN/cli.md +2 -0
- package/doc/ai/zh-CN/contributing.md +6 -2
- package/doc/ai/zh-CN/extensions.md +5 -1
- package/doc/ai/zh-CN/language-support.md +4 -4
- package/doc/ai/zh-CN/troubleshooting.md +35 -2
- package/doc/gui/animation.md +53 -2
- package/doc/gui/api.md +1 -1
- package/doc/gui/dom.md +6 -1
- package/doc/gui/input.md +4 -1
- package/doc/gui/known-issues.md +16 -4
- package/doc/gui/layout.md +36 -0
- package/doc/gui/rendering.md +15 -0
- package/doc/gui.md +32 -3
- package/doc/implemented.md +4 -4
- package/doc/requirements.md +13 -4
- package/doc/unimplemented.md +11 -10
- package/doc/zh-CN/DESIGN.md +15 -9
- package/doc/zh-CN/gui/animation.md +49 -2
- package/doc/zh-CN/gui/api.md +1 -1
- package/doc/zh-CN/gui/dom.md +6 -1
- package/doc/zh-CN/gui/input.md +2 -0
- package/doc/zh-CN/gui/known-issues.md +12 -2
- package/doc/zh-CN/gui/layout.md +31 -0
- package/doc/zh-CN/gui/rendering.md +11 -0
- package/doc/zh-CN/gui.md +28 -3
- package/doc/zh-CN/implemented.md +4 -4
- package/doc/zh-CN/requirements.md +12 -3
- package/doc/zh-CN/unimplemented.md +10 -9
- package/package.json +3 -1
- package/runtime/ext_gui/document.cpp +21 -3
- package/runtime/ext_gui/document.h +4 -1
- package/runtime/ext_gui/dom_api_node.cpp +6 -4
- package/runtime/ext_gui/layout.cpp +78 -1
- package/runtime/ext_gui/layout.h +21 -0
- package/runtime/ext_gui/paint.cpp +48 -15
- package/runtime/ext_gui/paint.h +8 -2
- package/runtime/ext_gui/renderer.cpp +4 -2
- package/runtime/ext_gui/renderer_image.cpp +2 -1
- package/runtime/ext_gui/renderer_internal.h +6 -1
- package/runtime/ext_gui/renderer_text.cpp +37 -15
- package/runtime/ext_gui/style.h +10 -0
- package/runtime/ext_gui/style_print.cpp +6 -0
- package/runtime/ext_gui/style_values.cpp +43 -0
- package/runtime/ext_gui/transform.cpp +412 -0
- package/runtime/ext_gui/transform.h +112 -0
- package/runtime/ext_gui/window.cpp +6 -4
- package/runtime/ext_node/buffer/parts/construction.inc +2 -2
- package/runtime/ext_node/buffer/parts/methods.inc +1 -1
- package/runtime/ext_node/buffer/parts/search.inc +1 -1
- package/runtime/ext_node/child_process/child_process.c +25 -0
- package/runtime/ext_node/fs/fd_ops.c +1 -1
- package/runtime/rt.h +17 -1
- package/runtime/rt_internal.h +47 -4
- package/runtime/xt_alloc.c +14 -2
- package/runtime/xt_bigint/conversions.inc +1 -1
- package/runtime/xt_builtins.c +136 -14
- package/runtime/xt_containers/access.inc +17 -16
- package/runtime/xt_containers/objects.inc +4 -3
- package/runtime/xt_io/exception.inc +171 -2
- package/runtime/xt_io/inspect.inc +15 -13
- package/runtime/xt_io.c +5 -0
- package/runtime/xt_stdlib/arrays.inc +2 -2
- package/runtime/xt_stdlib/dispatch.inc +2 -2
- package/runtime/xt_stdlib/strings.inc +33 -21
- package/runtime/xt_stdlib2/array-statics.inc +2 -1
- package/runtime/xt_stdlib2/arrays.inc +1 -1
- package/runtime/xt_stdlib2/error.inc +22 -6
- package/runtime/xt_stdlib2/json.inc +3 -2
- package/runtime/xt_stdlib2/number-string-statics.inc +3 -1
- package/runtime/xt_stdlib2/regexp-match.inc +48 -33
- package/runtime/xt_stdlib2/strings.inc +23 -14
- package/runtime/xt_symbol.c +1 -1
- package/runtime/xt_values/comparison.inc +2 -2
- package/runtime/xt_values/constructors.inc +10 -5
- package/runtime/xt_values/conversions.inc +4 -3
- package/runtime/xt_values/strings.inc +78 -4
- package/scripts/bench.ts +172 -0
- package/scripts/build-runtime.ts +2 -1
- package/scripts/check-version.ts +53 -0
- package/src/ast/common.ts +23 -0
- package/src/cli/main.ts +68 -26
- package/src/codegen/generator/calls/invocation.ts +5 -0
- package/src/codegen/generator/context.ts +129 -18
- package/src/codegen/generator/debug.ts +132 -0
- package/src/codegen/generator/expressions/assignment.ts +9 -5
- package/src/codegen/generator/expressions/operators.ts +47 -8
- package/src/codegen/generator/expressions/primary.ts +5 -2
- package/src/codegen/generator/generator.ts +18 -2
- package/src/codegen/generator/module.ts +78 -6
- package/src/codegen/generator/numeric.ts +158 -0
- package/src/codegen/generator/state.ts +22 -0
- package/src/codegen/generator/statements/control-flow.ts +1 -1
- package/src/codegen/generator/statements/dispatch.ts +3 -0
- package/src/codegen/generator/statements/variables.ts +8 -2
- package/src/codegen/generator/tables.ts +26 -0
- package/src/driver/bundler/merge.ts +39 -5
- package/src/driver/compiler.ts +68 -56
- package/src/driver/fingerprint.ts +85 -0
- package/src/driver/toolchain-provider.ts +17 -16
- package/src/driver/toolchain.ts +210 -21
- package/src/extensions/gui/index.ts +10 -2
- package/src/parser/expressions.ts +20 -1
|
@@ -109,8 +109,11 @@ static int xt_regexp_run(xt_value regexpValue, xt_value input, int32_t start,
|
|
|
109
109
|
if ((uint32_t)start > text->length) return 0;
|
|
110
110
|
int flags = REG_EXTENDED;
|
|
111
111
|
xt_string *f = xt_as_string(regexp->flags);
|
|
112
|
-
if (f)
|
|
113
|
-
|
|
112
|
+
if (f) {
|
|
113
|
+
const char *flagBytes = xt_string_bytes(f);
|
|
114
|
+
for (uint32_t i = 0; i < f->length; i++) if (flagBytes[i] == 'i') flags |= REG_ICASE;
|
|
115
|
+
}
|
|
116
|
+
char *translated = xt_regexp_translate_js(xt_string_bytes(pattern), jsGroups, groupCount);
|
|
114
117
|
if (!translated) return 0;
|
|
115
118
|
regex_t compiled;
|
|
116
119
|
if (regcomp(&compiled, translated, flags) != 0) {
|
|
@@ -118,7 +121,7 @@ static int xt_regexp_run(xt_value regexpValue, xt_value input, int32_t start,
|
|
|
118
121
|
return 0;
|
|
119
122
|
}
|
|
120
123
|
free(translated);
|
|
121
|
-
int ok = regexec(&compiled, text
|
|
124
|
+
int ok = regexec(&compiled, xt_string_bytes(text) + start, nmatch, matches, 0) == 0;
|
|
122
125
|
regfree(&compiled);
|
|
123
126
|
if (!ok) return 0;
|
|
124
127
|
for (size_t i = 0; i < nmatch; i++) {
|
|
@@ -142,7 +145,8 @@ static int xt_regexp_is_global(xt_value regexpValue) {
|
|
|
142
145
|
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
143
146
|
xt_string *f = xt_as_string(regexp->flags);
|
|
144
147
|
if (!f) return 0;
|
|
145
|
-
|
|
148
|
+
const char *flags = xt_string_bytes(f);
|
|
149
|
+
for (uint32_t i = 0; i < f->length; i++) if (flags[i] == 'g') return 1;
|
|
146
150
|
return 0;
|
|
147
151
|
}
|
|
148
152
|
|
|
@@ -154,7 +158,7 @@ static int xt_regexp_js_group_count(xt_value regexpValue) {
|
|
|
154
158
|
int count = (int)pattern->length + 1;
|
|
155
159
|
int *groups = (int *)malloc(sizeof(int) * (size_t)count);
|
|
156
160
|
int groupCount = 0;
|
|
157
|
-
char *translated = xt_regexp_translate_js(pattern
|
|
161
|
+
char *translated = xt_regexp_translate_js(xt_string_bytes(pattern), groups, &groupCount);
|
|
158
162
|
free(translated);
|
|
159
163
|
int total = 0;
|
|
160
164
|
for (int i = 0; i < groupCount; i++) if (groups[i] > total) total = groups[i];
|
|
@@ -177,6 +181,7 @@ xt_value xt_regexp_exec(xt_value target, xt_value input) {
|
|
|
177
181
|
if (!matches || !groups) { free(matches); free(groups); return XT_NULL; }
|
|
178
182
|
xt_value textValue = xt_to_string(input);
|
|
179
183
|
xt_string *text = xt_as_string(textValue);
|
|
184
|
+
const char *textBytes = xt_string_bytes(text);
|
|
180
185
|
int groupCount = 0;
|
|
181
186
|
if (!xt_regexp_run(target, input, 0, matches, cap, groups, &groupCount)) {
|
|
182
187
|
free(matches);
|
|
@@ -186,13 +191,13 @@ xt_value xt_regexp_exec(xt_value target, xt_value input) {
|
|
|
186
191
|
int jsTotal = 0;
|
|
187
192
|
for (int i = 0; i < groupCount; i++) if (groups[i] > jsTotal) jsTotal = groups[i];
|
|
188
193
|
xt_value out = xt_array_new(0, NULL);
|
|
189
|
-
xt_array_push(out, xt_string_new(
|
|
194
|
+
xt_array_push(out, xt_string_new(textBytes + matches[0].rm_so, (int64_t)(matches[0].rm_eo - matches[0].rm_so)));
|
|
190
195
|
for (int g = 1; g <= jsTotal; g++) {
|
|
191
196
|
xt_value groupValue = XT_UNDEFINED;
|
|
192
197
|
for (int i = 0; i < groupCount; i++) {
|
|
193
198
|
if (groups[i] != g) continue;
|
|
194
199
|
if (matches[i + 1].rm_so >= 0) {
|
|
195
|
-
groupValue = xt_string_new(
|
|
200
|
+
groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
196
201
|
}
|
|
197
202
|
break;
|
|
198
203
|
}
|
|
@@ -212,6 +217,7 @@ xt_value xt_regexp_match(xt_value input, xt_value regexp) {
|
|
|
212
217
|
if (!xt_regexp_is_global(regexp)) return xt_regexp_exec(regexp, input);
|
|
213
218
|
xt_value textValue = xt_to_string(input);
|
|
214
219
|
xt_string *text = xt_as_string(textValue);
|
|
220
|
+
const char *textBytes = xt_string_bytes(text);
|
|
215
221
|
xt_value results = xt_array_new(0, NULL);
|
|
216
222
|
int32_t cursor = 0;
|
|
217
223
|
int found = 0;
|
|
@@ -220,7 +226,7 @@ xt_value xt_regexp_match(xt_value input, xt_value regexp) {
|
|
|
220
226
|
int32_t end = 0;
|
|
221
227
|
if (!xt_regexp_find(regexp, input, cursor, &start, &end)) break;
|
|
222
228
|
found = 1;
|
|
223
|
-
xt_array_push(results, xt_string_new(
|
|
229
|
+
xt_array_push(results, xt_string_new(textBytes + start, (int64_t)(end - start)));
|
|
224
230
|
cursor = end > start ? end : end + 1;
|
|
225
231
|
}
|
|
226
232
|
return found ? results : XT_NULL;
|
|
@@ -241,10 +247,11 @@ static void xt_regexp_append(char **out, size_t *length, size_t *capacity, const
|
|
|
241
247
|
static void xt_regexp_append_group(char **out, size_t *length, size_t *capacity,
|
|
242
248
|
xt_string *text, regmatch_t *matches, int *groups,
|
|
243
249
|
int groupCount, int group) {
|
|
250
|
+
const char *textBytes = xt_string_bytes(text);
|
|
244
251
|
for (int i = 0; i < groupCount; i++) {
|
|
245
252
|
if (groups[i] != group) continue;
|
|
246
253
|
if (matches[i + 1].rm_so >= 0) {
|
|
247
|
-
xt_regexp_append(out, length, capacity,
|
|
254
|
+
xt_regexp_append(out, length, capacity, textBytes + matches[i + 1].rm_so,
|
|
248
255
|
(size_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
249
256
|
}
|
|
250
257
|
return;
|
|
@@ -255,32 +262,34 @@ static void xt_regexp_expand_replacement(char **out, size_t *length, size_t *cap
|
|
|
255
262
|
xt_string *replacement, xt_string *text,
|
|
256
263
|
regmatch_t *matches, int *groups, int groupCount,
|
|
257
264
|
int jsTotal) {
|
|
265
|
+
const char *repl = xt_string_bytes(replacement);
|
|
266
|
+
const char *textBytes = xt_string_bytes(text);
|
|
258
267
|
size_t start = (size_t)matches[0].rm_so;
|
|
259
268
|
size_t end = (size_t)matches[0].rm_eo;
|
|
260
269
|
for (size_t i = 0; i < replacement->length; i++) {
|
|
261
|
-
char c =
|
|
270
|
+
char c = repl[i];
|
|
262
271
|
if (c != '$' || i + 1 >= replacement->length) {
|
|
263
272
|
xt_regexp_append(out, length, capacity, &c, 1);
|
|
264
273
|
continue;
|
|
265
274
|
}
|
|
266
|
-
char next =
|
|
275
|
+
char next = repl[i + 1];
|
|
267
276
|
if (next == '$') {
|
|
268
277
|
xt_regexp_append(out, length, capacity, "$", 1);
|
|
269
278
|
i++;
|
|
270
279
|
} else if (next == '&') {
|
|
271
|
-
xt_regexp_append(out, length, capacity,
|
|
280
|
+
xt_regexp_append(out, length, capacity, textBytes + start, end - start);
|
|
272
281
|
i++;
|
|
273
282
|
} else if (next == '`') {
|
|
274
|
-
xt_regexp_append(out, length, capacity,
|
|
283
|
+
xt_regexp_append(out, length, capacity, textBytes, start);
|
|
275
284
|
i++;
|
|
276
285
|
} else if (next == '\'') {
|
|
277
|
-
xt_regexp_append(out, length, capacity,
|
|
286
|
+
xt_regexp_append(out, length, capacity, textBytes + end, text->length - end);
|
|
278
287
|
i++;
|
|
279
288
|
} else if (next >= '0' && next <= '9') {
|
|
280
289
|
int value = next - '0';
|
|
281
290
|
int digits = 1;
|
|
282
|
-
if (i + 2 < replacement->length &&
|
|
283
|
-
int two = value * 10 + (
|
|
291
|
+
if (i + 2 < replacement->length && repl[i + 2] >= '0' && repl[i + 2] <= '9') {
|
|
292
|
+
int two = value * 10 + (repl[i + 2] - '0');
|
|
284
293
|
if (two <= jsTotal) { value = two; digits = 2; }
|
|
285
294
|
}
|
|
286
295
|
if (value >= 1 && value <= jsTotal) {
|
|
@@ -299,6 +308,7 @@ static void xt_regexp_expand_replacement(char **out, size_t *length, size_t *cap
|
|
|
299
308
|
xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replacementValue) {
|
|
300
309
|
xt_string *text = xt_as_string(xt_to_string(value));
|
|
301
310
|
if (!text) return value;
|
|
311
|
+
const char *textBytes = xt_string_bytes(text);
|
|
302
312
|
int global = xt_regexp_is_global(regexpValue);
|
|
303
313
|
int useFunction = XT_IS_FUNCTION(replacementValue);
|
|
304
314
|
xt_string *replacement = NULL;
|
|
@@ -324,18 +334,18 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
324
334
|
if (!xt_regexp_run(regexpValue, value, cursor, matches, cap, groups, &groupCount)) break;
|
|
325
335
|
int32_t start = (int32_t)matches[0].rm_so;
|
|
326
336
|
int32_t end = (int32_t)matches[0].rm_eo;
|
|
327
|
-
xt_regexp_append(&out, &length, &capacity,
|
|
337
|
+
xt_regexp_append(&out, &length, &capacity, textBytes + cursor, (size_t)(start - cursor));
|
|
328
338
|
if (useFunction) {
|
|
329
339
|
/* Accumulate in an `xt_array` so the earlier match/group strings stay
|
|
330
340
|
reachable while later groups (and the callback itself) allocate. */
|
|
331
341
|
xt_value argArray = xt_array_new(0, NULL);
|
|
332
|
-
xt_array_push(argArray, xt_string_new(
|
|
342
|
+
xt_array_push(argArray, xt_string_new(textBytes + start, (int64_t)(end - start)));
|
|
333
343
|
for (int g = 1; g <= jsTotal; g++) {
|
|
334
344
|
xt_value groupValue = XT_UNDEFINED;
|
|
335
345
|
for (int i = 0; i < groupCount; i++) {
|
|
336
346
|
if (groups[i] != g) continue;
|
|
337
347
|
if (matches[i + 1].rm_so >= 0) {
|
|
338
|
-
groupValue = xt_string_new(
|
|
348
|
+
groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
339
349
|
}
|
|
340
350
|
break;
|
|
341
351
|
}
|
|
@@ -345,7 +355,7 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
345
355
|
xt_array_push(argArray, xt_to_string(value));
|
|
346
356
|
xt_value produced = xt_closure_call(replacementValue, jsTotal + 3, xt_as_array(argArray)->items);
|
|
347
357
|
xt_string *producedText = xt_as_string(xt_to_string(produced));
|
|
348
|
-
if (producedText) xt_regexp_append(&out, &length, &capacity, producedText
|
|
358
|
+
if (producedText) xt_regexp_append(&out, &length, &capacity, xt_string_bytes(producedText), producedText->length);
|
|
349
359
|
} else {
|
|
350
360
|
xt_regexp_expand_replacement(&out, &length, &capacity, replacement, text, matches, groups, groupCount, jsTotal);
|
|
351
361
|
}
|
|
@@ -355,7 +365,7 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
355
365
|
} else {
|
|
356
366
|
/* Zero-width match: copy one byte so the scan always progresses. */
|
|
357
367
|
if (cursor >= (int32_t)text->length) break;
|
|
358
|
-
xt_regexp_append(&out, &length, &capacity,
|
|
368
|
+
xt_regexp_append(&out, &length, &capacity, textBytes + cursor, 1);
|
|
359
369
|
cursor++;
|
|
360
370
|
}
|
|
361
371
|
if (!global) break;
|
|
@@ -367,7 +377,7 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
367
377
|
return value;
|
|
368
378
|
}
|
|
369
379
|
size_t tail = text->length - (size_t)cursor;
|
|
370
|
-
xt_regexp_append(&out, &length, &capacity,
|
|
380
|
+
xt_regexp_append(&out, &length, &capacity, textBytes + cursor, tail);
|
|
371
381
|
xt_value result = xt_string_new(out, (int64_t)length);
|
|
372
382
|
free(out);
|
|
373
383
|
free(matches);
|
|
@@ -381,6 +391,7 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
|
|
|
381
391
|
xt_value result = xt_array_new(0, NULL);
|
|
382
392
|
xt_string *text = xt_as_string(xt_to_string(value));
|
|
383
393
|
if (!text) return result;
|
|
394
|
+
const char *textBytes = xt_string_bytes(text);
|
|
384
395
|
int64_t limit = limitValue == XT_UNDEFINED ? 0x7fffffff : (int64_t)xt_to_int32(limitValue);
|
|
385
396
|
if (limit <= 0) return result;
|
|
386
397
|
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
@@ -400,14 +411,14 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
|
|
|
400
411
|
int32_t end = (int32_t)matches[0].rm_eo;
|
|
401
412
|
if (start != q) { q++; continue; }
|
|
402
413
|
if (end == p) { q++; continue; }
|
|
403
|
-
xt_array_push(result, xt_string_new(
|
|
414
|
+
xt_array_push(result, xt_string_new(textBytes + p, (int64_t)(q - p)));
|
|
404
415
|
if ((int64_t)xt_as_array(result)->length >= limit) { free(matches); free(groups); return result; }
|
|
405
416
|
for (int g = 1; g <= jsTotal; g++) {
|
|
406
417
|
xt_value groupValue = XT_UNDEFINED;
|
|
407
418
|
for (int i = 0; i < groupCount; i++) {
|
|
408
419
|
if (groups[i] != g) continue;
|
|
409
420
|
if (matches[i + 1].rm_so >= 0) {
|
|
410
|
-
groupValue = xt_string_new(
|
|
421
|
+
groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
411
422
|
}
|
|
412
423
|
break;
|
|
413
424
|
}
|
|
@@ -418,7 +429,7 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
|
|
|
418
429
|
q = p;
|
|
419
430
|
}
|
|
420
431
|
if ((int64_t)xt_as_array(result)->length < limit) {
|
|
421
|
-
xt_array_push(result, xt_string_new(
|
|
432
|
+
xt_array_push(result, xt_string_new(textBytes + p, (int64_t)(len - p)));
|
|
422
433
|
}
|
|
423
434
|
free(matches);
|
|
424
435
|
free(groups);
|
|
@@ -431,16 +442,20 @@ xt_value xt_regexp_get_property(xt_value target, xt_value key) {
|
|
|
431
442
|
if (!XT_IS_STRING(key)) return XT_UNDEFINED;
|
|
432
443
|
xt_string *k = xt_as_string(key);
|
|
433
444
|
xt_string *f = xt_as_string(regexp->flags);
|
|
445
|
+
const char *keyBytes = xt_string_bytes(k);
|
|
434
446
|
int flag = 0;
|
|
435
|
-
if (k->length == 6 && memcmp(
|
|
436
|
-
if (k->length == 5 && memcmp(
|
|
437
|
-
if (k->length == 9 && memcmp(
|
|
438
|
-
if (k->length == 6 && memcmp(
|
|
439
|
-
else if (k->length == 10 && memcmp(
|
|
440
|
-
else if (k->length == 9 && memcmp(
|
|
441
|
-
else if (k->length == 6 && memcmp(
|
|
447
|
+
if (k->length == 6 && memcmp(keyBytes, "source", 6) == 0) return regexp->source;
|
|
448
|
+
if (k->length == 5 && memcmp(keyBytes, "flags", 5) == 0) return regexp->flags;
|
|
449
|
+
if (k->length == 9 && memcmp(keyBytes, "lastIndex", 9) == 0) return xt_number(0);
|
|
450
|
+
if (k->length == 6 && memcmp(keyBytes, "global", 6) == 0) flag = 'g';
|
|
451
|
+
else if (k->length == 10 && memcmp(keyBytes, "ignoreCase", 10) == 0) flag = 'i';
|
|
452
|
+
else if (k->length == 9 && memcmp(keyBytes, "multiline", 9) == 0) flag = 'm';
|
|
453
|
+
else if (k->length == 6 && memcmp(keyBytes, "sticky", 6) == 0) flag = 'y';
|
|
442
454
|
if (flag) {
|
|
443
|
-
if (f)
|
|
455
|
+
if (f) {
|
|
456
|
+
const char *flags = xt_string_bytes(f);
|
|
457
|
+
for (uint32_t i = 0; i < f->length; i++) if (flags[i] == flag) return XT_TRUE;
|
|
458
|
+
}
|
|
444
459
|
return XT_FALSE;
|
|
445
460
|
}
|
|
446
461
|
return XT_UNDEFINED;
|
|
@@ -9,13 +9,16 @@ static xt_value xt_string_pad(xt_value value, xt_value lengthValue, xt_value pad
|
|
|
9
9
|
if (!pad || pad->length == 0) return value;
|
|
10
10
|
size_t total = (size_t)target;
|
|
11
11
|
xt_string *out = xt_string_alloc(total);
|
|
12
|
+
char *mutable = xt_string_mutable(out);
|
|
13
|
+
const char *text = xt_string_bytes(s);
|
|
14
|
+
const char *padBytes = xt_string_bytes(pad);
|
|
12
15
|
size_t needed = total - s->length;
|
|
13
16
|
if (start) {
|
|
14
|
-
for (size_t i = 0; i < needed; i++)
|
|
15
|
-
memcpy(
|
|
17
|
+
for (size_t i = 0; i < needed; i++) mutable[i] = padBytes[i % pad->length];
|
|
18
|
+
memcpy(mutable + needed, text, s->length);
|
|
16
19
|
} else {
|
|
17
|
-
memcpy(
|
|
18
|
-
for (size_t i = 0; i < needed; i++)
|
|
20
|
+
memcpy(mutable, text, s->length);
|
|
21
|
+
for (size_t i = 0; i < needed; i++) mutable[s->length + i] = padBytes[i % pad->length];
|
|
19
22
|
}
|
|
20
23
|
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
21
24
|
}
|
|
@@ -23,11 +26,12 @@ static xt_value xt_string_pad(xt_value value, xt_value lengthValue, xt_value pad
|
|
|
23
26
|
static xt_value xt_string_trim_side(xt_value value, int start) {
|
|
24
27
|
xt_string *s = xt_as_string(value);
|
|
25
28
|
if (!s) return value;
|
|
29
|
+
const char *bytes = xt_string_bytes(s);
|
|
26
30
|
uint32_t begin = 0;
|
|
27
31
|
uint32_t end = s->length;
|
|
28
|
-
if (start) { while (begin < end && isspace((unsigned char)
|
|
29
|
-
else { while (end > begin && isspace((unsigned char)
|
|
30
|
-
return xt_string_new(
|
|
32
|
+
if (start) { while (begin < end && isspace((unsigned char)bytes[begin])) begin++; }
|
|
33
|
+
else { while (end > begin && isspace((unsigned char)bytes[end - 1])) end--; }
|
|
34
|
+
return xt_string_new(bytes + begin, end - begin);
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
static xt_value xt_string_at(xt_value value, xt_value indexValue) {
|
|
@@ -36,7 +40,7 @@ static xt_value xt_string_at(xt_value value, xt_value indexValue) {
|
|
|
36
40
|
int32_t index = indexValue == XT_UNDEFINED ? 0 : xt_to_int32(indexValue);
|
|
37
41
|
if (index < 0) index += (int32_t)s->length;
|
|
38
42
|
if (index < 0 || (uint32_t)index >= s->length) return XT_UNDEFINED;
|
|
39
|
-
return xt_string_new(s
|
|
43
|
+
return xt_string_new(xt_string_bytes(s) + index, 1);
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
static xt_value xt_string_replace_all(xt_value value, xt_value searchValue, xt_value replacementValue) {
|
|
@@ -46,20 +50,23 @@ static xt_value xt_string_replace_all(xt_value value, xt_value searchValue, xt_v
|
|
|
46
50
|
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
47
51
|
xt_string *replacement = xt_as_string(xt_to_string(replacementValue));
|
|
48
52
|
if (!search || search->length == 0) return value;
|
|
53
|
+
const char *text = xt_string_bytes(s);
|
|
54
|
+
const char *needle = xt_string_bytes(search);
|
|
55
|
+
const char *repl = xt_string_bytes(replacement);
|
|
49
56
|
size_t capacity = s->length + replacement->length * 4 + 16;
|
|
50
57
|
char *out = (char *)malloc(capacity);
|
|
51
58
|
if (!out) abort();
|
|
52
59
|
size_t length = 0;
|
|
53
60
|
uint32_t i = 0;
|
|
54
61
|
while (i < s->length) {
|
|
55
|
-
if (i + search->length <= s->length && memcmp(
|
|
62
|
+
if (i + search->length <= s->length && memcmp(text + i, needle, search->length) == 0) {
|
|
56
63
|
if (length + replacement->length + 2 > capacity) { capacity = (length + replacement->length + 2) * 2; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
57
|
-
memcpy(out + length,
|
|
64
|
+
memcpy(out + length, repl, replacement->length);
|
|
58
65
|
length += replacement->length;
|
|
59
66
|
i += search->length;
|
|
60
67
|
} else {
|
|
61
68
|
if (length + 2 > capacity) { capacity *= 2; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
62
|
-
out[length++] =
|
|
69
|
+
out[length++] = text[i++];
|
|
63
70
|
}
|
|
64
71
|
}
|
|
65
72
|
xt_value result = xt_string_new(out, length);
|
|
@@ -106,17 +113,19 @@ xt_value xt_ext_string_method(xt_value target, const char *method, int32_t argc,
|
|
|
106
113
|
if (limit > (int32_t)s->length) limit = (int32_t)s->length;
|
|
107
114
|
int32_t nlen = (int32_t)needle->length;
|
|
108
115
|
if (nlen == 0) return xt_number((double)(limit < 0 ? 0 : limit));
|
|
116
|
+
const char *text = xt_string_bytes(s);
|
|
117
|
+
const char *need = xt_string_bytes(needle);
|
|
109
118
|
int32_t i = limit;
|
|
110
119
|
if (i > (int32_t)s->length - nlen) i = (int32_t)s->length - nlen;
|
|
111
120
|
for (; i >= 0; i--) {
|
|
112
|
-
if (memcmp(
|
|
121
|
+
if (memcmp(text + i, need, (size_t)nlen) == 0) return xt_number((double)i);
|
|
113
122
|
}
|
|
114
123
|
return xt_number(-1);
|
|
115
124
|
}
|
|
116
125
|
if (strcmp(method, "localeCompare") == 0) {
|
|
117
126
|
xt_string *a = xt_as_string(target);
|
|
118
127
|
xt_string *b = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|
|
119
|
-
int cmp = strcmp(a ? a
|
|
128
|
+
int cmp = strcmp(a ? xt_string_bytes(a) : "", b ? xt_string_bytes(b) : "");
|
|
120
129
|
return xt_number(cmp < 0 ? -1 : cmp > 0 ? 1 : 0);
|
|
121
130
|
}
|
|
122
131
|
if (strcmp(method, "codePointAt") == 0) {
|
|
@@ -124,7 +133,7 @@ xt_value xt_ext_string_method(xt_value target, const char *method, int32_t argc,
|
|
|
124
133
|
if (!s) return XT_UNDEFINED;
|
|
125
134
|
int32_t index = xt_to_int32(xt_arg_at(argc, argv, 0));
|
|
126
135
|
if (index < 0 || (uint32_t)index >= s->length) return XT_UNDEFINED;
|
|
127
|
-
return xt_number((double)(unsigned char)s
|
|
136
|
+
return xt_number((double)(unsigned char)xt_string_bytes(s)[index]);
|
|
128
137
|
}
|
|
129
138
|
if (strcmp(method, "valueOf") == 0 || strcmp(method, "toString") == 0) return target;
|
|
130
139
|
*handled = 0;
|
package/runtime/xt_symbol.c
CHANGED
|
@@ -98,7 +98,7 @@ xt_value xt_symbol_to_string(xt_value value) {
|
|
|
98
98
|
char *buffer = (char *)malloc(8 + length);
|
|
99
99
|
if (!buffer) abort();
|
|
100
100
|
memcpy(buffer, "Symbol(", 7);
|
|
101
|
-
if (description) memcpy(buffer + 7, description
|
|
101
|
+
if (description) memcpy(buffer + 7, xt_string_bytes(description), length);
|
|
102
102
|
buffer[7 + length] = ')';
|
|
103
103
|
xt_value result = xt_string_new(buffer, 8 + length);
|
|
104
104
|
free(buffer);
|
|
@@ -33,7 +33,7 @@ xt_value xt_lt(xt_value a, xt_value b) {
|
|
|
33
33
|
xt_string *x = xt_as_string(a);
|
|
34
34
|
xt_string *y = xt_as_string(b);
|
|
35
35
|
size_t n = x->length < y->length ? x->length : y->length;
|
|
36
|
-
int cmp = memcmp(x
|
|
36
|
+
int cmp = memcmp(xt_string_bytes(x), xt_string_bytes(y), n);
|
|
37
37
|
if (cmp == 0) return xt_bool(x->length < y->length);
|
|
38
38
|
return xt_bool(cmp < 0);
|
|
39
39
|
}
|
|
@@ -48,7 +48,7 @@ xt_value xt_le(xt_value a, xt_value b) {
|
|
|
48
48
|
xt_string *x = xt_as_string(a);
|
|
49
49
|
xt_string *y = xt_as_string(b);
|
|
50
50
|
size_t n = x->length < y->length ? x->length : y->length;
|
|
51
|
-
int cmp = memcmp(x
|
|
51
|
+
int cmp = memcmp(xt_string_bytes(x), xt_string_bytes(y), n);
|
|
52
52
|
if (cmp == 0) return xt_bool(x->length <= y->length);
|
|
53
53
|
return xt_bool(cmp < 0);
|
|
54
54
|
}
|
|
@@ -10,19 +10,22 @@ xt_value xt_number(double d) { return xt_from_double(d); }
|
|
|
10
10
|
xt_string *xt_string_alloc(size_t length) {
|
|
11
11
|
xt_string *s = (xt_string *)xt_alloc(sizeof(xt_string), XT_OBJECT_KIND_STRING);
|
|
12
12
|
s->length = (uint32_t)length;
|
|
13
|
+
/* Keep the terminator in the allocation: many consumers hand `flat` to libc. */
|
|
13
14
|
s->capacity = (uint32_t)length + 1;
|
|
14
|
-
s->
|
|
15
|
-
if (!s->
|
|
15
|
+
s->flat = (char *)malloc(s->capacity);
|
|
16
|
+
if (!s->flat) {
|
|
16
17
|
fprintf(stderr, "xbintsc: out of memory allocating string\n");
|
|
17
18
|
abort();
|
|
18
19
|
}
|
|
19
|
-
s->
|
|
20
|
+
s->flat[length] = '\0';
|
|
21
|
+
s->left = NULL;
|
|
22
|
+
s->right = NULL;
|
|
20
23
|
return s;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
xt_value xt_string_new(const char *data, size_t len) {
|
|
24
27
|
xt_string *s = xt_string_alloc(len);
|
|
25
|
-
if (len > 0) memcpy(s->
|
|
28
|
+
if (len > 0) memcpy(s->flat, data, len);
|
|
26
29
|
return XT_FROM_PTR(XT_TAG_STRING, s);
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -30,9 +33,11 @@ xt_value xt_string_from_cstr(const char *data) { return xt_string_new(data, strl
|
|
|
30
33
|
|
|
31
34
|
xt_string *xt_as_string(xt_value v);
|
|
32
35
|
|
|
36
|
+
char *xt_string_mutable(xt_string *s) { return s ? s->flat : NULL; }
|
|
37
|
+
|
|
33
38
|
const char *xt_string_data(xt_value value) {
|
|
34
39
|
xt_string *s = xt_as_string(value);
|
|
35
|
-
return s ? s
|
|
40
|
+
return s ? xt_string_bytes(s) : NULL;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
int32_t xt_string_length_value(xt_value value) {
|
|
@@ -24,8 +24,9 @@ double xt_to_number(xt_value v) {
|
|
|
24
24
|
if (XT_IS_BIGINT(v)) return xt_bigint_to_double_value(v);
|
|
25
25
|
if (XT_IS_STRING(v)) {
|
|
26
26
|
xt_string *s = xt_as_string(v);
|
|
27
|
-
const char *
|
|
28
|
-
const char *
|
|
27
|
+
const char *bytes = xt_string_bytes(s);
|
|
28
|
+
const char *begin = bytes;
|
|
29
|
+
const char *end = bytes + s->length;
|
|
29
30
|
while (begin < end && isspace((unsigned char)*begin)) begin++;
|
|
30
31
|
while (end > begin && isspace((unsigned char)end[-1])) end--;
|
|
31
32
|
size_t length = (size_t)(end - begin);
|
|
@@ -83,7 +84,7 @@ xt_value xt_to_string(xt_value v) {
|
|
|
83
84
|
out = (char *)realloc(out, capacity);
|
|
84
85
|
}
|
|
85
86
|
if (i > 0) out[length++] = ',';
|
|
86
|
-
memcpy(out + length, str
|
|
87
|
+
memcpy(out + length, xt_string_bytes(str), str->length);
|
|
87
88
|
length += str->length;
|
|
88
89
|
out[length] = '\0';
|
|
89
90
|
}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
/* Strings */
|
|
3
3
|
/* ------------------------------------------------------------------------- */
|
|
4
4
|
|
|
5
|
+
/* Concatenations up to this many bytes are copied eagerly. The copy is cheaper
|
|
6
|
+
* than a cons node for the short strings that dominate everyday code, and a
|
|
7
|
+
* loop that keeps appending crosses the threshold quickly, after which every
|
|
8
|
+
* append is O(1). */
|
|
9
|
+
#define XT_STRING_CONS_THRESHOLD 32
|
|
10
|
+
|
|
5
11
|
xt_string *xt_as_string(xt_value v) {
|
|
6
12
|
if (XT_IS_STRING(v)) return (xt_string *)XT_GET_PTR(v);
|
|
7
13
|
return NULL;
|
|
@@ -9,14 +15,82 @@ xt_string *xt_as_string(xt_value v) {
|
|
|
9
15
|
|
|
10
16
|
int32_t xt_string_length(xt_string *s) { return (int32_t)s->length; }
|
|
11
17
|
|
|
18
|
+
const char *xt_string_bytes(xt_string *s) {
|
|
19
|
+
if (!s) return NULL;
|
|
20
|
+
if (s->flat) return s->flat;
|
|
21
|
+
|
|
22
|
+
/* The tree is traversed depth-first without recursion: a loop that appends
|
|
23
|
+
* 100k times produces a 100k-deep left spine, which would overflow the C
|
|
24
|
+
* stack. Right children are pushed so the leftmost leaf is emitted first. */
|
|
25
|
+
size_t total = (size_t)s->length;
|
|
26
|
+
char *out = (char *)malloc(total + 1);
|
|
27
|
+
if (!out) {
|
|
28
|
+
fprintf(stderr, "xbintsc: out of memory flattening string\n");
|
|
29
|
+
abort();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
size_t capacity = 64;
|
|
33
|
+
size_t count = 0;
|
|
34
|
+
xt_string **stack = (xt_string **)malloc(sizeof(xt_string *) * capacity);
|
|
35
|
+
if (!stack) {
|
|
36
|
+
fprintf(stderr, "xbintsc: out of memory flattening string\n");
|
|
37
|
+
abort();
|
|
38
|
+
}
|
|
39
|
+
stack[count++] = s;
|
|
40
|
+
|
|
41
|
+
size_t offset = 0;
|
|
42
|
+
while (count > 0) {
|
|
43
|
+
xt_string *node = stack[--count];
|
|
44
|
+
if (node->flat) {
|
|
45
|
+
memcpy(out + offset, node->flat, node->length);
|
|
46
|
+
offset += node->length;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (count + 2 > capacity) {
|
|
50
|
+
while (count + 2 > capacity) capacity *= 2;
|
|
51
|
+
xt_string **grown = (xt_string **)realloc(stack, sizeof(xt_string *) * capacity);
|
|
52
|
+
if (!grown) {
|
|
53
|
+
fprintf(stderr, "xbintsc: out of memory flattening string\n");
|
|
54
|
+
abort();
|
|
55
|
+
}
|
|
56
|
+
stack = grown;
|
|
57
|
+
}
|
|
58
|
+
if (node->right) stack[count++] = node->right;
|
|
59
|
+
if (node->left) stack[count++] = node->left;
|
|
60
|
+
}
|
|
61
|
+
free(stack);
|
|
62
|
+
|
|
63
|
+
out[offset] = '\0';
|
|
64
|
+
s->flat = out;
|
|
65
|
+
s->capacity = (uint32_t)(total + 1);
|
|
66
|
+
s->left = NULL;
|
|
67
|
+
s->right = NULL;
|
|
68
|
+
return s->flat;
|
|
69
|
+
}
|
|
70
|
+
|
|
12
71
|
static xt_value xt_string_concat(xt_string *a, xt_string *b) {
|
|
72
|
+
if (a->length == 0) return XT_FROM_PTR(XT_TAG_STRING, b);
|
|
73
|
+
if (b->length == 0) return XT_FROM_PTR(XT_TAG_STRING, a);
|
|
13
74
|
size_t total = (size_t)a->length + (size_t)b->length;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
75
|
+
if (total <= XT_STRING_CONS_THRESHOLD) {
|
|
76
|
+
xt_string *out = xt_string_alloc(total);
|
|
77
|
+
memcpy(out->flat, xt_string_bytes(a), a->length);
|
|
78
|
+
memcpy(out->flat + a->length, xt_string_bytes(b), b->length);
|
|
79
|
+
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
80
|
+
}
|
|
81
|
+
xt_string *out = (xt_string *)xt_alloc(sizeof(xt_string), XT_OBJECT_KIND_STRING);
|
|
82
|
+
out->length = (uint32_t)total;
|
|
83
|
+
out->capacity = 0;
|
|
84
|
+
out->flat = NULL;
|
|
85
|
+
out->left = a;
|
|
86
|
+
out->right = b;
|
|
17
87
|
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
18
88
|
}
|
|
19
89
|
|
|
20
90
|
int xt_string_equals(xt_string *a, xt_string *b) {
|
|
21
|
-
|
|
91
|
+
/* Interned literals (property names above all) are shared, so identity is the
|
|
92
|
+
* common case and skips the length, flattening and byte comparison entirely. */
|
|
93
|
+
if (a == b) return 1;
|
|
94
|
+
if (a->length != b->length) return 0;
|
|
95
|
+
return memcmp(xt_string_bytes(a), xt_string_bytes(b), a->length) == 0;
|
|
22
96
|
}
|