json-as 0.4.2 → 0.4.5

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/assembly/index.ts CHANGED
@@ -1,21 +1,48 @@
1
1
  import { StringSink } from "as-string-sink/assembly";
2
2
  import { Variant } from "as-variant/assembly";
3
+ import { isSpace } from "assemblyscript/std/assembly/util/string";
4
+ import { stringify } from "as-console/assembly";
3
5
  import {
4
6
  backSlashCode,
5
7
  colonCode,
6
8
  commaCode,
9
+ eCode,
10
+ fCode,
11
+ forwardSlashCode,
7
12
  leftBraceCode,
8
13
  leftBracketCode,
9
14
  quoteCode,
10
15
  rightBraceCode,
11
- rightBracketCode
16
+ rightBracketCode,
17
+ tCode,
12
18
  } from "./chars";
19
+ import { removeWhitespace, unsafeCharCodeAt } from "./util";
13
20
 
14
21
  /**
15
22
  * JSON Encoder/Decoder for AssemblyScript
16
23
  */
17
- export namespace JSON {
18
- export type _Variant = Variant;
24
+ export class JSON {
25
+ private static parseObjectValue<T>(data: string): T {
26
+ let type!: T;
27
+ if (isString<T>()) {
28
+ // @ts-ignore
29
+ return data.replaceAll('\\"', '"');
30
+ } else if (isBoolean<T>()) {
31
+ // @ts-ignore
32
+ return parseBoolean<T>(data);
33
+ } else if (isFloat<T>() || isInteger<T>()) {
34
+ return parseNumber<T>(data);
35
+ } else if (isArrayLike<T>()) {
36
+ // @ts-ignore
37
+ return parseArray<T>(data);
38
+ // @ts-ignore
39
+ } else if (isDefined(type.__JSON_Deserialize)) {
40
+ return parseObject<T>(data);
41
+ } else {
42
+ // @ts-ignore
43
+ return null;
44
+ }
45
+ }
19
46
  /**
20
47
  * Stringifies valid JSON data.
21
48
  * ```js
@@ -24,19 +51,15 @@ export namespace JSON {
24
51
  * @param data T
25
52
  * @returns string
26
53
  */
27
- export function stringify<T = Nullable | null>(data: T): string {
54
+ static stringify<T = Nullable | null>(data: T): string {
28
55
  // String
29
- if (isString(data)) {
30
- return serializeString(<string>data);
56
+ if (isString<T>()) {
57
+ return '"' + (<string>data).replaceAll('"', '\\"') + '"';
31
58
  }
32
59
  // Boolean
33
- else if (isBoolean(data)) {
60
+ else if (isBoolean<T>()) {
34
61
  return data ? "true" : "false";
35
62
  }
36
- // Null
37
- else if (isNullable<T>() && data == null) {
38
- return "null";
39
- }
40
63
  // Integers/Floats
41
64
  // @ts-ignore
42
65
  else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
@@ -46,20 +69,28 @@ export namespace JSON {
46
69
  // Class-Based serialization
47
70
  // @ts-ignore
48
71
  else if (isDefined(data.__JSON_Serialize)) {
49
- //@ts-ignore
72
+ // @ts-ignore
50
73
  return data.__JSON_Serialize();
51
74
  }
52
75
  // ArrayLike
53
- else if (isArrayLike(data)) {
76
+ else if (isArrayLike<T>()) {
54
77
  let result = new StringSink("[");
78
+ // @ts-ignore
55
79
  if (data.length == 0) return "[]";
80
+ // @ts-ignore
56
81
  for (let i = 0; i < data.length - 1; i++) {
82
+ // @ts-ignore
57
83
  result.write(stringify(unchecked(data[i])));
58
84
  result.write(",");
59
85
  }
86
+ // @ts-ignore
60
87
  result.write(stringify(unchecked(data[data.length - 1])));
61
88
  result.write("]");
62
89
  return result.toString();
90
+ }
91
+ // Null
92
+ else if (isNullable<T>() && data == null) {
93
+ return "null";
63
94
  } else {
64
95
  return "null";
65
96
  }
@@ -72,7 +103,7 @@ export namespace JSON {
72
103
  * @param data string
73
104
  * @returns T
74
105
  */
75
- export function parse<T = Variant>(data: string): T {
106
+ static parse<T = Variant>(data: string): T {
76
107
  let type!: T;
77
108
  if (isString<T>()) {
78
109
  // @ts-ignore
@@ -83,48 +114,11 @@ export namespace JSON {
83
114
  } else if (isFloat<T>() || isInteger<T>()) {
84
115
  return parseNumber<T>(data);
85
116
  } else if (isArrayLike<T>()) {
86
- return parseArray<T>(data);
87
117
  // @ts-ignore
88
- } else if (isDefined(type.__JSON_Deserialize)) {
89
- const len: u32 = data.length - 1
90
- let schema!: T
91
- const result = new Map<string, string>()
92
- let lastPos: u32 = 1
93
- let key: string = ''
94
- let instr: u32 = 0
95
- let char: u32 = 0
96
- let depth: u32 = 0
97
- let fdepth: u32 = 0
98
- for (let i: u32 = 1; i < len; i++) {
99
- char = data.charCodeAt(i)
100
- if (char === "\"".charCodeAt(0) && data.charCodeAt(i - 1) !== "/".charCodeAt(0)) instr = (instr ? 0 : 1)
101
- else if (instr === 0) {
102
- if (char === leftBraceCode || char === leftBracketCode) depth++
103
- if (char === rightBraceCode || char === rightBracketCode) fdepth++
104
- }
105
- if (depth !== 0 && depth === fdepth) {
106
- result.set(key, data.slice(lastPos + 1, i + 1))
107
- // Reset the depth
108
- depth = 0
109
- fdepth = 0
110
- // Set new lastPos
111
- lastPos = i + 1
112
- }
113
- if (depth === 0) {
114
- if (char === colonCode) {
115
- key = data.slice(lastPos + 1, i - 1)
116
- lastPos = i
117
- }
118
- else if (char === commaCode) {
119
- if ((i - lastPos) > 0) result.set(key, data.slice(lastPos + 1, i))
120
- lastPos = i + 1
121
- }
122
- }
123
- }
124
-
125
- if ((len - lastPos) > 0) result.set(key, data.slice(lastPos + 1, len))
118
+ return parseArray<T>(data.trimStart());
126
119
  // @ts-ignore
127
- return schema.__JSON_Deserialize(result)
120
+ } else if (isDefined(type.__JSON_Deserialize)) {
121
+ return parseObject<T>(data.trimStart());
128
122
  } else {
129
123
  // @ts-ignore
130
124
  return null;
@@ -134,25 +128,21 @@ export namespace JSON {
134
128
 
135
129
  // @ts-ignore
136
130
  @inline
137
- function serializeString(data: string): string {
138
- return '"' + data.replaceAll('"', '\\"') + '"';
139
- }
140
- // @ts-ignore
141
- @inline
142
- function parseString(data: string): string {
131
+ function parseString(data: string): string {
143
132
  return data.slice(1, data.length - 1).replaceAll('\\"', '"');
144
133
  }
145
134
 
146
135
  // @ts-ignore
147
136
  @inline
148
- function parseBoolean<T extends boolean>(data: string): T {
137
+ function parseBoolean<T extends boolean>(data: string): T {
149
138
  if (data.length > 3 && data.startsWith("true")) return <T>true;
150
139
  else if (data.length > 4 && data.startsWith("false")) return <T>false;
151
140
  else throw new Error(`JSON: Cannot parse "${data}" as boolean`);
152
141
  }
142
+
153
143
  // @ts-ignore
154
144
  @inline
155
- function parseNumber<T>(data: string): T {
145
+ function parseNumber<T>(data: string): T {
156
146
  let type: T;
157
147
  // @ts-ignore
158
148
  if (type instanceof f64) return F64.parseFloat(data);
@@ -176,162 +166,287 @@ export namespace JSON {
176
166
  );
177
167
  }
178
168
 
179
- // @ts-ignore
180
- @inline
181
- export function parseNumberArray<T>(data: string): T {
182
- const result = instantiate<T>();
183
- if (data.length == 0) return result;
184
- let lastPos: u32 = 1;
185
- let i: u32 = 1;
186
- let char: u32 = 0;
187
- for (; i < u32(data.length - 1); i++) {
188
- char = data.charCodeAt(i);
189
- if (char == commaCode) {
190
- // console.log(data.slice(lastPos, i))
191
- // @ts-ignore
192
- result.push(parseNumber<valueof<T>>(data.slice(lastPos, i).trim()));
193
- lastPos = ++i;
169
+ export function parseObject<T>(data: string): T {
170
+ let schema!: T;
171
+ const result = new Map<string, string>();
172
+ let key: usize = 0;
173
+ let depth = 1;
174
+ let char = 0;
175
+ for (
176
+ let outerLoopIndex = 1;
177
+ outerLoopIndex < data.length - 1;
178
+ outerLoopIndex++
179
+ ) {
180
+ char = unsafeCharCodeAt(data, outerLoopIndex);
181
+ if (char === leftBracketCode) {
182
+ for (
183
+ let arrayValueIndex = outerLoopIndex;
184
+ arrayValueIndex < data.length - 1;
185
+ arrayValueIndex++
186
+ ) {
187
+ char = unsafeCharCodeAt(data, arrayValueIndex);
188
+ if (char === leftBracketCode) {
189
+ depth = depth << 1;
190
+ } else if (char === rightBracketCode) {
191
+ depth = depth >> 1;
192
+ if (depth === 1) {
193
+ ++arrayValueIndex;
194
+ result.set(
195
+ changetype<string>(key),
196
+ data.slice(outerLoopIndex, arrayValueIndex)
197
+ );
198
+ outerLoopIndex = arrayValueIndex;
199
+ key = 0;
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ } else if (char === leftBraceCode) {
205
+ for (
206
+ let objectValueIndex = outerLoopIndex;
207
+ objectValueIndex < data.length - 1;
208
+ objectValueIndex++
209
+ ) {
210
+ char = unsafeCharCodeAt(data, objectValueIndex);
211
+ if (char === leftBraceCode) {
212
+ depth = depth << 1;
213
+ } else if (char === rightBraceCode) {
214
+ depth = depth >> 1;
215
+ if (depth === 1) {
216
+ ++objectValueIndex;
217
+ result.set(
218
+ changetype<string>(key),
219
+ data.slice(outerLoopIndex, objectValueIndex)
220
+ );
221
+ outerLoopIndex = objectValueIndex;
222
+ key = 0;
223
+ break;
224
+ }
225
+ }
226
+ }
227
+ } else if (char === quoteCode) {
228
+ for (
229
+ let stringValueIndex = ++outerLoopIndex;
230
+ stringValueIndex < data.length - 1;
231
+ stringValueIndex++
232
+ ) {
233
+ char = unsafeCharCodeAt(data, stringValueIndex);
234
+ if (
235
+ char === quoteCode &&
236
+ unsafeCharCodeAt(data, stringValueIndex - 1) !== backSlashCode
237
+ ) {
238
+ if (key === 0) {
239
+ key = changetype<usize>(
240
+ data.slice(outerLoopIndex, stringValueIndex)
241
+ );
242
+ } else {
243
+ result.set(
244
+ changetype<string>(key),
245
+ data.slice(outerLoopIndex, stringValueIndex)
246
+ );
247
+ key = 0;
248
+ }
249
+ outerLoopIndex = ++stringValueIndex;
250
+ break;
251
+ }
252
+ }
253
+ } else if (
254
+ char === tCode &&
255
+ unsafeCharCodeAt(data, ++outerLoopIndex) === "r".charCodeAt(0) &&
256
+ unsafeCharCodeAt(data, ++outerLoopIndex) === "u".charCodeAt(0) &&
257
+ unsafeCharCodeAt(data, ++outerLoopIndex) === eCode
258
+ ) {
259
+ result.set(changetype<string>(key), "true");
260
+ key = 0;
261
+ } else if (
262
+ char === fCode &&
263
+ unsafeCharCodeAt(data, ++outerLoopIndex) === "a".charCodeAt(0) &&
264
+ unsafeCharCodeAt(data, ++outerLoopIndex) === "l".charCodeAt(0) &&
265
+ unsafeCharCodeAt(data, ++outerLoopIndex) === "s".charCodeAt(0) &&
266
+ unsafeCharCodeAt(data, ++outerLoopIndex) === eCode
267
+ ) {
268
+ result.set(changetype<string>(key), "false");
269
+ key = 0;
270
+ } else if (char >= 48 && char <= 57) {
271
+ let numberValueIndex = ++outerLoopIndex;
272
+ for (; numberValueIndex < data.length - 1; numberValueIndex++) {
273
+ char = unsafeCharCodeAt(data, numberValueIndex);
274
+ if (
275
+ char === commaCode ||
276
+ isSpace(char) ||
277
+ numberValueIndex == data.length - 2
278
+ ) {
279
+ result.set(
280
+ changetype<string>(key),
281
+ data.slice(outerLoopIndex - 1, numberValueIndex)
282
+ );
283
+ outerLoopIndex = numberValueIndex;
284
+ key = 0;
285
+ break;
286
+ }
287
+ }
194
288
  }
195
289
  }
196
- //console.log(data.slice(lastPos, data.length - 1))
197
290
  // @ts-ignore
198
- result.push(
291
+ return schema.__JSON_Deserialize(result);
292
+ }
293
+
294
+ // @ts-ignore
295
+ @inline
296
+ // @ts-ignore
297
+ export function parseArray<T extends unknown[]>(data: string): T {
298
+ // TODO: Replace with opt
299
+ let type!: valueof<T>;
300
+ if (type instanceof String) {
301
+ return <T>parseStringArray(data);
302
+ } else if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) {
303
+ // @ts-ignore
304
+ return parseNumberArray<T>(data);
305
+ } else if (isBoolean<valueof<T>>()) {
199
306
  // @ts-ignore
200
- parseNumber<valueof<T>>(data.slice(lastPos, data.length - 1).trimStart())
201
- );
307
+ return parseBooleanArray<T>(data);
308
+ } else if (isArrayLike<valueof<T>>()) {
309
+ // @ts-ignore
310
+ return parseArrayArray<T>(data);
311
+ // @ts-ignore
312
+ } else if (isDefined(type.__JSON_Deserialize)) {
313
+ // @ts-ignore
314
+ return parseObjectArray<T>(data);
315
+ }
316
+ }
317
+
318
+ // @ts-ignore
319
+ @inline
320
+ export function parseStringArray(data: string): string[] {
321
+ const result: string[] = [];
322
+ let lastPos = 0;
323
+ let instr = false;
324
+ for (let i = 1; i < data.length - 1; i++) {
325
+ if (unsafeCharCodeAt(data, i) === quoteCode) {
326
+ if (instr === false) {
327
+ instr = true;
328
+ lastPos = i;
329
+ } else if (unsafeCharCodeAt(data, i - 1) !== backSlashCode) {
330
+ instr = false;
331
+ result.push(data.slice(lastPos + 1, i).replaceAll('\\"', '"'));
332
+ }
333
+ }
334
+ }
202
335
  return result;
203
336
  }
204
337
 
205
338
  // @ts-ignore
206
339
  @inline
207
- export function parseBooleanArray<T>(data: string): T {
340
+ export function parseBooleanArray<T extends boolean[]>(data: string): T {
208
341
  const result = instantiate<T>();
209
- if (data.length == 0) return result;
210
- let lastPos: u32 = 1;
211
- let i: u32 = 1;
212
- let char: u32 = 0;
213
- for (; i < u32(data.length - 1); i++) {
214
- char = data.charCodeAt(i);
215
- if (char == commaCode) {
216
- // @ts-ignore
217
- result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i).trimStart()));
218
- lastPos = ++i;
342
+ let lastPos = 1;
343
+ let char = 0;
344
+ for (let i = 1; i < data.length - 1; i++) {
345
+ char = unsafeCharCodeAt(data, i);
346
+ /*// if char == "t" && i+3 == "e"
347
+ if (char === tCode && data.charCodeAt(i + 3) === eCode) {
348
+ //i += 3;
349
+ result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i+2)));
350
+ //i++;
351
+ } else if (char === fCode && data.charCodeAt(i + 4) === eCode) {
352
+ //i += 4;
353
+ result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i+3)));
354
+ //i++;
355
+ }*/
356
+ if (char === tCode || char === fCode) {
357
+ lastPos = i;
358
+ } else if (char === eCode) {
359
+ i++;
360
+ result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i)));
219
361
  }
220
362
  }
221
- // @ts-ignore
222
- result.push(
223
- // @ts-ignore
224
- parseBoolean<valueof<T>>(data.slice(lastPos, data.length - 1).trimStart())
225
- );
226
363
  return result;
227
364
  }
228
365
 
229
366
  // @ts-ignore
230
367
  @inline
231
- export function parseArray<T>(data: string): T {
368
+ export function parseNumberArray<T extends number[]>(data: string): T {
232
369
  const result = instantiate<T>();
233
- data = data.trim();
234
- let len: u32 = data.length - 1;
235
- let lastPos: u32 = 1;
236
- let i: u32 = 1;
237
- let char: u32 = 0;
238
- // Is struct such as Object, or Array
239
- let isStruct: boolean = false;
240
- let isStr: boolean = false;
241
- //let offset: u32 = 0;
242
- // Depth for finding matching brackets
243
- let inDepth: u32 = 0;
244
- let outDepth: u32 = 0;
245
- for (; i < len; i++) {
246
- char = data.charCodeAt(i);
247
- if (char == quoteCode && data.charCodeAt(i - 1) != backSlashCode)
248
- isStr = !isStr;
249
- if (char == leftBraceCode || char == leftBracketCode) {
250
- inDepth++;
251
- isStruct = true;
252
- } else if (char == rightBraceCode || char == rightBracketCode) {
253
- outDepth++;
254
- isStruct = true;
255
- }
256
- if (!isStr) {
257
- if (!isStruct) {
258
- // This removes whitespace before and after an element
259
- /*if (offset != 0 && isSpace(char)) {
260
- lastPos++;
261
- } else {
262
- if (isSpace(char)) offset++;
263
- }*/
264
- // This checks to see if we are dealing with structures such as Objects and Arrays
265
- if (char == commaCode) {
266
- // @ts-ignore
267
- result.push(JSON.parse<valueof<T>>(data.slice(lastPos, i).trim()));
268
- //offset = 0;
269
- lastPos = i + 1;
270
- }
271
- } else {
272
- if (inDepth == outDepth) {
273
- i++;
274
- //console.log(`Struct-${data.slice(lastPos, i).trim()}-`)
275
- lastPos = i + 1;
276
- inDepth = 0;
277
- outDepth = 0;
278
- isStruct = false;
279
- }
280
- }
370
+ let lastPos = 0;
371
+ let char = 0;
372
+ let i = 1;
373
+ for (; i < data.length - 1; i++) {
374
+ char = unsafeCharCodeAt(data, i);
375
+ if (lastPos === 0 && char >= 48 && char <= 57) {
376
+ lastPos = i;
377
+ } else if ((isSpace(char) || char == commaCode) && lastPos > 0) {
378
+ result.push(parseNumber<valueof<T>>(data.slice(lastPos, i)));
379
+ lastPos = 0;
281
380
  }
282
381
  }
283
- /*char = data.charCodeAt(lastPos)
284
- // Remove preceeding whitespace
285
- while (isSpace(char)) {
286
- lastPos++;
287
- char = data.charCodeAt(lastPos);
382
+ for (; i > lastPos; i--) {
383
+ char = unsafeCharCodeAt(data, i);
384
+ if (char !== rightBracketCode) {
385
+ result.push(parseNumber<valueof<T>>(data.slice(lastPos, i + 1)));
386
+ break;
387
+ }
288
388
  }
289
- char = data.charCodeAt(--len);
290
- while (isSpace(char)) {
291
- len--;
292
- char = data.charCodeAt(len);
293
- }*/
294
-
295
- // @ts-ignore
296
- // Handle empty arrays
297
- data = data.slice(lastPos, len).trim();
298
- // @ts-ignore
299
- if (data.length != 0) result.push(JSON.parse<valueof<T>>(data));
300
- //if (data.length != 0) console.log(`Trailing-${data.slice(lastPos, len).trim()}-`)
301
389
  return result;
302
390
  }
303
391
 
304
- export function parseObject(data: string): void {
305
- //const obj = instantiate<T>()
306
- const result = new Array<string>();
307
- let lastPos: u32 = 0;
308
- let char: u32 = 0;
309
- let i: u32 = 1;
310
- let key: string = "";
311
- let isKey: boolean = false;
312
- for (; i < u32(data.length - 1); i++) {
313
- char = data.charCodeAt(i);
314
- if (isKey == false) {
315
- if (char == colonCode) {
316
- key = data.slice(lastPos + 2, i - 1);
317
- //console.log(`Found Key: ${key}`);
318
- result.push(key);
319
- lastPos = ++i;
320
- isKey = true;
392
+ // @ts-ignore
393
+ @inline
394
+ export function parseArrayArray<T extends unknown[][]>(data: string): T {
395
+ const result = instantiate<T>();
396
+ let char = 0;
397
+ let lastPos = 0;
398
+ let depth = 1;
399
+ let i = 1;
400
+ // Find start of bracket
401
+ //for (; unsafeCharCodeAt(data, i) !== leftBracketCode; i++) {}
402
+ //i++;
403
+ for (; i < data.length - 1; i++) {
404
+ char = unsafeCharCodeAt(data, i);
405
+ if (char === leftBracketCode) {
406
+ if (depth === 1) {
407
+ lastPos = i;
321
408
  }
322
- } else {
323
- if (char == commaCode) {
324
- const val = data.slice(lastPos, i);
409
+ // Shifting is 6% faster than incrementing
410
+ depth = depth << 1;
411
+ } else if (char === rightBracketCode) {
412
+ depth = depth >> 1;
413
+ if (depth === 1) {
414
+ i++;
415
+ result.push(JSON.parse<valueof<T>>(data.slice(lastPos, i)));
416
+ }
417
+ }
418
+ }
419
+ return result;
420
+ }
421
+
422
+ // @ts-ignore
423
+ @inline
424
+ export function parseObjectArray<T extends unknown[][]>(data: string): T {
425
+ const result = instantiate<T>();
426
+ let char = 0;
427
+ let lastPos = 1;
428
+ let depth = 1;
429
+ let i = 1;
430
+ // Find start of bracket
431
+ //for (; unsafeCharCodeAt(data, i) !== leftBracketCode; i++) { }
432
+ //i++;
433
+ for (; i < data.length - 1; i++) {
434
+ char = unsafeCharCodeAt(data, i);
435
+ if (char === leftBraceCode) {
436
+ if (depth === 1) {
325
437
  lastPos = i;
326
- isKey = false;
327
- //console.log(`Found Val: ${val}`)
328
- result.push(val);
438
+ }
439
+ // Shifting is 6% faster than incrementing
440
+ depth = depth << 1;
441
+ } else if (char === rightBraceCode) {
442
+ depth = depth >> 1;
443
+ if (depth === 1) {
444
+ i++;
445
+ result.push(JSON.parse<valueof<T>>(data.slice(lastPos, i)));
329
446
  }
330
447
  }
331
448
  }
332
- result.push(data.slice(lastPos, data.length - 1));
333
- //console.log(stringify(result))
334
- //return obj
449
+ return result;
335
450
  }
336
451
 
337
- class Nullable { }
452
+ class Nullable {}