json-as 0.4.9 → 0.5.1

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 CHANGED
File without changes
package/README.md CHANGED
@@ -27,25 +27,17 @@ Or, add it to `asconfig.json`
27
27
  }
28
28
  ```
29
29
 
30
- ## Support
31
-
32
- - ✅ Objects (Supported)
33
- - ✅ Arrays (Supported)
34
- - ✅ Numbers (Supported)
35
- - ✅ Integers (Supported)
36
- - ✅ Null (Supported)
37
- - ❌ Dynamic Variants (Not supported)
38
-
39
30
  ## Usage
40
31
 
41
32
  ```js
42
- import { JSON } from "json-as";
33
+ import { JSON } from "json-as/assembly";
43
34
 
44
35
  @json
45
36
  class Vec2 {
46
37
  x: f32
47
38
  y: f32
48
39
  }
40
+
49
41
  @json
50
42
  class Player {
51
43
  firstName: string
@@ -69,46 +61,24 @@ const data: Player = {
69
61
  }
70
62
 
71
63
  const stringified = JSON.stringify<Player>(data);
72
- // {
73
- // "firstName": "Emmet",
74
- // "lastName": "West",
75
- // "lastActive": [8, 27, 2022],
76
- // "age": 23,
77
- // "pos": {
78
- // "x": -3.4000000953674318,
79
- // "y": 1.2000000476837159
80
- // },
81
- // "isVerified": true
82
- // }
83
- console.log(`Stringified: ${stringified}`);
84
64
 
85
65
  const parsed = JSON.parse<Player>(stringified);
86
- // Player {
87
- // firstName: "Emmet",
88
- // lastName: "West",
89
- // lastActive: [8, 27, 2022],
90
- // age: 23,
91
- // pos: {
92
- // x: -3.4000000953674318,
93
- // y: 1.2000000476837159
94
- // },
95
- // isVerified: true
96
- // }
97
- console.log(`Parsed: ${JSON.stringify(parsed)}`);
98
66
  ```
99
67
 
100
- ## FAQ
101
-
102
- - Does it support the full JSON spec?
103
- Yes, as-json supports the full JSON specification and trys to mimic the JSON API as much as possible
104
- - What are the differences between as-json and the JSON API?
105
- The main difference between as-json and the JSON API is the ability to create new fields in objects during runtime. Since classes are static, Map ser/de support will be added soon allowing the user to parse and stringify dynamic objects and their properties
106
- - Does this support nested structures?
107
- Yes, as-json supports nested structures
108
- - Does this support whitespace?
109
- Yes, as-json supports whitespace!
110
- - How fast is it?
111
- Really fast. For example, here are some benchmarks for ser/de a Vec2 with as-json
68
+ ## Performance
69
+
70
+ **Serialize Object (Vec2):** ~7.29m ops/s
71
+
72
+ **Deserialize Object (Vec2):** ~1.36m ops/s
73
+
74
+ **Serialize Array (int[4]):** ~1.4m ops/s
75
+
76
+ **Deserialize Array (int[4]):** ~2.8m ops/s
77
+
78
+ **Serialize String (5):** ~5.2m ops/sw
79
+
80
+ **Deserialize String (5):** ~1.36m ops/s
81
+
112
82
  ## Issues
113
83
 
114
84
  Please submit an issue to https://github.com/JairusSW/as-json/issues if you find anything wrong with this library
File without changes
package/as-pect.config.js CHANGED
File without changes
package/asconfig.json CHANGED
File without changes
@@ -1,5 +1,4 @@
1
1
  import { JSON } from "..";
2
- import { rainbow } from "as-rainbow/assembly"
3
2
 
4
3
  @json
5
4
  class Vec2 {
@@ -12,8 +11,6 @@ const vec: Vec2 = blackbox<Vec2>({
12
11
  y: 0.0,
13
12
  });
14
13
 
15
- //console.log(rainbow.bgBlue("Running benchmark for as-json"));
16
-
17
14
  bench("Stringify Object (Vec2)", () => {
18
15
  blackbox(JSON.stringify(vec));
19
16
  });
File without changes
@@ -5,6 +5,20 @@ function canSerde<T>(data: T): void {
5
5
  expect(serialized).toBe(deserialized);
6
6
  }
7
7
 
8
+ // @ts-ignore
9
+ @json
10
+ class Vec2 {
11
+ x: f32;
12
+ y: f32;
13
+ }
14
+
15
+ class Nullable {}
16
+ type Null = Nullable | null
17
+
18
+ describe("Ser/de Nulls", () => {
19
+ canSerde<Null>(null)
20
+ })
21
+
8
22
  describe("Ser/de Numbers", () => {
9
23
  it("should ser/de integers", () => {
10
24
  canSerde<i32>(0)
@@ -82,4 +96,14 @@ describe("Ser/de Array", () => {
82
96
  it("should ser/de string arrays", () => {
83
97
  canSerde<string[][]>([["abcdefg"], ["st\"ring\" w\"\"ith quotes\""], ["string \t\r\\\"with ran\tdom spa\nces and \nnewlines\n\n\n"], ["string with colon : comma , brace [ ] bracket { } and quote \" and other quote \\\""]])
84
98
  });
85
- });
99
+ });
100
+
101
+ describe("Ser/de Objects", () => {
102
+ it("should ser/de Vec2 Objects", () => {
103
+ canSerde<Vec2>({
104
+ x: 3.4,
105
+ y: 1.2
106
+ })
107
+ })
108
+
109
+ })
File without changes
package/assembly/chars.ts CHANGED
@@ -15,3 +15,4 @@ export const fCode = "f".charCodeAt(0);
15
15
  export const aCode = "a".charCodeAt(0);
16
16
  export const lCode = "l".charCodeAt(0);
17
17
  export const sCode = "s".charCodeAt(0);
18
+ export const nCode = "n".charCodeAt(0);
package/assembly/index.ts CHANGED
@@ -1,457 +1 @@
1
- import { StringSink } from "as-string-sink/assembly";
2
- import { Variant } from "as-variant/assembly";
3
- import { isSpace } from "util/string";
4
- import {
5
- backSlashCode,
6
- colonCode,
7
- commaCode,
8
- eCode,
9
- fCode,
10
- forwardSlashCode,
11
- leftBraceCode,
12
- leftBracketCode,
13
- quoteCode,
14
- rightBraceCode,
15
- rightBracketCode,
16
- tCode,
17
- } from "./chars";
18
- import { removeWhitespace, unsafeCharCodeAt } from "./util";
19
-
20
- /**
21
- * JSON Encoder/Decoder for AssemblyScript
22
- */
23
- export class JSON {
24
- private static parseObjectValue<T>(data: string): T {
25
- let type!: T;
26
- if (isString<T>()) {
27
- // @ts-ignore
28
- return data.replaceAll('\\"', '"');
29
- } else if (isBoolean<T>()) {
30
- // @ts-ignore
31
- return parseBoolean<T>(data);
32
- } else if (isFloat<T>() || isInteger<T>()) {
33
- return parseNumber<T>(data);
34
- } else if (isArrayLike<T>()) {
35
- // @ts-ignore
36
- return parseArray<T>(data);
37
- // @ts-ignore
38
- } else if (isDefined(type.__JSON_Deserialize)) {
39
- return parseObject<T>(data);
40
- } else {
41
- // @ts-ignore
42
- return null;
43
- }
44
- }
45
- /**
46
- * Stringifies valid JSON data.
47
- * ```js
48
- * JSON.stringify<T>(data)
49
- * ```
50
- * @param data T
51
- * @returns string
52
- */
53
- static stringify<T = Nullable | null>(data: T): string {
54
- // String
55
- if (isString<T>()) {
56
- return '"' + (<string>data).replaceAll('"', '\\"') + '"';
57
- }
58
- // Boolean
59
- else if (isBoolean<T>()) {
60
- return data ? "true" : "false";
61
- }
62
- // Integers/Floats
63
- // @ts-ignore
64
- else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
65
- // @ts-ignore
66
- return data.toString();
67
- }
68
- // Class-Based serialization
69
- // @ts-ignore
70
- else if (isDefined(data.__JSON_Serialize)) {
71
- // @ts-ignore
72
- return data.__JSON_Serialize();
73
- }
74
- // ArrayLike
75
- else if (isArrayLike<T>()) {
76
- let result = new StringSink("[");
77
- // @ts-ignore
78
- if (data.length == 0) return "[]";
79
- // @ts-ignore
80
- for (let i = 0; i < data.length - 1; i++) {
81
- // @ts-ignore
82
- result.write(JSON.stringify(unchecked(data[i])));
83
- result.write(",");
84
- }
85
- // @ts-ignore
86
- result.write(JSON.stringify(unchecked(data[data.length - 1])));
87
- result.write("]");
88
- return result.toString();
89
- }
90
- // Null
91
- else if (isNullable<T>() && data == null) {
92
- return "null";
93
- } else {
94
- return "null";
95
- }
96
- }
97
- /**
98
- * Parses valid JSON strings into their original format.
99
- * ```js
100
- * JSON.parse<T>(data)
101
- * ```
102
- * @param data string
103
- * @returns T
104
- */
105
- static parse<T = Variant>(data: string): T {
106
- let type!: T;
107
- if (isString<T>()) {
108
- // @ts-ignore
109
- return parseString(data);
110
- } else if (isBoolean<T>()) {
111
- // @ts-ignore
112
- return parseBoolean<T>(data);
113
- } else if (isFloat<T>() || isInteger<T>()) {
114
- return parseNumber<T>(data);
115
- } else if (isArrayLike<T>()) {
116
- // @ts-ignore
117
- return parseArray<T>(data.trimStart());
118
- // @ts-ignore
119
- } else if (isDefined(type.__JSON_Deserialize)) {
120
- return parseObject<T>(data.trimStart());
121
- } else {
122
- // @ts-ignore
123
- return null;
124
- }
125
- }
126
- }
127
-
128
- // @ts-ignore
129
- @inline
130
- function parseString(data: string): string {
131
- return data.slice(1, data.length - 1).replaceAll('\\"', '"');
132
- }
133
-
134
- // @ts-ignore
135
- @inline
136
- function parseBoolean<T extends boolean>(data: string): T {
137
- if (data.length > 3 && data.startsWith("true")) return <T>true;
138
- else if (data.length > 4 && data.startsWith("false")) return <T>false;
139
- else throw new Error(`JSON: Cannot parse "${data}" as boolean`);
140
- }
141
-
142
- // @ts-ignore
143
- @inline
144
- function parseNumber<T>(data: string): T {
145
- let type: T;
146
- // @ts-ignore
147
- if (type instanceof f64) return F64.parseFloat(data);
148
- // @ts-ignore
149
- else if (type instanceof f32) return F32.parseFloat(data);
150
- // @ts-ignore
151
- else if (type instanceof u64) return U64.parseInt(data);
152
- // @ts-ignore
153
- else if (type instanceof u32) return U32.parseInt(data);
154
- // @ts-ignore
155
- else if (type instanceof u8) return U8.parseInt(data);
156
- // @ts-ignore
157
- else if (type instanceof u16) return U16.parseInt(data);
158
- // @ts-ignore
159
- else if (type instanceof i64) return I64.parseInt(data);
160
- // @ts-ignore
161
- else if (type instanceof i32) return I32.parseInt(data);
162
- // @ts-ignore
163
- else if (type instanceof i16) return I16.parseInt(data);
164
- // @ts-ignore
165
- else if (type instanceof i8) return I8.parseInt(data);
166
- else
167
- throw new Error(
168
- `JSON: Cannot parse invalid data into a number. Either "${data}" is not a valid number, or <${nameof<T>()}> is an invald number type.`
169
- );
170
- }
171
-
172
- // @ts-ignore
173
- @inline
174
- export function parseObject<T>(data: string): T {
175
- let schema!: T;
176
- const result = new Map<string, string>();
177
- let key = "";
178
- let isKey = false;
179
- let depth = 1;
180
- let char = 0;
181
- for (
182
- let outerLoopIndex = 1;
183
- outerLoopIndex < data.length - 1;
184
- outerLoopIndex++
185
- ) {
186
- char = unsafeCharCodeAt(data, outerLoopIndex);
187
- if (char === leftBracketCode) {
188
- for (
189
- let arrayValueIndex = outerLoopIndex;
190
- arrayValueIndex < data.length - 1;
191
- arrayValueIndex++
192
- ) {
193
- char = unsafeCharCodeAt(data, arrayValueIndex);
194
- if (char === leftBracketCode) {
195
- depth = depth << 1;
196
- } else if (char === rightBracketCode) {
197
- depth = depth >> 1;
198
- if (depth === 1) {
199
- ++arrayValueIndex;
200
- result.set(
201
- key,
202
- data.slice(outerLoopIndex, arrayValueIndex)
203
- );
204
- outerLoopIndex = arrayValueIndex;
205
- isKey = false;
206
- break;
207
- }
208
- }
209
- }
210
- } else if (char === leftBraceCode) {
211
- for (
212
- let objectValueIndex = outerLoopIndex;
213
- objectValueIndex < data.length - 1;
214
- objectValueIndex++
215
- ) {
216
- char = unsafeCharCodeAt(data, objectValueIndex);
217
- if (char === leftBraceCode) {
218
- depth = depth << 1;
219
- } else if (char === rightBraceCode) {
220
- depth = depth >> 1;
221
- if (depth === 1) {
222
- ++objectValueIndex;
223
- result.set(
224
- key,
225
- data.slice(outerLoopIndex, objectValueIndex)
226
- );
227
- outerLoopIndex = objectValueIndex;
228
- isKey = false;
229
- break;
230
- }
231
- }
232
- }
233
- } else if (char === quoteCode) {
234
- for (
235
- let stringValueIndex = ++outerLoopIndex;
236
- stringValueIndex < data.length - 1;
237
- stringValueIndex++
238
- ) {
239
- char = unsafeCharCodeAt(data, stringValueIndex);
240
- if (
241
- char === quoteCode &&
242
- unsafeCharCodeAt(data, stringValueIndex - 1) !== backSlashCode
243
- ) {
244
- if (isKey === false) {
245
- key = data.slice(outerLoopIndex, stringValueIndex);
246
- isKey = true;
247
- } else {
248
- result.set(
249
- key,
250
- data.slice(outerLoopIndex, stringValueIndex)
251
- );
252
- isKey = false;
253
- }
254
- outerLoopIndex = ++stringValueIndex;
255
- break;
256
- }
257
- }
258
- } else if (
259
- char === tCode &&
260
- unsafeCharCodeAt(data, ++outerLoopIndex) === "r".charCodeAt(0) &&
261
- unsafeCharCodeAt(data, ++outerLoopIndex) === "u".charCodeAt(0) &&
262
- unsafeCharCodeAt(data, ++outerLoopIndex) === eCode
263
- ) {
264
- result.set(key, "true");
265
- isKey = false;
266
- } else if (
267
- char === fCode &&
268
- unsafeCharCodeAt(data, ++outerLoopIndex) === "a".charCodeAt(0) &&
269
- unsafeCharCodeAt(data, ++outerLoopIndex) === "l".charCodeAt(0) &&
270
- unsafeCharCodeAt(data, ++outerLoopIndex) === "s".charCodeAt(0) &&
271
- unsafeCharCodeAt(data, ++outerLoopIndex) === eCode
272
- ) {
273
- result.set(key, "false");
274
- isKey = false;
275
- } else if ((char >= 48 && char <= 57) || char === 45) {
276
- let numberValueIndex = ++outerLoopIndex;
277
- for (; numberValueIndex < data.length - 1; numberValueIndex++) {
278
- char = unsafeCharCodeAt(data, numberValueIndex);
279
- if (
280
- char === commaCode ||
281
- isSpace(char) ||
282
- numberValueIndex == data.length - 2
283
- ) {
284
- result.set(
285
- key,
286
- data.slice(outerLoopIndex - 1, numberValueIndex)
287
- );
288
- outerLoopIndex = numberValueIndex;
289
- isKey = false;
290
- break;
291
- }
292
- }
293
- }
294
- }
295
- // @ts-ignore
296
- return schema.__JSON_Deserialize(result);
297
- }
298
-
299
- // @ts-ignore
300
- @inline
301
- // @ts-ignore
302
- export function parseArray<T extends unknown[]>(data: string): T {
303
- // TODO: Replace with opt
304
- let type!: valueof<T>;
305
- if (type instanceof String) {
306
- return <T>parseStringArray(data);
307
- } else if (isBoolean<valueof<T>>()) {
308
- // @ts-ignore
309
- return parseBooleanArray<T>(data);
310
- } else if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) {
311
- // @ts-ignore
312
- return parseNumberArray<T>(data);
313
- } else if (isArrayLike<valueof<T>>()) {
314
- // @ts-ignore
315
- return parseArrayArray<T>(data);
316
- // @ts-ignore
317
- } else if (isDefined(type.__JSON_Deserialize)) {
318
- // @ts-ignore
319
- return parseObjectArray<T>(data);
320
- }
321
- }
322
-
323
- // @ts-ignore
324
- @inline
325
- export function parseStringArray(data: string): string[] {
326
- const result: string[] = [];
327
- let lastPos = 0;
328
- let instr = false;
329
- for (let i = 1; i < data.length - 1; i++) {
330
- if (unsafeCharCodeAt(data, i) === quoteCode) {
331
- if (instr === false) {
332
- instr = true;
333
- lastPos = i;
334
- } else if (unsafeCharCodeAt(data, i - 1) !== backSlashCode) {
335
- instr = false;
336
- result.push(data.slice(lastPos + 1, i).replaceAll('\\"', '"'));
337
- }
338
- }
339
- }
340
- return result;
341
- }
342
-
343
- // @ts-ignore
344
- @inline
345
- export function parseBooleanArray<T extends boolean[]>(data: string): T {
346
- const result = instantiate<T>();
347
- let lastPos = 1;
348
- let char = 0;
349
- for (let i = 1; i < data.length - 1; i++) {
350
- char = unsafeCharCodeAt(data, i);
351
- /*// if char == "t" && i+3 == "e"
352
- if (char === tCode && data.charCodeAt(i + 3) === eCode) {
353
- //i += 3;
354
- result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i+2)));
355
- //i++;
356
- } else if (char === fCode && data.charCodeAt(i + 4) === eCode) {
357
- //i += 4;
358
- result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i+3)));
359
- //i++;
360
- }*/
361
- if (char === tCode || char === fCode) {
362
- lastPos = i;
363
- } else if (char === eCode) {
364
- i++;
365
- result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i)));
366
- }
367
- }
368
- return result;
369
- }
370
-
371
- // @ts-ignore
372
- @inline
373
- export function parseNumberArray<T extends number[]>(data: string): T {
374
- const result = instantiate<T>();
375
- let lastPos = 0;
376
- let char = 0;
377
- let i = 1;
378
- for (; i < data.length - 1; i++) {
379
- char = unsafeCharCodeAt(data, i);
380
- if (lastPos === 0 && (char >= 48 && char <= 57) || char === 45) {
381
- lastPos = i;
382
- } else if ((isSpace(char) || char == commaCode) && lastPos > 0) {
383
- result.push(parseNumber<valueof<T>>(data.slice(lastPos, i)));
384
- lastPos = 0;
385
- }
386
- }
387
- for (; i > lastPos - 1; i--) {
388
- char = unsafeCharCodeAt(data, i);
389
- if (char !== rightBracketCode) {
390
- result.push(parseNumber<valueof<T>>(data.slice(lastPos, i + 1)));
391
- break;
392
- }
393
- }
394
- return result;
395
- }
396
-
397
- // @ts-ignore
398
- @inline
399
- export function parseArrayArray<T extends unknown[][]>(data: string): T {
400
- const result = instantiate<T>();
401
- let char = 0;
402
- let lastPos = 0;
403
- let depth = 1;
404
- let i = 1;
405
- // Find start of bracket
406
- //for (; unsafeCharCodeAt(data, i) !== leftBracketCode; i++) {}
407
- //i++;
408
- for (; i < data.length - 1; i++) {
409
- char = unsafeCharCodeAt(data, i);
410
- if (char === leftBracketCode) {
411
- if (depth === 1) {
412
- lastPos = i;
413
- }
414
- // Shifting is 6% faster than incrementing
415
- depth = depth << 1;
416
- } else if (char === rightBracketCode) {
417
- depth = depth >> 1;
418
- if (depth === 1) {
419
- i++;
420
- result.push(JSON.parse<valueof<T>>(data.slice(lastPos, i)));
421
- }
422
- }
423
- }
424
- return result;
425
- }
426
-
427
- // @ts-ignore
428
- @inline
429
- export function parseObjectArray<T extends unknown[][]>(data: string): T {
430
- const result = instantiate<T>();
431
- let char = 0;
432
- let lastPos = 1;
433
- let depth = 1;
434
- let i = 1;
435
- // Find start of bracket
436
- //for (; unsafeCharCodeAt(data, i) !== leftBracketCode; i++) { }
437
- //i++;
438
- for (; i < data.length - 1; i++) {
439
- char = unsafeCharCodeAt(data, i);
440
- if (char === leftBraceCode) {
441
- if (depth === 1) {
442
- lastPos = i;
443
- }
444
- // Shifting is 6% faster than incrementing
445
- depth = depth << 1;
446
- } else if (char === rightBraceCode) {
447
- depth = depth >> 1;
448
- if (depth === 1) {
449
- i++;
450
- result.push(JSON.parse<valueof<T>>(data.slice(lastPos, i)));
451
- }
452
- }
453
- }
454
- return result;
455
- }
456
-
457
- class Nullable { }
1
+ export { JSON } from "./json";