@2702rebels/wpidata 1.0.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/LICENSE +28 -0
- package/README.md +5 -0
- package/dist/abstractions.cjs +0 -0
- package/dist/abstractions.d.cts +246 -0
- package/dist/abstractions.d.cts.map +1 -0
- package/dist/abstractions.d.mts +246 -0
- package/dist/abstractions.d.mts.map +1 -0
- package/dist/abstractions.mjs +1 -0
- package/dist/formats/json.cjs +32 -0
- package/dist/formats/json.d.cts +14 -0
- package/dist/formats/json.d.cts.map +1 -0
- package/dist/formats/json.d.mts +14 -0
- package/dist/formats/json.d.mts.map +1 -0
- package/dist/formats/json.mjs +33 -0
- package/dist/formats/json.mjs.map +1 -0
- package/dist/formats/msgpack.cjs +30 -0
- package/dist/formats/msgpack.d.cts +14 -0
- package/dist/formats/msgpack.d.cts.map +1 -0
- package/dist/formats/msgpack.d.mts +14 -0
- package/dist/formats/msgpack.d.mts.map +1 -0
- package/dist/formats/msgpack.mjs +31 -0
- package/dist/formats/msgpack.mjs.map +1 -0
- package/dist/formats/protobuf.cjs +130 -0
- package/dist/formats/protobuf.d.cts +68 -0
- package/dist/formats/protobuf.d.cts.map +1 -0
- package/dist/formats/protobuf.d.mts +68 -0
- package/dist/formats/protobuf.d.mts.map +1 -0
- package/dist/formats/protobuf.mjs +128 -0
- package/dist/formats/protobuf.mjs.map +1 -0
- package/dist/formats/struct.cjs +593 -0
- package/dist/formats/struct.d.cts +134 -0
- package/dist/formats/struct.d.cts.map +1 -0
- package/dist/formats/struct.d.mts +134 -0
- package/dist/formats/struct.d.mts.map +1 -0
- package/dist/formats/struct.mjs +591 -0
- package/dist/formats/struct.mjs.map +1 -0
- package/dist/sink.cjs +360 -0
- package/dist/sink.d.cts +93 -0
- package/dist/sink.d.cts.map +1 -0
- package/dist/sink.d.mts +93 -0
- package/dist/sink.d.mts.map +1 -0
- package/dist/sink.mjs +361 -0
- package/dist/sink.mjs.map +1 -0
- package/dist/types/protobuf.cjs +0 -0
- package/dist/types/protobuf.d.cts +302 -0
- package/dist/types/protobuf.d.cts.map +1 -0
- package/dist/types/protobuf.d.mts +302 -0
- package/dist/types/protobuf.d.mts.map +1 -0
- package/dist/types/protobuf.mjs +1 -0
- package/dist/types/sendable.cjs +0 -0
- package/dist/types/sendable.d.cts +225 -0
- package/dist/types/sendable.d.cts.map +1 -0
- package/dist/types/sendable.d.mts +225 -0
- package/dist/types/sendable.d.mts.map +1 -0
- package/dist/types/sendable.mjs +1 -0
- package/dist/types/struct.cjs +0 -0
- package/dist/types/struct.d.cts +304 -0
- package/dist/types/struct.d.cts.map +1 -0
- package/dist/types/struct.d.mts +304 -0
- package/dist/types/struct.d.mts.map +1 -0
- package/dist/types/struct.mjs +1 -0
- package/dist/utils.cjs +140 -0
- package/dist/utils.d.cts +40 -0
- package/dist/utils.d.cts.map +1 -0
- package/dist/utils.d.mts +40 -0
- package/dist/utils.d.mts.map +1 -0
- package/dist/utils.mjs +135 -0
- package/dist/utils.mjs.map +1 -0
- package/package.json +51 -0
- package/src/abstractions.ts +308 -0
- package/src/formats/json.ts +53 -0
- package/src/formats/msgpack.ts +42 -0
- package/src/formats/protobuf.ts +213 -0
- package/src/formats/struct.test.ts +814 -0
- package/src/formats/struct.ts +992 -0
- package/src/sink.ts +611 -0
- package/src/types/protobuf.ts +334 -0
- package/src/types/sendable.ts +244 -0
- package/src/types/struct.ts +333 -0
- package/src/utils.ts +241 -0
|
@@ -0,0 +1,814 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { pack, StructRepository, unpack } from "./struct";
|
|
4
|
+
|
|
5
|
+
import type { StructDescriptor } from "./struct";
|
|
6
|
+
|
|
7
|
+
const encoder = new TextEncoder();
|
|
8
|
+
const encode = (s: string) => encoder.encode(s);
|
|
9
|
+
|
|
10
|
+
describe("schema parser", () => {
|
|
11
|
+
const repository = new StructRepository();
|
|
12
|
+
|
|
13
|
+
it("parses empty schema", () => {
|
|
14
|
+
expect(repository.add("", encode(""))).toHaveProperty("fields", []);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("parses trivial schema", () => {
|
|
18
|
+
expect(repository.add("", encode(";"))).toHaveProperty("fields", []);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("parses simple declaration", () => {
|
|
22
|
+
expect(repository.add("", encode("int32 a"))).toEqual<StructDescriptor>({
|
|
23
|
+
name: "",
|
|
24
|
+
size: 4,
|
|
25
|
+
fields: [
|
|
26
|
+
{
|
|
27
|
+
identifier: "a",
|
|
28
|
+
type: "int32",
|
|
29
|
+
offset: 0,
|
|
30
|
+
size: 4,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("parses array declaration", () => {
|
|
37
|
+
expect(repository.add("", encode("int32 a[2]"))).toEqual<StructDescriptor>({
|
|
38
|
+
name: "",
|
|
39
|
+
size: 8,
|
|
40
|
+
fields: [
|
|
41
|
+
{
|
|
42
|
+
identifier: "a",
|
|
43
|
+
type: "int32",
|
|
44
|
+
offset: 0,
|
|
45
|
+
size: 4,
|
|
46
|
+
arraySize: 2,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("parses bit-field declaration", () => {
|
|
53
|
+
expect(repository.add("", encode("int32 a:2"))).toEqual<StructDescriptor>({
|
|
54
|
+
name: "",
|
|
55
|
+
size: 4,
|
|
56
|
+
fields: [
|
|
57
|
+
{
|
|
58
|
+
identifier: "a",
|
|
59
|
+
type: "int32",
|
|
60
|
+
offset: 0,
|
|
61
|
+
size: 4,
|
|
62
|
+
bitWidth: 2,
|
|
63
|
+
bitShift: 0,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("parses bit-field declarations with compatible types", () => {
|
|
70
|
+
expect(repository.add("", encode("int32 a:2;uint32 b:30"))).toEqual<StructDescriptor>({
|
|
71
|
+
name: "",
|
|
72
|
+
size: 4,
|
|
73
|
+
fields: [
|
|
74
|
+
{
|
|
75
|
+
identifier: "a",
|
|
76
|
+
type: "int32",
|
|
77
|
+
offset: 0,
|
|
78
|
+
size: 4,
|
|
79
|
+
bitWidth: 2,
|
|
80
|
+
bitShift: 0,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
identifier: "b",
|
|
84
|
+
type: "uint32",
|
|
85
|
+
offset: 0,
|
|
86
|
+
size: 4,
|
|
87
|
+
bitWidth: 30,
|
|
88
|
+
bitShift: 2,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("parses bit-field declarations with incompatible types", () => {
|
|
95
|
+
expect(repository.add("", encode("int32 a:2;int16 b:2"))).toEqual<StructDescriptor>({
|
|
96
|
+
name: "",
|
|
97
|
+
size: 6,
|
|
98
|
+
fields: [
|
|
99
|
+
{
|
|
100
|
+
identifier: "a",
|
|
101
|
+
type: "int32",
|
|
102
|
+
offset: 0,
|
|
103
|
+
size: 4,
|
|
104
|
+
bitWidth: 2,
|
|
105
|
+
bitShift: 0,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
identifier: "b",
|
|
109
|
+
type: "int16",
|
|
110
|
+
offset: 4,
|
|
111
|
+
size: 2,
|
|
112
|
+
bitWidth: 2,
|
|
113
|
+
bitShift: 0,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("parses bit-field declarations with overflow", () => {
|
|
120
|
+
expect(repository.add("", encode("int8 a:4;int8 b:5"))).toEqual<StructDescriptor>({
|
|
121
|
+
name: "",
|
|
122
|
+
size: 2,
|
|
123
|
+
fields: [
|
|
124
|
+
{
|
|
125
|
+
identifier: "a",
|
|
126
|
+
type: "int8",
|
|
127
|
+
offset: 0,
|
|
128
|
+
size: 1,
|
|
129
|
+
bitWidth: 4,
|
|
130
|
+
bitShift: 0,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
identifier: "b",
|
|
134
|
+
type: "int8",
|
|
135
|
+
offset: 1,
|
|
136
|
+
size: 1,
|
|
137
|
+
bitWidth: 5,
|
|
138
|
+
bitShift: 0,
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("parses bit-field declarations with boolean and 16-bit", () => {
|
|
145
|
+
expect(repository.add("", encode("bool a:1;int16 b:5"))).toEqual<StructDescriptor>({
|
|
146
|
+
name: "",
|
|
147
|
+
size: 3,
|
|
148
|
+
fields: [
|
|
149
|
+
{
|
|
150
|
+
identifier: "a",
|
|
151
|
+
type: "bool",
|
|
152
|
+
offset: 0,
|
|
153
|
+
size: 1,
|
|
154
|
+
bitWidth: 1,
|
|
155
|
+
bitShift: 0,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
identifier: "b",
|
|
159
|
+
type: "int16",
|
|
160
|
+
offset: 1,
|
|
161
|
+
size: 2,
|
|
162
|
+
bitWidth: 5,
|
|
163
|
+
bitShift: 0,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("parses bit-field declarations with two booleans and 16-bit", () => {
|
|
170
|
+
expect(repository.add("", encode("bool a:1;bool b:1;int16 c:5"))).toEqual<StructDescriptor>({
|
|
171
|
+
name: "",
|
|
172
|
+
size: 3,
|
|
173
|
+
fields: [
|
|
174
|
+
{
|
|
175
|
+
identifier: "a",
|
|
176
|
+
type: "bool",
|
|
177
|
+
offset: 0,
|
|
178
|
+
size: 1,
|
|
179
|
+
bitWidth: 1,
|
|
180
|
+
bitShift: 0,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
identifier: "b",
|
|
184
|
+
type: "bool",
|
|
185
|
+
offset: 0,
|
|
186
|
+
size: 1,
|
|
187
|
+
bitWidth: 1,
|
|
188
|
+
bitShift: 1,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
identifier: "c",
|
|
192
|
+
type: "int16",
|
|
193
|
+
offset: 1,
|
|
194
|
+
size: 2,
|
|
195
|
+
bitWidth: 5,
|
|
196
|
+
bitShift: 0,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("parses bit-field declarations with 16-bit and boolean", () => {
|
|
203
|
+
expect(repository.add("", encode("int16 a:15;bool b:1"))).toEqual<StructDescriptor>({
|
|
204
|
+
name: "",
|
|
205
|
+
size: 2,
|
|
206
|
+
fields: [
|
|
207
|
+
{
|
|
208
|
+
identifier: "a",
|
|
209
|
+
type: "int16",
|
|
210
|
+
offset: 0,
|
|
211
|
+
size: 2,
|
|
212
|
+
bitWidth: 15,
|
|
213
|
+
bitShift: 0,
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
identifier: "b",
|
|
217
|
+
type: "bool",
|
|
218
|
+
offset: 0,
|
|
219
|
+
size: 2,
|
|
220
|
+
bitWidth: 1,
|
|
221
|
+
bitShift: 15,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("parses bit-field declarations with 16-bit and boolean with overflow", () => {
|
|
228
|
+
expect(repository.add("", encode("int16 a:16;bool b:1"))).toEqual<StructDescriptor>({
|
|
229
|
+
name: "",
|
|
230
|
+
size: 3,
|
|
231
|
+
fields: [
|
|
232
|
+
{
|
|
233
|
+
identifier: "a",
|
|
234
|
+
type: "int16",
|
|
235
|
+
offset: 0,
|
|
236
|
+
size: 2,
|
|
237
|
+
bitWidth: 16,
|
|
238
|
+
bitShift: 0,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
identifier: "b",
|
|
242
|
+
type: "bool",
|
|
243
|
+
offset: 2,
|
|
244
|
+
size: 1,
|
|
245
|
+
bitWidth: 1,
|
|
246
|
+
bitShift: 0,
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("parses bit-field declarations with 16-bit, booleans and 16-bit", () => {
|
|
253
|
+
expect(repository.add("", encode("int16 a:2;bool b:1; bool c:1; uint16 d:5"))).toEqual<StructDescriptor>({
|
|
254
|
+
name: "",
|
|
255
|
+
size: 2,
|
|
256
|
+
fields: [
|
|
257
|
+
{
|
|
258
|
+
identifier: "a",
|
|
259
|
+
type: "int16",
|
|
260
|
+
offset: 0,
|
|
261
|
+
size: 2,
|
|
262
|
+
bitWidth: 2,
|
|
263
|
+
bitShift: 0,
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
identifier: "b",
|
|
267
|
+
type: "bool",
|
|
268
|
+
offset: 0,
|
|
269
|
+
size: 2,
|
|
270
|
+
bitWidth: 1,
|
|
271
|
+
bitShift: 2,
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
identifier: "c",
|
|
275
|
+
type: "bool",
|
|
276
|
+
offset: 0,
|
|
277
|
+
size: 2,
|
|
278
|
+
bitWidth: 1,
|
|
279
|
+
bitShift: 3,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
identifier: "d",
|
|
283
|
+
type: "uint16",
|
|
284
|
+
offset: 0,
|
|
285
|
+
size: 2,
|
|
286
|
+
bitWidth: 5,
|
|
287
|
+
bitShift: 4,
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("parses bit-field declarations with 64-bit", () => {
|
|
294
|
+
expect(repository.add("", encode("int64 a:16;int64 b:32;int64 c:48"))).toEqual<StructDescriptor>({
|
|
295
|
+
name: "",
|
|
296
|
+
size: 16,
|
|
297
|
+
fields: [
|
|
298
|
+
{
|
|
299
|
+
identifier: "a",
|
|
300
|
+
type: "int64",
|
|
301
|
+
offset: 0,
|
|
302
|
+
size: 8,
|
|
303
|
+
bitWidth: 16,
|
|
304
|
+
bitShift: 0,
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
identifier: "b",
|
|
308
|
+
type: "int64",
|
|
309
|
+
offset: 0,
|
|
310
|
+
size: 8,
|
|
311
|
+
bitWidth: 32,
|
|
312
|
+
bitShift: 16,
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
identifier: "c",
|
|
316
|
+
type: "int64",
|
|
317
|
+
offset: 8,
|
|
318
|
+
size: 8,
|
|
319
|
+
bitWidth: 48,
|
|
320
|
+
bitShift: 0,
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("parses bit-field declarations with 64-bit and booleans", () => {
|
|
327
|
+
expect(repository.add("", encode("int64 a:31;int64 b:32;bool x:1;bool y:1;int64 c:48"))).toEqual<StructDescriptor>({
|
|
328
|
+
name: "",
|
|
329
|
+
size: 17,
|
|
330
|
+
fields: [
|
|
331
|
+
{
|
|
332
|
+
identifier: "a",
|
|
333
|
+
type: "int64",
|
|
334
|
+
offset: 0,
|
|
335
|
+
size: 8,
|
|
336
|
+
bitWidth: 31,
|
|
337
|
+
bitShift: 0,
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
identifier: "b",
|
|
341
|
+
type: "int64",
|
|
342
|
+
offset: 0,
|
|
343
|
+
size: 8,
|
|
344
|
+
bitWidth: 32,
|
|
345
|
+
bitShift: 31,
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
identifier: "x",
|
|
349
|
+
type: "bool",
|
|
350
|
+
offset: 0,
|
|
351
|
+
size: 8,
|
|
352
|
+
bitWidth: 1,
|
|
353
|
+
bitShift: 63,
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
identifier: "y",
|
|
357
|
+
type: "bool",
|
|
358
|
+
offset: 8,
|
|
359
|
+
size: 1,
|
|
360
|
+
bitWidth: 1,
|
|
361
|
+
bitShift: 0,
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
identifier: "c",
|
|
365
|
+
type: "int64",
|
|
366
|
+
offset: 9,
|
|
367
|
+
size: 8,
|
|
368
|
+
bitWidth: 48,
|
|
369
|
+
bitShift: 0,
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("parses bit-field declarations with 64-bit and booleans fit tightly", () => {
|
|
376
|
+
expect(repository.add("", encode("int64 a:31;int64 b:31;bool x:1;bool y:1;int64 c:48"))).toEqual<StructDescriptor>({
|
|
377
|
+
name: "",
|
|
378
|
+
size: 16,
|
|
379
|
+
fields: [
|
|
380
|
+
{
|
|
381
|
+
identifier: "a",
|
|
382
|
+
type: "int64",
|
|
383
|
+
offset: 0,
|
|
384
|
+
size: 8,
|
|
385
|
+
bitWidth: 31,
|
|
386
|
+
bitShift: 0,
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
identifier: "b",
|
|
390
|
+
type: "int64",
|
|
391
|
+
offset: 0,
|
|
392
|
+
size: 8,
|
|
393
|
+
bitWidth: 31,
|
|
394
|
+
bitShift: 31,
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
identifier: "x",
|
|
398
|
+
type: "bool",
|
|
399
|
+
offset: 0,
|
|
400
|
+
size: 8,
|
|
401
|
+
bitWidth: 1,
|
|
402
|
+
bitShift: 62,
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
identifier: "y",
|
|
406
|
+
type: "bool",
|
|
407
|
+
offset: 0,
|
|
408
|
+
size: 8,
|
|
409
|
+
bitWidth: 1,
|
|
410
|
+
bitShift: 63,
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
identifier: "c",
|
|
414
|
+
type: "int64",
|
|
415
|
+
offset: 8,
|
|
416
|
+
size: 8,
|
|
417
|
+
bitWidth: 48,
|
|
418
|
+
bitShift: 0,
|
|
419
|
+
},
|
|
420
|
+
],
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("parses enum declaration", () => {
|
|
425
|
+
expect(repository.add("", encode("enum {x=1} int32 a;"))).toEqual<StructDescriptor>({
|
|
426
|
+
name: "",
|
|
427
|
+
size: 4,
|
|
428
|
+
fields: [
|
|
429
|
+
{
|
|
430
|
+
identifier: "a",
|
|
431
|
+
type: "int32",
|
|
432
|
+
offset: 0,
|
|
433
|
+
size: 4,
|
|
434
|
+
enum: new Map<number, string>([[1, "x"]]),
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it("parses enum declaration without keyword", () => {
|
|
441
|
+
expect(repository.add("", encode("{x=1} int32 a;"))).toEqual<StructDescriptor>({
|
|
442
|
+
name: "",
|
|
443
|
+
size: 4,
|
|
444
|
+
fields: [
|
|
445
|
+
{
|
|
446
|
+
identifier: "a",
|
|
447
|
+
type: "int32",
|
|
448
|
+
offset: 0,
|
|
449
|
+
size: 4,
|
|
450
|
+
enum: new Map<number, string>([[1, "x"]]),
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it("parses enum declaration with no values", () => {
|
|
457
|
+
expect(repository.add("", encode("{} int32 a;"))).toEqual<StructDescriptor>({
|
|
458
|
+
name: "",
|
|
459
|
+
size: 4,
|
|
460
|
+
fields: [
|
|
461
|
+
{
|
|
462
|
+
identifier: "a",
|
|
463
|
+
type: "int32",
|
|
464
|
+
offset: 0,
|
|
465
|
+
size: 4,
|
|
466
|
+
},
|
|
467
|
+
],
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("parses enum declaration with multiple values", () => {
|
|
472
|
+
expect(repository.add("", encode("{x=1,y=-2} int32 a;"))).toEqual<StructDescriptor>({
|
|
473
|
+
name: "",
|
|
474
|
+
size: 4,
|
|
475
|
+
fields: [
|
|
476
|
+
{
|
|
477
|
+
identifier: "a",
|
|
478
|
+
type: "int32",
|
|
479
|
+
offset: 0,
|
|
480
|
+
size: 4,
|
|
481
|
+
enum: new Map<number, string>([
|
|
482
|
+
[1, "x"],
|
|
483
|
+
[-2, "y"],
|
|
484
|
+
]),
|
|
485
|
+
},
|
|
486
|
+
],
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("parses enum declaration with trailing comma", () => {
|
|
491
|
+
expect(repository.add("", encode("{x=1,y=2,} int32 a;"))).toEqual<StructDescriptor>({
|
|
492
|
+
name: "",
|
|
493
|
+
size: 4,
|
|
494
|
+
fields: [
|
|
495
|
+
{
|
|
496
|
+
identifier: "a",
|
|
497
|
+
type: "int32",
|
|
498
|
+
offset: 0,
|
|
499
|
+
size: 4,
|
|
500
|
+
enum: new Map<number, string>([
|
|
501
|
+
[1, "x"],
|
|
502
|
+
[2, "y"],
|
|
503
|
+
]),
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it("parses multiple declarations", () => {
|
|
510
|
+
expect(repository.add("", encode("int32 a; int16 b"))).toEqual<StructDescriptor>({
|
|
511
|
+
name: "",
|
|
512
|
+
size: 6,
|
|
513
|
+
fields: [
|
|
514
|
+
{
|
|
515
|
+
identifier: "a",
|
|
516
|
+
type: "int32",
|
|
517
|
+
offset: 0,
|
|
518
|
+
size: 4,
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
identifier: "b",
|
|
522
|
+
type: "int16",
|
|
523
|
+
offset: 4,
|
|
524
|
+
size: 2,
|
|
525
|
+
},
|
|
526
|
+
],
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it("parses nested type with unresolved dependency", () => {
|
|
531
|
+
expect(repository.add("typeA", encode("typeB b"))).toEqual<StructDescriptor>({
|
|
532
|
+
name: "typeA",
|
|
533
|
+
size: 0,
|
|
534
|
+
fields: [
|
|
535
|
+
{
|
|
536
|
+
identifier: "b",
|
|
537
|
+
type: "ref",
|
|
538
|
+
typeRef: "typeB",
|
|
539
|
+
offset: -1,
|
|
540
|
+
size: 0,
|
|
541
|
+
},
|
|
542
|
+
],
|
|
543
|
+
unresolved: new Set(["typeB"]),
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it("parses nested type and resolves dependency", () => {
|
|
548
|
+
expect(() => {
|
|
549
|
+
repository.add("typeA", encode("typeB b"));
|
|
550
|
+
repository.add("typeB", encode("int32 a"));
|
|
551
|
+
return repository.descriptors.get("typeA");
|
|
552
|
+
}).not.toHaveProperty("unresolved");
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it("throws on duplicate field name", () => {
|
|
556
|
+
expect(() => repository.add("", encode("int16 a; int8 a"))).toThrow(
|
|
557
|
+
`Failed to parse schema: duplicate 'a' field declaration`
|
|
558
|
+
);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it("throws on mixed array and bit-field declaration", () => {
|
|
562
|
+
expect(() => repository.add("", encode("int32 a[1]:2"))).toThrow(
|
|
563
|
+
`Failed to parse schema: invalid declaration 'int32 a[1]:2'`
|
|
564
|
+
);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it("throws on array declaration without size", () => {
|
|
568
|
+
expect(() => repository.add("", encode("int32 a[]"))).toThrow(
|
|
569
|
+
`Failed to parse schema: invalid declaration 'int32 a[]'`
|
|
570
|
+
);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it("throws on array declaration with zero size", () => {
|
|
574
|
+
expect(() => repository.add("", encode("int32 a[0]"))).toThrow(
|
|
575
|
+
`Failed to parse schema: invalid array size in 'a' field declaration`
|
|
576
|
+
);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("throws on array declaration without name", () => {
|
|
580
|
+
expect(() => repository.add("", encode("int32 [2]"))).toThrow(
|
|
581
|
+
`Failed to parse schema: invalid declaration 'int32 [2]'`
|
|
582
|
+
);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it("throws on bit-field declaration without name", () => {
|
|
586
|
+
expect(() => repository.add("", encode("int32 :2"))).toThrow(
|
|
587
|
+
`Failed to parse schema: invalid declaration 'int32 :2'`
|
|
588
|
+
);
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it("throws on bit-field declaration with negative width", () => {
|
|
592
|
+
expect(() => repository.add("", encode("int32 a:-1"))).toThrow(
|
|
593
|
+
`Failed to parse schema: invalid declaration 'int32 a:-1'`
|
|
594
|
+
);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it("throws on bit-field declaration with zero width", () => {
|
|
598
|
+
expect(() => repository.add("", encode("int32 a:0"))).toThrow(
|
|
599
|
+
`Failed to parse schema: invalid declaration 'int32 a:0'`
|
|
600
|
+
);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it("throws on bit-field declaration with excessive width", () => {
|
|
604
|
+
expect(() => repository.add("", encode("int16 a:17"))).toThrow(
|
|
605
|
+
`Failed to parse schema: invalid integer bit-field width 'a' field declaration`
|
|
606
|
+
);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it("throws on bit-field declaration with unsupported type", () => {
|
|
610
|
+
expect(() => repository.add("", encode("float a:1"))).toThrow(
|
|
611
|
+
`Failed to parse schema: bit-field in non-integer/boolean 'a' field declaration`
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it("throws on bit-field declaration with unknown type", () => {
|
|
616
|
+
expect(() => repository.add("", encode("foo a:1"))).toThrow(
|
|
617
|
+
`Failed to parse schema: bit-field in non-integer/boolean 'a' field declaration`
|
|
618
|
+
);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it("throws on bit-field declaration with invalid boolean size", () => {
|
|
622
|
+
expect(() => repository.add("", encode("bool a:2"))).toThrow(
|
|
623
|
+
`Failed to parse schema: invalid boolean bit-field width 'a' field declaration`
|
|
624
|
+
);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it("throws on enum declaration with unsupported type", () => {
|
|
628
|
+
expect(() => repository.add("", encode("enum {x=1} float a"))).toThrow(
|
|
629
|
+
`Failed to parse schema: enum declaration in non-integer 'a' field declaration`
|
|
630
|
+
);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it("throws on enum declaration with non-integer value", () => {
|
|
634
|
+
expect(() => repository.add("", encode("enum {x=y} int32 a"))).toThrow(
|
|
635
|
+
`Failed to parse schema: invalid declaration 'enum {x=y} int32 a'`
|
|
636
|
+
);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it("throws on circular dependency", () => {
|
|
640
|
+
expect(() => {
|
|
641
|
+
repository.add("typeA", encode("typeB b"));
|
|
642
|
+
repository.add("typeB", encode("typeA a"));
|
|
643
|
+
}).toThrow(`Failed to parse schema: circular dependency detected between 'typeB' and 'typeA'`);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it("throws on nested circular dependency", () => {
|
|
647
|
+
expect(() => {
|
|
648
|
+
repository.add("typeA", encode("typeB b"));
|
|
649
|
+
repository.add("typeB", encode("typeC c"));
|
|
650
|
+
repository.add("typeC", encode("typeA a"));
|
|
651
|
+
}).toThrow(`Failed to parse schema: circular dependency detected between 'typeC' and 'typeA'`);
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
describe("unpack: string", () => {
|
|
656
|
+
const repository = new StructRepository();
|
|
657
|
+
repository.add("test", encode("char a[32]"));
|
|
658
|
+
repository.add("test2", encode("char a[2]"));
|
|
659
|
+
repository.add("test3", encode("char a[3]"));
|
|
660
|
+
repository.add("test4", encode("char a[4]"));
|
|
661
|
+
repository.add("test5", encode("char a[5]"));
|
|
662
|
+
|
|
663
|
+
const encodeString = (v: string, length: number) => {
|
|
664
|
+
const array = new Uint8Array(length);
|
|
665
|
+
new TextEncoder().encodeInto(v, array);
|
|
666
|
+
return array;
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
it("with all zeros", () => {
|
|
670
|
+
expect(unpack("test", encodeString("", 32), repository)["a"]).toEqual("");
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it("with partial value", () => {
|
|
674
|
+
expect(unpack("test", encodeString("abc", 32), repository)["a"]).toEqual("abc");
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it("with embedded null", () => {
|
|
678
|
+
expect(unpack("test", encodeString("ab\0c", 32), repository)["a"]).toEqual("ab\0c");
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it("with overflow", () => {
|
|
682
|
+
expect(unpack("test2", encodeString("abc", 2), repository)["a"]).toEqual("ab");
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
it("with partial 2 bytes", () => {
|
|
686
|
+
expect(unpack("test2", encodeString("a\u0234", 2), repository)["a"]).toEqual("a");
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it("with complete 2 bytes", () => {
|
|
690
|
+
expect(unpack("test3", encodeString("a\u0234", 3), repository)["a"]).toEqual("a\u0234");
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
it("with partial 3 bytes (two short)", () => {
|
|
694
|
+
expect(unpack("test2", encodeString("a\u1234", 2), repository)["a"]).toEqual("a");
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it("with partial 3 bytes (one short)", () => {
|
|
698
|
+
expect(unpack("test3", encodeString("a\u1234", 3), repository)["a"]).toEqual("a");
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
it("with complete 3 bytes", () => {
|
|
702
|
+
expect(unpack("test4", encodeString("a\u1234", 4), repository)["a"]).toEqual("a\u1234");
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it("with partial 4 bytes (three short)", () => {
|
|
706
|
+
expect(unpack("test2", encodeString("a\ud83d\udc00", 2), repository)["a"]).toEqual("a");
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
it("with partial 4 bytes (two short)", () => {
|
|
710
|
+
expect(unpack("test3", encodeString("a\ud83d\udc00", 3), repository)["a"]).toEqual("a");
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it("with partial 4 bytes (one short)", () => {
|
|
714
|
+
expect(unpack("test4", encodeString("a\ud83d\udc00", 4), repository)["a"]).toEqual("a");
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
it("with complete 4 bytes", () => {
|
|
718
|
+
expect(unpack("test5", encodeString("a\ud83d\udc00", 5), repository)["a"]).toEqual("a\ud83d\udc00");
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
describe("roundtrip", () => {
|
|
723
|
+
const repository = new StructRepository();
|
|
724
|
+
|
|
725
|
+
repository.add("test_bool", encode("bool a"));
|
|
726
|
+
repository.add("test_int8", encode("int8 a"));
|
|
727
|
+
repository.add("test_int16", encode("int16 a"));
|
|
728
|
+
repository.add("test_int32", encode("int32 a"));
|
|
729
|
+
repository.add("test_int64", encode("int64 a"));
|
|
730
|
+
repository.add("test_uint8", encode("uint8 a"));
|
|
731
|
+
repository.add("test_uint16", encode("uint16 a"));
|
|
732
|
+
repository.add("test_uint32", encode("uint32 a"));
|
|
733
|
+
repository.add("test_uint64", encode("uint64 a"));
|
|
734
|
+
repository.add("test_float", encode("float a"));
|
|
735
|
+
repository.add("test_double", encode("double a"));
|
|
736
|
+
repository.add("test_char", encode("char a"));
|
|
737
|
+
repository.add("test_char10", encode("char a[10]"));
|
|
738
|
+
repository.add("test_enum", encode("enum {x=1,y=2,z=3} int32 a"));
|
|
739
|
+
repository.add("test_bitfield8", encode("int8 a:2;int8 b:4;int8 c:6"));
|
|
740
|
+
repository.add("test_bitfield16", encode("int16 a:4;int16 b:8;int16 c:12"));
|
|
741
|
+
repository.add("test_bitfield32", encode("int32 a:8;int32 b:16;int32 c:24"));
|
|
742
|
+
repository.add("test_bitfield64", encode("int64 a:16;int64 b:32;int64 c:48"));
|
|
743
|
+
repository.add("test_bitfield8u", encode("uint8 a:2;uint8 b:4;uint8 c:6"));
|
|
744
|
+
repository.add("test_bitfield16u", encode("uint16 a:4;uint16 b:8;uint16 c:12"));
|
|
745
|
+
repository.add("test_bitfield32u", encode("uint32 a:8;uint32 b:16;uint32 c:24"));
|
|
746
|
+
repository.add("test_bitfield64u", encode("uint64 a:16;uint64 b:32;uint64 c:48"));
|
|
747
|
+
repository.add("test_nested", encode("int32 a;test_char10 b;test_enum c"));
|
|
748
|
+
repository.add("test_sample1", encode("uint8 a;uint8 b;uint16 c;uint16 d;uint16 e;int8 f;bool g;"));
|
|
749
|
+
repository.add("test_sample2", encode("uint32 a;char b[10];"));
|
|
750
|
+
|
|
751
|
+
const roundtrip = (name: string, value: Record<string, unknown>) =>
|
|
752
|
+
unpack(name, pack(name, value, repository), repository, { useEnum: true });
|
|
753
|
+
|
|
754
|
+
const expectEqualRoundtrip = (name: string, value: Record<string, unknown>) =>
|
|
755
|
+
expect(roundtrip(name, value)).toEqual(value);
|
|
756
|
+
|
|
757
|
+
it("bool", () => expectEqualRoundtrip("test_bool", { a: true }));
|
|
758
|
+
it("int8", () => expectEqualRoundtrip("test_int8", { a: -42 }));
|
|
759
|
+
it("int16", () => expectEqualRoundtrip("test_int16", { a: -42 }));
|
|
760
|
+
it("int32", () => expectEqualRoundtrip("test_int32", { a: -42 }));
|
|
761
|
+
it("int64", () => expectEqualRoundtrip("test_int64", { a: -42 }));
|
|
762
|
+
it("uint8", () => expectEqualRoundtrip("test_uint8", { a: 42 }));
|
|
763
|
+
it("uint16", () => expectEqualRoundtrip("test_uint16", { a: 42 }));
|
|
764
|
+
it("uint32", () => expectEqualRoundtrip("test_uint32", { a: 42 }));
|
|
765
|
+
it("uint64", () => expectEqualRoundtrip("test_uint64", { a: 42 }));
|
|
766
|
+
it("float", () => expectEqualRoundtrip("test_float", { a: 1.5 }));
|
|
767
|
+
it("double", () => expectEqualRoundtrip("test_double", { a: 3.14 }));
|
|
768
|
+
it("char", () => expectEqualRoundtrip("test_char", { a: "a" }));
|
|
769
|
+
it("char10", () => expectEqualRoundtrip("test_char10", { a: "0123456789" }));
|
|
770
|
+
it("enum", () => expectEqualRoundtrip("test_enum", { a: "y" }));
|
|
771
|
+
it("bitfield8", () => expectEqualRoundtrip("test_bitfield8", { a: 1, b: 2, c: 3 }));
|
|
772
|
+
it("bitfield16", () => expectEqualRoundtrip("test_bitfield16", { a: 1, b: 2, c: 3 }));
|
|
773
|
+
it("bitfield32", () => expectEqualRoundtrip("test_bitfield32", { a: 1, b: 2, c: 3 }));
|
|
774
|
+
it("bitfield64", () => expectEqualRoundtrip("test_bitfield64", { a: 1, b: 2, c: 3 }));
|
|
775
|
+
it("bitfield8u", () => expectEqualRoundtrip("test_bitfield8u", { a: 1, b: 2, c: 3 }));
|
|
776
|
+
it("bitfield16u", () => expectEqualRoundtrip("test_bitfield16u", { a: 1, b: 2, c: 3 }));
|
|
777
|
+
it("bitfield32u", () => expectEqualRoundtrip("test_bitfield32u", { a: 1, b: 2, c: 3 }));
|
|
778
|
+
it("bitfield64u", () => expectEqualRoundtrip("test_bitfield64u", { a: 1, b: 2, c: 3 }));
|
|
779
|
+
it("negative bitfield8", () => expectEqualRoundtrip("test_bitfield8", { a: -1, b: -2, c: -3 }));
|
|
780
|
+
it("negative bitfield16", () => expectEqualRoundtrip("test_bitfield16", { a: -1, b: -2, c: -3 }));
|
|
781
|
+
it("negative bitfield32", () => expectEqualRoundtrip("test_bitfield32", { a: -1, b: -2, c: -3 }));
|
|
782
|
+
|
|
783
|
+
// negative 64-bit values that are wider than 32-bit are expected to be treated as unsigned
|
|
784
|
+
// due to Javascript bitwise operations limitations
|
|
785
|
+
it("negative bitfield64", () =>
|
|
786
|
+
expect(roundtrip("test_bitfield64", { a: -1, b: -2, c: -3 })).toEqual({
|
|
787
|
+
a: -1,
|
|
788
|
+
b: -2,
|
|
789
|
+
c: 281474976710653,
|
|
790
|
+
}));
|
|
791
|
+
|
|
792
|
+
it("nested", () => expectEqualRoundtrip("test_nested", { a: 42, b: { a: "abc" }, c: { a: "z" } }));
|
|
793
|
+
|
|
794
|
+
it("char10 overflow", () =>
|
|
795
|
+
expect(roundtrip("test_char10", { a: "01234567890abcdef" })).toEqual({ a: "0123456789" }));
|
|
796
|
+
|
|
797
|
+
it("char10 utf-16", () => expectEqualRoundtrip("test_char10", { a: " тест " }));
|
|
798
|
+
|
|
799
|
+
it("sample1", () => expectEqualRoundtrip("test_sample1", { a: 63, b: 0, c: 0, d: 0, e: 0, f: -1, g: false }));
|
|
800
|
+
it("sample2", () => expectEqualRoundtrip("test_sample2", { a: 123456789, b: "123456789" }));
|
|
801
|
+
|
|
802
|
+
it("incomplete values 1", () =>
|
|
803
|
+
expect(roundtrip("test_sample1", { a: 63, c: 1, f: -1 })).toEqual({
|
|
804
|
+
a: 63,
|
|
805
|
+
b: 0,
|
|
806
|
+
c: 1,
|
|
807
|
+
d: 0,
|
|
808
|
+
e: 0,
|
|
809
|
+
f: -1,
|
|
810
|
+
g: false,
|
|
811
|
+
}));
|
|
812
|
+
|
|
813
|
+
it("incomplete values 2", () => expect(roundtrip("test_sample2", {})).toEqual({ a: 0, b: "" }));
|
|
814
|
+
});
|