teptris 0.3.22 → 0.3.24
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/package.json +1 -1
- package/src/index.js +113 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teptris",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
4
4
|
"description": "TOML for JavaScript/TypeScript at libteptris speed (WebAssembly, no fallback) - version tracks the engine ({engine}.{iteration}; npm semver is 3-part)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/index.js
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
const KIND_OFFSET = 4, KIND_LOCAL_DT = 5, KIND_DATE = 6, KIND_TIME = 7;
|
|
5
5
|
|
|
6
|
+
// Date instances are the JS ecosystem's datetime shape (smol-toml
|
|
7
|
+
// hands them back) - they re-enter as the engine's datetime op. The
|
|
8
|
+
// fields come straight off the Date (toISOString + the parse regex
|
|
9
|
+
// per value measured as the datetime-heavy hot cost); Date carries
|
|
10
|
+
// millisecond precision, so nanoseconds = ms * 1e6.
|
|
11
|
+
const dateToDt = (d) => ({
|
|
12
|
+
kind: KIND_OFFSET, /* toISOString always yields Z */
|
|
13
|
+
year: d.getUTCFullYear(), month: d.getUTCMonth() + 1,
|
|
14
|
+
day: d.getUTCDate(), hour: d.getUTCHours(),
|
|
15
|
+
minute: d.getUTCMinutes(), second: d.getUTCSeconds(),
|
|
16
|
+
nanosecond: d.getUTCMilliseconds() * 1e6, offset_seconds: 0,
|
|
17
|
+
});
|
|
18
|
+
|
|
6
19
|
function cstr(mod, s) {
|
|
7
20
|
const bytes = mod.lengthBytesUTF8(s) + 1;
|
|
8
21
|
const ptr = mod._teptris_wasm_alloc(bytes);
|
|
@@ -10,6 +23,21 @@ function cstr(mod, s) {
|
|
|
10
23
|
return ptr;
|
|
11
24
|
}
|
|
12
25
|
|
|
26
|
+
// The len-bounded fast path: TextEncoder.encodeInto writes UTF-8
|
|
27
|
+
// STRAIGHT into the wasm heap with the native encoder, replacing
|
|
28
|
+
// emscripten's two JS-loop passes (lengthBytesUTF8 + stringToUTF8) -
|
|
29
|
+
// the measured biggest JS-side cost of the loads crossing. Only for
|
|
30
|
+
// entry points whose C side takes an explicit length (no NUL needed);
|
|
31
|
+
// NUL-terminated destinations keep cstr.
|
|
32
|
+
const utf8Encoder = new TextEncoder();
|
|
33
|
+
|
|
34
|
+
function cstrLen(mod, s) {
|
|
35
|
+
const cap = s.length * 3 + 4; /* UTF-8 worst case: 3 bytes/unit */
|
|
36
|
+
const ptr = mod._teptris_wasm_alloc(cap);
|
|
37
|
+
const { written } = utf8Encoder.encodeInto(s, mod.HEAPU8.subarray(ptr, ptr + cap));
|
|
38
|
+
return { ptr, len: written };
|
|
39
|
+
}
|
|
40
|
+
|
|
13
41
|
function freeCstr(mod, ptr) { mod._teptris_wasm_free(ptr); }
|
|
14
42
|
|
|
15
43
|
// RFC 3339-ish (the four TOML shapes) back into builder fields
|
|
@@ -116,6 +144,15 @@ function buildValue(mod, b, key, v, errs) {
|
|
|
116
144
|
default:
|
|
117
145
|
break;
|
|
118
146
|
}
|
|
147
|
+
if (v instanceof Date) {
|
|
148
|
+
// the JS ecosystem's datetime shape re-enters as the engine's
|
|
149
|
+
// datetime op (same mapping as the RFC 3339 string form)
|
|
150
|
+
const dt = dateToDt(v);
|
|
151
|
+
mod._teptris_wasm_builder_put_datetime(
|
|
152
|
+
b, kp, dt.kind, dt.year, dt.month, dt.day, dt.hour,
|
|
153
|
+
dt.minute, dt.second, dt.nanosecond, dt.offset_seconds);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
119
156
|
if (Array.isArray(v)) {
|
|
120
157
|
mod._teptris_wasm_builder_open_array(b, kp);
|
|
121
158
|
for (const item of v) buildValue(mod, b, null, item, errs);
|
|
@@ -148,8 +185,8 @@ export function initApi(mod) {
|
|
|
148
185
|
if (typeof toml !== "string") {
|
|
149
186
|
throw new TypeError("loads expects a string");
|
|
150
187
|
}
|
|
151
|
-
const ptr =
|
|
152
|
-
const st = mod._teptris_wasm_load(ptr,
|
|
188
|
+
const { ptr, len } = cstrLen(mod, toml);
|
|
189
|
+
const st = mod._teptris_wasm_load(ptr, len);
|
|
153
190
|
let value = null, error = null;
|
|
154
191
|
if (st === 0) {
|
|
155
192
|
const jptr = mod._teptris_wasm_json();
|
|
@@ -263,9 +300,73 @@ export function initApi(mod) {
|
|
|
263
300
|
n += 1 + written;
|
|
264
301
|
}
|
|
265
302
|
};
|
|
303
|
+
// the datetime record is value-only (the key op precedes it)
|
|
304
|
+
const writeDatetime = (k, dt) => {
|
|
305
|
+
if (k !== null) wkey(k);
|
|
306
|
+
ensure(27);
|
|
307
|
+
buf[n++] = 7;
|
|
308
|
+
buf[n++] = dt.kind;
|
|
309
|
+
i64(dt.year);
|
|
310
|
+
buf[n++] = dt.month; buf[n++] = dt.day; buf[n++] = dt.hour;
|
|
311
|
+
buf[n++] = dt.minute; buf[n++] = dt.second;
|
|
312
|
+
u32(dt.nanosecond);
|
|
313
|
+
i64(dt.offset_seconds);
|
|
314
|
+
};
|
|
266
315
|
const scalar = (k, v) => {
|
|
267
316
|
// array ELEMENTS carry no key op (writer grammar v0.3.11): the
|
|
268
317
|
// old klen-0 marker is now a genuine empty key
|
|
318
|
+
if (v instanceof Date) {
|
|
319
|
+
writeDatetime(k, dateToDt(v));
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// HOT PATH: keyed int/bool scalars (the mega-flat shapes are
|
|
323
|
+
// exactly this) - ONE growth check for key + value together,
|
|
324
|
+
// wkey inlined, direct DataView writes. The profiled dump cost
|
|
325
|
+
// was 91% JS walk: per-byte closure calls dominated.
|
|
326
|
+
if (k !== null && typeof v === "number" && !Object.is(v, -0) &&
|
|
327
|
+
Number.isInteger(v) && v >= -I64_BOUND && v < I64_BOUND) {
|
|
328
|
+
ensure(4 + k.length * 3 + 8);
|
|
329
|
+
buf[n++] = 1; /* the KEY op */
|
|
330
|
+
let ascii = true;
|
|
331
|
+
for (let i = 0; i < k.length; i++) {
|
|
332
|
+
const c = k.charCodeAt(i);
|
|
333
|
+
if (c > 127) { ascii = false; break; }
|
|
334
|
+
buf[n + 1 + i] = c;
|
|
335
|
+
}
|
|
336
|
+
if (ascii) {
|
|
337
|
+
buf[n] = k.length; n += 1 + k.length;
|
|
338
|
+
} else {
|
|
339
|
+
const { written } = utf8enc.encodeInto(k, buf.subarray(n + 1));
|
|
340
|
+
buf[n] = written; n += 1 + written;
|
|
341
|
+
}
|
|
342
|
+
buf[n++] = 3;
|
|
343
|
+
if (v >= -2147483648 && v <= 2147483647) {
|
|
344
|
+
dv.setInt32(n, v, true);
|
|
345
|
+
dv.setInt32(n + 4, v < 0 ? -1 : 0, true);
|
|
346
|
+
} else {
|
|
347
|
+
dv.setBigInt64(n, BigInt(v), true);
|
|
348
|
+
}
|
|
349
|
+
n += 8;
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (k !== null && typeof v === "boolean") {
|
|
353
|
+
ensure(3 + k.length * 3 + 1);
|
|
354
|
+
buf[n++] = 1; /* the KEY op */
|
|
355
|
+
let ascii = true;
|
|
356
|
+
for (let i = 0; i < k.length; i++) {
|
|
357
|
+
const c = k.charCodeAt(i);
|
|
358
|
+
if (c > 127) { ascii = false; break; }
|
|
359
|
+
buf[n + 1 + i] = c;
|
|
360
|
+
}
|
|
361
|
+
if (ascii) {
|
|
362
|
+
buf[n] = k.length; n += 1 + k.length;
|
|
363
|
+
} else {
|
|
364
|
+
const { written } = utf8enc.encodeInto(k, buf.subarray(n + 1));
|
|
365
|
+
buf[n] = written; n += 1 + written;
|
|
366
|
+
}
|
|
367
|
+
buf[n++] = v ? 6 : 5;
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
269
370
|
if (k !== null) wkey(k);
|
|
270
371
|
if (typeof v === "boolean") { byte(v ? 6 : 5); return; }
|
|
271
372
|
if (typeof v === "number") {
|
|
@@ -289,18 +390,7 @@ export function initApi(mod) {
|
|
|
289
390
|
}
|
|
290
391
|
if (typeof v === "string") {
|
|
291
392
|
if (maybeRfc3339(v) && RFC3339.test(v)) {
|
|
292
|
-
|
|
293
|
-
// the key prefix is already written (top of scalar()); the
|
|
294
|
-
// datetime record is value-only - re-sending the key here
|
|
295
|
-
// crashed bare array elements (k === null)
|
|
296
|
-
ensure(27);
|
|
297
|
-
buf[n++] = 7;
|
|
298
|
-
buf[n++] = dt.kind;
|
|
299
|
-
i64(dt.year);
|
|
300
|
-
buf[n++] = dt.month; buf[n++] = dt.day; buf[n++] = dt.hour;
|
|
301
|
-
buf[n++] = dt.minute; buf[n++] = dt.second;
|
|
302
|
-
u32(dt.nanosecond);
|
|
303
|
-
i64(dt.offset_seconds);
|
|
393
|
+
writeDatetime(null, parseDateTime(v));
|
|
304
394
|
return;
|
|
305
395
|
}
|
|
306
396
|
ensure(5 + v.length * 3);
|
|
@@ -326,7 +416,8 @@ export function initApi(mod) {
|
|
|
326
416
|
const isScalar = (v) =>
|
|
327
417
|
v !== null && (typeof v !== "object" || Array.isArray(v) === false &&
|
|
328
418
|
(typeof v === "string" || typeof v === "number" ||
|
|
329
|
-
typeof v === "boolean" || typeof v === "bigint"))
|
|
419
|
+
typeof v === "boolean" || typeof v === "bigint")) ||
|
|
420
|
+
v instanceof Date;
|
|
330
421
|
let depth = 0;
|
|
331
422
|
const members = (o, inline) => {
|
|
332
423
|
// name the engine's 512 ceiling: without this the JS stack
|
|
@@ -350,7 +441,7 @@ export function initApi(mod) {
|
|
|
350
441
|
if (Array.isArray(v)) {
|
|
351
442
|
const aot = v.length > 0 && !inline && v.every(
|
|
352
443
|
(x) => x !== null && typeof x === "object" &&
|
|
353
|
-
!Array.isArray(x));
|
|
444
|
+
!Array.isArray(x) && !(x instanceof Date));
|
|
354
445
|
if (aot) {
|
|
355
446
|
// element bodies are EXPLICIT (the element opens with the
|
|
356
447
|
// section op): nested tables promote to [path.sub]
|
|
@@ -428,8 +519,8 @@ export function initApi(mod) {
|
|
|
428
519
|
if (typeof toml !== "string") {
|
|
429
520
|
throw new TypeError("loadsDoc expects a string");
|
|
430
521
|
}
|
|
431
|
-
const ptr =
|
|
432
|
-
const st = mod._teptris_wasm_load_doc(ptr,
|
|
522
|
+
const { ptr, len } = cstrLen(mod, toml);
|
|
523
|
+
const st = mod._teptris_wasm_load_doc(ptr, len);
|
|
433
524
|
freeCstr(mod, ptr);
|
|
434
525
|
if (st !== 0) {
|
|
435
526
|
docHeld = false;
|
|
@@ -567,8 +658,8 @@ export function initApi(mod) {
|
|
|
567
658
|
if (typeof toml !== "string") {
|
|
568
659
|
throw new TypeError("planWalk expects a TOML string");
|
|
569
660
|
}
|
|
570
|
-
const p =
|
|
571
|
-
const st = mod._teptris_wasm_plan_walk(plan, p,
|
|
661
|
+
const { ptr: p, len: plen } = cstrLen(mod, toml);
|
|
662
|
+
const st = mod._teptris_wasm_plan_walk(plan, p, plen);
|
|
572
663
|
freeCstr(mod, p);
|
|
573
664
|
if (st !== 0) {
|
|
574
665
|
throw planError();
|
|
@@ -594,11 +685,11 @@ export function initApi(mod) {
|
|
|
594
685
|
throw new TypeError("edit expects three strings");
|
|
595
686
|
}
|
|
596
687
|
}
|
|
597
|
-
const sp =
|
|
688
|
+
const { ptr: sp, len: slen } = cstrLen(mod, source);
|
|
598
689
|
const kp = cstr(mod, keyPath);
|
|
599
690
|
const rp = cstr(mod, replacement);
|
|
600
691
|
let out = null, error = null;
|
|
601
|
-
const st = mod._teptris_wasm_edit(sp,
|
|
692
|
+
const st = mod._teptris_wasm_edit(sp, slen, kp, rp);
|
|
602
693
|
if (st === 0) {
|
|
603
694
|
const optr = mod._teptris_wasm_json();
|
|
604
695
|
const olen = mod._teptris_wasm_json_len();
|