teptris 0.3.23 → 0.3.25
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 +106 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teptris",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.25",
|
|
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);
|
|
@@ -131,6 +144,15 @@ function buildValue(mod, b, key, v, errs) {
|
|
|
131
144
|
default:
|
|
132
145
|
break;
|
|
133
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
|
+
}
|
|
134
156
|
if (Array.isArray(v)) {
|
|
135
157
|
mod._teptris_wasm_builder_open_array(b, kp);
|
|
136
158
|
for (const item of v) buildValue(mod, b, null, item, errs);
|
|
@@ -278,9 +300,73 @@ export function initApi(mod) {
|
|
|
278
300
|
n += 1 + written;
|
|
279
301
|
}
|
|
280
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
|
+
};
|
|
281
315
|
const scalar = (k, v) => {
|
|
282
316
|
// array ELEMENTS carry no key op (writer grammar v0.3.11): the
|
|
283
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
|
+
}
|
|
284
370
|
if (k !== null) wkey(k);
|
|
285
371
|
if (typeof v === "boolean") { byte(v ? 6 : 5); return; }
|
|
286
372
|
if (typeof v === "number") {
|
|
@@ -304,18 +390,7 @@ export function initApi(mod) {
|
|
|
304
390
|
}
|
|
305
391
|
if (typeof v === "string") {
|
|
306
392
|
if (maybeRfc3339(v) && RFC3339.test(v)) {
|
|
307
|
-
|
|
308
|
-
// the key prefix is already written (top of scalar()); the
|
|
309
|
-
// datetime record is value-only - re-sending the key here
|
|
310
|
-
// crashed bare array elements (k === null)
|
|
311
|
-
ensure(27);
|
|
312
|
-
buf[n++] = 7;
|
|
313
|
-
buf[n++] = dt.kind;
|
|
314
|
-
i64(dt.year);
|
|
315
|
-
buf[n++] = dt.month; buf[n++] = dt.day; buf[n++] = dt.hour;
|
|
316
|
-
buf[n++] = dt.minute; buf[n++] = dt.second;
|
|
317
|
-
u32(dt.nanosecond);
|
|
318
|
-
i64(dt.offset_seconds);
|
|
393
|
+
writeDatetime(null, parseDateTime(v));
|
|
319
394
|
return;
|
|
320
395
|
}
|
|
321
396
|
ensure(5 + v.length * 3);
|
|
@@ -341,7 +416,8 @@ export function initApi(mod) {
|
|
|
341
416
|
const isScalar = (v) =>
|
|
342
417
|
v !== null && (typeof v !== "object" || Array.isArray(v) === false &&
|
|
343
418
|
(typeof v === "string" || typeof v === "number" ||
|
|
344
|
-
typeof v === "boolean" || typeof v === "bigint"))
|
|
419
|
+
typeof v === "boolean" || typeof v === "bigint")) ||
|
|
420
|
+
v instanceof Date;
|
|
345
421
|
let depth = 0;
|
|
346
422
|
const members = (o, inline) => {
|
|
347
423
|
// name the engine's 512 ceiling: without this the JS stack
|
|
@@ -351,13 +427,25 @@ export function initApi(mod) {
|
|
|
351
427
|
}
|
|
352
428
|
try {
|
|
353
429
|
const keys = Object.keys(o);
|
|
430
|
+
// ONE pass: scalars emit immediately (canonical order - scalar
|
|
431
|
+
// ops precede table ops per table); container keys collect and
|
|
432
|
+
// walk after. The inlined type test replaces the per-key
|
|
433
|
+
// isScalar closure call x2 the two-pass structure paid.
|
|
434
|
+
const containers = [];
|
|
354
435
|
for (let i = 0; i < keys.length; i++) {
|
|
355
436
|
const k = keys[i], v = o[k];
|
|
356
|
-
|
|
437
|
+
const tv = typeof v;
|
|
438
|
+
if (v instanceof Date ||
|
|
439
|
+
(v !== null && (tv === "string" || tv === "number" ||
|
|
440
|
+
tv === "boolean" || tv === "bigint"))) {
|
|
441
|
+
scalar(k, v);
|
|
442
|
+
} else {
|
|
443
|
+
containers.push(k);
|
|
444
|
+
}
|
|
357
445
|
}
|
|
358
|
-
for (let i = 0; i <
|
|
359
|
-
const k =
|
|
360
|
-
|
|
446
|
+
for (let i = 0; i < containers.length; i++) {
|
|
447
|
+
const k = containers[i];
|
|
448
|
+
const v = o[k];
|
|
361
449
|
if (v === null || v === undefined) {
|
|
362
450
|
throw new Error("null/undefined cannot round-trip through TOML (nan/inf parse to null in loads() and cannot re-enter)");
|
|
363
451
|
}
|
|
@@ -365,7 +453,7 @@ export function initApi(mod) {
|
|
|
365
453
|
if (Array.isArray(v)) {
|
|
366
454
|
const aot = v.length > 0 && !inline && v.every(
|
|
367
455
|
(x) => x !== null && typeof x === "object" &&
|
|
368
|
-
!Array.isArray(x));
|
|
456
|
+
!Array.isArray(x) && !(x instanceof Date));
|
|
369
457
|
if (aot) {
|
|
370
458
|
// element bodies are EXPLICIT (the element opens with the
|
|
371
459
|
// section op): nested tables promote to [path.sub]
|