@fraczak/k 7.0.2 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -0
- package/DOCS/CODECS.md +37 -2
- package/DOCS/CODEC_ARCHITECTURE.md +12 -5
- package/DOCS/FILE_FORMATS.md +238 -15
- package/DOCS/POLYMORPHIC_IR.md +104 -0
- package/DOCS/REPL.md +11 -3
- package/DOCS/WEB_REPL_REQUIREMENTS.md +1 -1
- package/JOURNAL.md +288 -0
- package/README.md +12 -3
- package/Value.mjs +28 -3
- package/access-table.mjs +343 -0
- package/backend-api.mjs +23 -1
- package/backends/llvm/README.md +20 -21
- package/backends/llvm/bin/k-llvm-build.mjs +105 -32
- package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
- package/backends/llvm/bin/k-llvm.mjs +292 -0
- package/backends/llvm/runtime/krt.c +8 -0
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/src/cli.mjs +95 -17
- package/backends/llvm/src/executable.mjs +128 -11
- package/backends/llvm/src/kvm2llvm.mjs +10 -5
- package/backends/llvm/src/llvm.mjs +121 -9
- package/backends/llvm/tests/integration.sh +11 -11
- package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
- package/backends/llvm/tests/perf-support.mjs +24 -3
- package/backends/llvm/tests/test-llvm.mjs +22 -1
- package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
- package/backends/wasm/bin/k-wasm-run.mjs +61 -20
- package/backends/wasm/bin/k-wasm.mjs +47 -13
- package/backends/wasm/src/cli.mjs +141 -19
- package/backends/wasm/src/wasm.mjs +130 -23
- package/browser/repl-browser.mjs +16 -5
- package/browser/tree-view.mjs +47 -8
- package/browser/vfs-data.mjs +6 -5
- package/codecs/README.md +1 -0
- package/codecs/djson.mjs +270 -0
- package/codecs/int.mjs +2 -1
- package/codecs/json.mjs +7 -2
- package/codecs/k-parse.mjs +36 -9
- package/codecs/k-print.mjs +26 -6
- package/codecs/runtime/codec-sdk.mjs +24 -6
- package/codecs/runtime/pattern-json.mjs +80 -1
- package/codecs/runtime/prefix-codec.mjs +629 -67
- package/codecs/runtime/show-value.mjs +93 -51
- package/codecs/runtime/unicode-string.mjs +29 -6
- package/codecs/show.mjs +24 -6
- package/codecs/utf16.mjs +2 -1
- package/k.mjs +56 -16
- package/kir.mjs +58 -19
- package/kvm.mjs +49 -12
- package/objects/compile.mjs +37 -23
- package/objects/decompile.mjs +40 -10
- package/objects/extract-aliases.mjs +44 -14
- package/objects/inspect.mjs +19 -14
- package/objects/validate.mjs +18 -11
- package/package.json +11 -3
- package/pkir.mjs +772 -0
- package/repl.mjs +92 -23
- package/scripts/build-repl-html.mjs +22 -3
- package/scripts/perf-ieee.mjs +264 -13
- package/scripts/perf-int.mjs +315 -22
- package/scripts/perf-multiplication.mjs +58 -7
- package/scripts/perf-poly.mjs +306 -24
- package/scripts/run-tests.mjs +3 -0
- package/tests/test-access-table.mjs +213 -0
- package/tests/test-codecs.mjs +168 -0
- package/tests/test-pkir.mjs +530 -0
- package/tests/test-polymorphic-tdd.mjs +196 -0
- package/tests/test-repl-html.mjs +16 -2
- package/tests/test-repl.mjs +63 -1
- package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { buildAccessTable } from "../access-table.mjs";
|
|
3
|
+
|
|
4
|
+
console.log("==> Running Access Table Tests (tests/test-access-table.mjs)");
|
|
5
|
+
|
|
6
|
+
// --------------------------------------------------------------------
|
|
7
|
+
// Test 1: Access Table for 'search' on Disparate Schema
|
|
8
|
+
// --------------------------------------------------------------------
|
|
9
|
+
{
|
|
10
|
+
console.log("--> Test 1: Access Table for 'search' on Disparate Schema");
|
|
11
|
+
|
|
12
|
+
// Input Schema S: list of { data, flag, id }
|
|
13
|
+
const S_list = [
|
|
14
|
+
["closed-union", [["cons", 1], ["nil", 2]]],
|
|
15
|
+
["closed-product", [["car", 3], ["cdr", 0]]],
|
|
16
|
+
["closed-product", []],
|
|
17
|
+
["closed-product", [["data", 4], ["flag", 5], ["id", 6]]],
|
|
18
|
+
["any", []],
|
|
19
|
+
["closed-union", [["false", 2], ["true", 2]]],
|
|
20
|
+
["any", []]
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
// Function Pattern P: list of { flag: <true, ...> }
|
|
24
|
+
const P_search = [
|
|
25
|
+
["closed-union", [["cons", 1], ["nil", 2]]],
|
|
26
|
+
["closed-product", [["car", 3], ["cdr", 0]]],
|
|
27
|
+
["closed-product", []],
|
|
28
|
+
["open-product", [["flag", 4]]],
|
|
29
|
+
["open-union", [["true", 2]]],
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const table = buildAccessTable(S_list, P_search, { wordSize: 8 });
|
|
33
|
+
assert.equal(table.stateCount, 7);
|
|
34
|
+
|
|
35
|
+
// 1a: Check list union resolution (State 0)
|
|
36
|
+
const consVariant = table.resolveVariant(0, "cons");
|
|
37
|
+
assert.equal(consVariant.tagId, 0);
|
|
38
|
+
assert.equal(consVariant.branchId, 0);
|
|
39
|
+
assert.equal(consVariant.childState, 1);
|
|
40
|
+
|
|
41
|
+
const nilVariant = table.resolveVariant(0, "nil");
|
|
42
|
+
assert.equal(nilVariant.tagId, 1);
|
|
43
|
+
assert.equal(nilVariant.branchId, 1);
|
|
44
|
+
assert.equal(nilVariant.childState, 2);
|
|
45
|
+
|
|
46
|
+
// 1b: Check cons product resolution (State 1)
|
|
47
|
+
const carField = table.resolveField(1, "car");
|
|
48
|
+
assert.equal(carField.physicalSlot, 0);
|
|
49
|
+
assert.equal(carField.byteOffset, 0);
|
|
50
|
+
assert.equal(carField.childState, 3);
|
|
51
|
+
|
|
52
|
+
const cdrField = table.resolveField(1, "cdr");
|
|
53
|
+
assert.equal(cdrField.physicalSlot, 1);
|
|
54
|
+
assert.equal(cdrField.byteOffset, 8);
|
|
55
|
+
assert.equal(cdrField.childState, 0);
|
|
56
|
+
|
|
57
|
+
// 1c: Check item product resolution (State 3: { data, flag, id })
|
|
58
|
+
// In alphabetical order: data = 0, flag = 1, id = 2
|
|
59
|
+
const flagField = table.resolveField(3, "flag");
|
|
60
|
+
assert.equal(flagField.physicalSlot, 1);
|
|
61
|
+
assert.equal(flagField.byteOffset, 8);
|
|
62
|
+
assert.equal(flagField.skipCount, 1);
|
|
63
|
+
assert.equal(flagField.childState, 5); // points to boolean union
|
|
64
|
+
|
|
65
|
+
// Logical slot access (by numeric slot 0)
|
|
66
|
+
const flagBySlot = table.resolveField(3, 0);
|
|
67
|
+
assert.equal(flagBySlot.byteOffset, 8);
|
|
68
|
+
assert.equal(flagBySlot.childState, 5);
|
|
69
|
+
assert.equal(flagBySlot.skipCount, 1);
|
|
70
|
+
|
|
71
|
+
console.log(" Passed: AccessTable correctly resolves fields and variants.");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// --------------------------------------------------------------------
|
|
75
|
+
// Test 2: Access Table for Multi-Variant Dispatch ('extract_id')
|
|
76
|
+
// --------------------------------------------------------------------
|
|
77
|
+
{
|
|
78
|
+
console.log("--> Test 2: Access Table for Multi-Variant Dispatch");
|
|
79
|
+
|
|
80
|
+
const P_extract = [
|
|
81
|
+
["closed-union", [["click", 1], ["ping", 2], ["sensor", 3]]],
|
|
82
|
+
["open-product", [["id", 4]]],
|
|
83
|
+
["open-product", [["id", 4]]],
|
|
84
|
+
["open-product", [["id", 4]]],
|
|
85
|
+
["any", []]
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
// Click: { active, id } -> id is at physical slot 1
|
|
89
|
+
const S_click = [
|
|
90
|
+
["closed-union", [["click", 1]]],
|
|
91
|
+
["closed-product", [["active", 2], ["id", 3]]],
|
|
92
|
+
["any", []],
|
|
93
|
+
["any", []]
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
// Sensor: { battery_low, id, reading } -> id is at physical slot 1
|
|
97
|
+
const S_sensor = [
|
|
98
|
+
["closed-union", [["sensor", 1]]],
|
|
99
|
+
["closed-product", [["battery_low", 2], ["id", 3], ["reading", 4]]],
|
|
100
|
+
["any", []],
|
|
101
|
+
["any", []],
|
|
102
|
+
["any", []]
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
// Ping: { id } -> id is at physical slot 0
|
|
106
|
+
const S_ping = [
|
|
107
|
+
["closed-union", [["ping", 1]]],
|
|
108
|
+
["closed-product", [["id", 2]]],
|
|
109
|
+
["any", []]
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const tClick = buildAccessTable(S_click, P_extract, { wordSize: 8 });
|
|
113
|
+
const tSensor = buildAccessTable(S_sensor, P_extract, { wordSize: 8 });
|
|
114
|
+
const tPing = buildAccessTable(S_ping, P_extract, { wordSize: 8 });
|
|
115
|
+
|
|
116
|
+
// In click, id is at offset 8 (after active)
|
|
117
|
+
assert.equal(tClick.resolveField(1, "id").byteOffset, 8);
|
|
118
|
+
assert.equal(tClick.resolveField(1, "id").skipCount, 1);
|
|
119
|
+
|
|
120
|
+
// In sensor, id is at offset 8 (after battery_low)
|
|
121
|
+
assert.equal(tSensor.resolveField(1, "id").byteOffset, 8);
|
|
122
|
+
assert.equal(tSensor.resolveField(1, "id").skipCount, 1);
|
|
123
|
+
|
|
124
|
+
// In ping, id is at offset 0
|
|
125
|
+
assert.equal(tPing.resolveField(1, "id").byteOffset, 0);
|
|
126
|
+
assert.equal(tPing.resolveField(1, "id").skipCount, 0);
|
|
127
|
+
|
|
128
|
+
console.log(" Passed: Variable offsets resolved per variant schema.");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// --------------------------------------------------------------------
|
|
132
|
+
// Test 3: Simulating In-Memory Payload Navigation via AccessTable
|
|
133
|
+
// --------------------------------------------------------------------
|
|
134
|
+
{
|
|
135
|
+
console.log("--> Test 3: Simulating In-Memory Payload Navigation");
|
|
136
|
+
|
|
137
|
+
// S: list of { data, flag, id }
|
|
138
|
+
const S = [
|
|
139
|
+
["closed-union", [["cons", 1], ["nil", 2]]],
|
|
140
|
+
["closed-product", [["car", 3], ["cdr", 0]]],
|
|
141
|
+
["closed-product", []],
|
|
142
|
+
["closed-product", [["data", 4], ["flag", 5], ["id", 6]]],
|
|
143
|
+
["any", []],
|
|
144
|
+
["closed-union", [["false", 2], ["true", 2]]],
|
|
145
|
+
["any", []]
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
const P = [
|
|
149
|
+
["closed-union", [["cons", 1], ["nil", 2]]],
|
|
150
|
+
["closed-product", [["car", 3], ["cdr", 0]]],
|
|
151
|
+
["closed-product", []],
|
|
152
|
+
["open-product", [["flag", 4]]],
|
|
153
|
+
["open-union", [["true", 2]]],
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const table = buildAccessTable(S, P, { wordSize: 8 });
|
|
157
|
+
|
|
158
|
+
// Mock in-memory value representation:
|
|
159
|
+
// Node representation in word arrays:
|
|
160
|
+
// Item 1: { data: 'blob1', flag: 'true', id: 'alpha' }
|
|
161
|
+
// Sorted physical layout: slot 0 = data, slot 1 = flag, slot 2 = id
|
|
162
|
+
const item1 = {
|
|
163
|
+
tag: null,
|
|
164
|
+
words: [
|
|
165
|
+
{ text: "blob1" }, // offset 0
|
|
166
|
+
{ tag: 1, text: "true" }, // offset 8 (true is tag 1 in {false:0, true:1})
|
|
167
|
+
{ text: "alpha" } // offset 16
|
|
168
|
+
]
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Cons cell: slot 0 = car, slot 1 = cdr
|
|
172
|
+
const nilCell = { tag: 1, words: [] }; // tag 1 = nil
|
|
173
|
+
const consCell = {
|
|
174
|
+
tag: 0, // tag 0 = cons
|
|
175
|
+
words: [item1, nilCell]
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Navigate using table and word indices (zero string comparisons):
|
|
179
|
+
let state = table.rootState; // 0
|
|
180
|
+
assert.equal(state, 0);
|
|
181
|
+
|
|
182
|
+
// Step 1: Match union at root
|
|
183
|
+
const unionDesc = table.getDescriptor(state);
|
|
184
|
+
const tag = consCell.tag; // 0
|
|
185
|
+
const branch = unionDesc.branchMap[tag]; // 0 (cons)
|
|
186
|
+
assert.equal(branch, 0);
|
|
187
|
+
state = unionDesc.childStateMap[tag]; // state 1 (cons product)
|
|
188
|
+
assert.equal(state, 1);
|
|
189
|
+
|
|
190
|
+
// Step 2: Project .car (slot 0)
|
|
191
|
+
const carMeta = table.resolveField(state, 0);
|
|
192
|
+
assert.equal(carMeta.byteOffset, 0);
|
|
193
|
+
const carNode = consCell.words[carMeta.byteOffset / 8];
|
|
194
|
+
state = carMeta.childState; // state 3 (item product)
|
|
195
|
+
assert.equal(state, 3);
|
|
196
|
+
|
|
197
|
+
// Step 3: Project .flag (slot 0 in P's view of car)
|
|
198
|
+
const flagMeta = table.resolveField(state, 0);
|
|
199
|
+
assert.equal(flagMeta.byteOffset, 8); // correctly jumps to slot 1!
|
|
200
|
+
const flagVal = carNode.words[flagMeta.byteOffset / 8];
|
|
201
|
+
state = flagMeta.childState; // state 5 (boolean union)
|
|
202
|
+
assert.equal(state, 5);
|
|
203
|
+
|
|
204
|
+
// Step 4: Check if flag is 'true'
|
|
205
|
+
const boolDesc = table.getDescriptor(state);
|
|
206
|
+
const flagTag = flagVal.tag; // 1 ('true')
|
|
207
|
+
const flagBranch = boolDesc.branchMap[flagTag];
|
|
208
|
+
assert.equal(flagBranch, 0); // matches P's only branch ('true')
|
|
209
|
+
|
|
210
|
+
console.log(" Passed: Zero-string navigation simulation successful.");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log("\n==> All Access Table Tests Passed Successfully!");
|
package/tests/test-codecs.mjs
CHANGED
|
@@ -8,10 +8,15 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
import * as SDK from "../codecs/runtime/codec-sdk.mjs";
|
|
9
9
|
import * as IntCodec from "../codecs/int.mjs";
|
|
10
10
|
import * as JsonCodec from "../codecs/json.mjs";
|
|
11
|
+
import * as DjsonCodec from "../codecs/djson.mjs";
|
|
11
12
|
import * as Utf8Codec from "../codecs/utf8.mjs";
|
|
12
13
|
import * as Utf16Codec from "../codecs/utf16.mjs";
|
|
13
14
|
import * as IeeeCodec from "../codecs/ieee.mjs";
|
|
14
15
|
import * as UnitCodec from "../codecs/unit.mjs";
|
|
16
|
+
import {
|
|
17
|
+
valueToK,
|
|
18
|
+
valueWithEnvelopeToK
|
|
19
|
+
} from "../codecs/runtime/show-value.mjs";
|
|
15
20
|
|
|
16
21
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
22
|
|
|
@@ -49,6 +54,7 @@ console.log("2. Verifying in-memory parse -> enveloped value (P, v)...");
|
|
|
49
54
|
for (const [name, codec, sampleInput] of [
|
|
50
55
|
["int.mjs", IntCodec, "42"],
|
|
51
56
|
["json.mjs", JsonCodec, '{"x": 1}'],
|
|
57
|
+
["djson.mjs", DjsonCodec, '{"x": 1, "arr": [true, null, "hi"]}'],
|
|
52
58
|
["utf8.mjs", Utf8Codec, "hello"],
|
|
53
59
|
["utf16.mjs", Utf16Codec, "hello"],
|
|
54
60
|
["ieee.mjs", IeeeCodec, "3.14"],
|
|
@@ -68,6 +74,7 @@ console.log("3. Verifying CLI --help flag...");
|
|
|
68
74
|
for (const relPath of [
|
|
69
75
|
"codecs/int.mjs",
|
|
70
76
|
"codecs/json.mjs",
|
|
77
|
+
"codecs/djson.mjs",
|
|
71
78
|
"codecs/utf8.mjs",
|
|
72
79
|
"codecs/utf16.mjs",
|
|
73
80
|
"codecs/ieee.mjs",
|
|
@@ -111,6 +118,24 @@ console.log("4. Verifying CLI --parse and --print pipelines...");
|
|
|
111
118
|
assert.deepEqual(JSON.parse(printRes.stdout.toString("utf8")), JSON.parse(jsonInput));
|
|
112
119
|
}
|
|
113
120
|
|
|
121
|
+
// djson.mjs: dynamic JSON AST with singleton pattern
|
|
122
|
+
{
|
|
123
|
+
const djsonInput = JSON.stringify({
|
|
124
|
+
name: "k",
|
|
125
|
+
count: 42.5,
|
|
126
|
+
tags: ["fast", "typed"],
|
|
127
|
+
meta: { active: true, extra: null }
|
|
128
|
+
});
|
|
129
|
+
const parseRes = runCLI("codecs/djson.mjs", ["--parse"], djsonInput);
|
|
130
|
+
assert.equal(parseRes.status, 0);
|
|
131
|
+
const printRes = runCLI("codecs/djson.mjs", ["--print"], parseRes.stdout);
|
|
132
|
+
assert.equal(printRes.status, 0);
|
|
133
|
+
assert.deepEqual(
|
|
134
|
+
JSON.parse(printRes.stdout.toString("utf8")),
|
|
135
|
+
JSON.parse(djsonInput)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
114
139
|
// utf8.mjs: unicode text
|
|
115
140
|
{
|
|
116
141
|
const utf8Input = "Hello 🌍, ąęćłó! \nSecond line.";
|
|
@@ -183,4 +208,147 @@ console.log("6. Verifying pattern derivation via k type system (patternFromFilte
|
|
|
183
208
|
assert.throws(() => SDK.patternFromFilter(".field"), /Main expression must be a filter or a type name/);
|
|
184
209
|
}
|
|
185
210
|
|
|
211
|
+
// 7. Lazy Prefix-Free Wire Decoding
|
|
212
|
+
console.log("7. Verifying lazy prefix-free wire decoding...");
|
|
213
|
+
{
|
|
214
|
+
assert.equal(typeof SDK.decodeWireLazy, "function");
|
|
215
|
+
|
|
216
|
+
// Lazy decoding should instantiate root in sub-millisecond time
|
|
217
|
+
const sampleStr = "a".repeat(20000);
|
|
218
|
+
const parsedStr = Utf8Codec.parse(sampleStr);
|
|
219
|
+
const wireStr = SDK.encodeToWire(parsedStr, parsedStr.pattern);
|
|
220
|
+
|
|
221
|
+
const t0 = performance.now();
|
|
222
|
+
const decodedLazy = SDK.decodeWire(wireStr);
|
|
223
|
+
const constructMs = performance.now() - t0;
|
|
224
|
+
assert(
|
|
225
|
+
constructMs < 50,
|
|
226
|
+
`Lazy decode took ${constructMs}ms (expected <50ms)`
|
|
227
|
+
);
|
|
228
|
+
assert.equal(decodedLazy.value.tag, "cons");
|
|
229
|
+
|
|
230
|
+
// On-demand traversal through print()
|
|
231
|
+
const printedStr = Utf8Codec.print(decodedLazy.value);
|
|
232
|
+
assert.equal(printedStr, sampleStr);
|
|
233
|
+
|
|
234
|
+
// Lazy list of integers
|
|
235
|
+
const ints = Array.from({ length: 5000 }, (_, i) => i).join(",");
|
|
236
|
+
const sampleList = `[${ints}]`;
|
|
237
|
+
const parsedList = IntCodec.parse(sampleList);
|
|
238
|
+
const wireList = SDK.encodeToWire(parsedList, parsedList.pattern);
|
|
239
|
+
const decodedList = SDK.decodeWireLazy(wireList);
|
|
240
|
+
assert.equal(IntCodec.print(decodedList.value), sampleList);
|
|
241
|
+
|
|
242
|
+
// Verify stack-safe toJSON on deep variant chains
|
|
243
|
+
let deepVal = SDK.Value.product({});
|
|
244
|
+
for (let i = 0; i < 15000; i++) {
|
|
245
|
+
deepVal = SDK.Value.variant("step", deepVal);
|
|
246
|
+
}
|
|
247
|
+
const json = deepVal.toJSON();
|
|
248
|
+
assert(json && typeof json === "object");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// 8. High-Speed Direct Bitstream String Decoding & Bounded Rendering
|
|
252
|
+
console.log("8. Verifying fast string decoding & bounded rendering...");
|
|
253
|
+
{
|
|
254
|
+
const testStrings = [
|
|
255
|
+
"hello world",
|
|
256
|
+
"Zażółć gęślą jaźń",
|
|
257
|
+
"Unicode: 🚀🎉 𝒦-lang π ≈ 3.14159",
|
|
258
|
+
"a".repeat(50000),
|
|
259
|
+
""
|
|
260
|
+
];
|
|
261
|
+
|
|
262
|
+
for (const s of testStrings) {
|
|
263
|
+
const parsed = Utf8Codec.parse(s);
|
|
264
|
+
const wire = SDK.encodeToWire(parsed, parsed.pattern);
|
|
265
|
+
const decoded = SDK.decodeWire(wire);
|
|
266
|
+
const restored = Utf8Codec.print(decoded.value);
|
|
267
|
+
assert.equal(
|
|
268
|
+
restored,
|
|
269
|
+
s,
|
|
270
|
+
`String round-trip failed for "${s.slice(0, 20)}..."`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Fast decoding performance: 50,000 ASCII chars in < 500ms
|
|
275
|
+
const bigAscii = "x".repeat(50000);
|
|
276
|
+
const bigParsed = Utf8Codec.parse(bigAscii);
|
|
277
|
+
const bigWire = SDK.encodeToWire(bigParsed, bigParsed.pattern);
|
|
278
|
+
const tStart = performance.now();
|
|
279
|
+
const bigDecoded = SDK.decodeWire(bigWire);
|
|
280
|
+
const bigRestored = Utf8Codec.print(bigDecoded.value);
|
|
281
|
+
const elapsed = performance.now() - tStart;
|
|
282
|
+
assert.equal(bigRestored.length, 50000);
|
|
283
|
+
assert(
|
|
284
|
+
elapsed < 500,
|
|
285
|
+
`Fast string decoding took ${elapsed}ms (expected < 500ms)`
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Bounded rendering: valueToK with limit
|
|
289
|
+
let deepVal = SDK.Value.product({});
|
|
290
|
+
for (let i = 0; i < 1000; i++) {
|
|
291
|
+
deepVal = SDK.Value.variant("item", deepVal);
|
|
292
|
+
}
|
|
293
|
+
const preview = valueToK(deepVal, 30);
|
|
294
|
+
assert(preview.endsWith("..."), "Output should end with '...'");
|
|
295
|
+
assert(preview.length <= 35, "Preview length should stay bounded");
|
|
296
|
+
|
|
297
|
+
// valueWithEnvelopeToK with limit
|
|
298
|
+
const enveloped = SDK.withPattern(deepVal, [["any", []]]);
|
|
299
|
+
const envPreview = valueWithEnvelopeToK(enveloped, 30);
|
|
300
|
+
assert(envPreview.includes("..."), "Preview should contain '...'");
|
|
301
|
+
|
|
302
|
+
// Ensure early termination: evaluating deep chain with limit 20
|
|
303
|
+
let hugeVal = SDK.Value.product({});
|
|
304
|
+
for (let i = 0; i < 50000; i++) {
|
|
305
|
+
hugeVal = SDK.Value.variant("v", hugeVal);
|
|
306
|
+
}
|
|
307
|
+
const tEarly0 = performance.now();
|
|
308
|
+
const earlyPreview = valueToK(hugeVal, 20);
|
|
309
|
+
const tEarlyElapsed = performance.now() - tEarly0;
|
|
310
|
+
assert(earlyPreview.endsWith("..."));
|
|
311
|
+
assert(
|
|
312
|
+
tEarlyElapsed < 50,
|
|
313
|
+
`Early termination took ${tEarlyElapsed}ms (expected < 50ms)`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 9. DJSON Singleton Pattern Invariant & Round-Trip
|
|
318
|
+
console.log("9. Verifying DJSON singleton pattern invariant...");
|
|
319
|
+
{
|
|
320
|
+
assert(Array.isArray(DjsonCodec.DJSON_PATTERN));
|
|
321
|
+
assert.equal(DjsonCodec.DJSON_PATTERN.length, 29);
|
|
322
|
+
|
|
323
|
+
const samples = [
|
|
324
|
+
null,
|
|
325
|
+
true,
|
|
326
|
+
false,
|
|
327
|
+
123.456,
|
|
328
|
+
"Hello world!",
|
|
329
|
+
[],
|
|
330
|
+
[1, "text", false, null],
|
|
331
|
+
{},
|
|
332
|
+
{ key: "value", nested: [1, 2, { deep: true }] }
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
for (const sample of samples) {
|
|
336
|
+
const jsonStr = JSON.stringify(sample);
|
|
337
|
+
const enveloped = DjsonCodec.parse(jsonStr);
|
|
338
|
+
assert.strictEqual(
|
|
339
|
+
enveloped.pattern,
|
|
340
|
+
DjsonCodec.DJSON_PATTERN,
|
|
341
|
+
"Every DJSON value must share the exact singleton pattern"
|
|
342
|
+
);
|
|
343
|
+
const wire = SDK.encodeToWire(enveloped, enveloped.pattern);
|
|
344
|
+
const decoded = SDK.decodeWire(wire);
|
|
345
|
+
const restored = DjsonCodec.print(decoded.value);
|
|
346
|
+
assert.deepEqual(
|
|
347
|
+
JSON.parse(restored),
|
|
348
|
+
sample,
|
|
349
|
+
`DJSON round-trip mismatch for: ${jsonStr}`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
186
354
|
console.log("OK - All codec tests passed.");
|