json-as 0.2.6 → 0.4.2

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,778 +1,337 @@
1
- import { StringSink } from 'as-string-sink'
2
- import { DynamicObject } from './DynamicObject'
3
- import { unknown, unknownTypes } from './unknown'
4
- export { unknown, unknownTypes } from './unknown'
5
- export { DynamicObject as Object } from './DynamicObject'
6
-
7
- @global
8
- export class Nullable { }
9
-
10
- const quote = '"'
11
- const lbracket = "["
12
- const rbracket = "]"
13
- const rcbracket = "}"
14
- const lcbracket = "{"
15
- const trueVal = "true"
16
- const falseVal = "false"
17
- const nullVal = "null"
18
- const escapeQuote = '\\"'
19
- const quoteCode: u16 = 34// '"'
20
- const commaCode: u16 = 44// ","
21
- const rbracketCode: u16 = 93// "]"
22
- const lbracketCode: u16 = 91// "["
23
- const rcbracketCode: u16 = 125// "}"
24
- const lcbracketCode: u16 = 123// "{"
25
- const colonCode: u16 = 58// ":"
26
- const fwd_slashCode: u16 = 92// "/"
27
- const t_charCode: u16 = 116// "t"
28
- const f_charCode: u16 = 116// "t"
29
- const nCode: u16 = 110// "n"
30
- const unknownId = idof<unknown>()
31
- const stringId = idof<string>()
32
- const objectId = idof<DynamicObject>()
33
- const arrayStringId = idof<string[]>()
34
- const arrayBooleanId = idof<boolean[]>()
35
- const arrayBoolId = idof<bool[]>()
36
- const arrayU8Id = idof<u8[]>()
37
- const arrayU16Id = idof<u16[]>()
38
- const arrayU32Id = idof<u32[]>()
39
- const arrayU64Id = idof<u64[]>()
40
- const arrayI8Id = idof<i8[]>()
41
- const arrayI16Id = idof<i16[]>()
42
- const arrayI32Id = idof<i32[]>()
43
- const arrayI64Id = idof<i64[]>()
44
- const arrayF32Id = idof<f32[]>()
45
- const arrayF64Id = idof<f64[]>()
46
- const arrayUnknownId = idof<unknown[]>()
47
- const WS1code = " ".charCodeAt(0)
48
- const WS2code = '\u0020'.charCodeAt(0)
49
- const WS3code = '\u000A'.charCodeAt(0)
50
- const WS4code = '\u000D'.charCodeAt(0)
51
- const WS5code = '\u0009'.charCodeAt(0)
52
- const unknownTrue = unknown.wrap(true)
53
- const unknownFalse = unknown.wrap(false)
54
- const unknownNull = unknown.wrap(null)
55
- const fwd_slash = "\\"
56
- const empty_stringCode = " ".charCodeAt(0)
57
-
58
- /**
59
- * JSON Encoder/Decoder for AssemblyScript
60
- */
61
- export namespace JSON {
62
- /**
63
- * Stringifies valid JSON data.
64
- * ```js
65
- * JSON.stringify<T>(data)
66
- * ```
67
- * @param data unknown
68
- * @returns string
69
- */
70
- export function stringify<T = Nullable | null>(data: T): string {
71
- // @ts-ignore
72
- if (isString(data)) {
73
- return serializeString(<string>data)
74
- } else if (isBoolean(data)) {
75
- return serializeBoolean(data)
76
- // @ts-ignore
77
- } else if (isNullable(data) && data == null) {
78
- return nullVal
79
- } else if (isFloat(data) || isInteger(data)) {
80
- return data.toString()
81
- } else if (isArrayLike(data)) {
82
- // @ts-ignore
83
- return serializeArray<T>(data)
84
- } else if (data instanceof unknown) {
85
- return serializeUnknown(data)
86
- } else if (data instanceof DynamicObject) {
87
- return serializeDynamicObject(data)
88
- }
89
-
90
- // @ts-ignore
91
- if (data.__encoded.length === 0) data.__encode()
92
- // @ts-ignore
93
- return lcbracket + data.__encoded + rcbracket
94
- }
95
- /**
96
- * Parses valid JSON strings into their original format.
97
- * Useful for exchanging data and cloning.
98
- * ```js
99
- * JSON.parse<T>(data)
100
- * ```
101
- * @param data string
102
- * @returns T
103
- */
104
- export function parse<T = Nullable | null>(data: string): T {
105
- const char = data.charCodeAt(0)
106
- let type!: T
107
- // @ts-ignore
108
- if (isString<T>()) return parseString(data.trim())
109
- // @ts-ignore
110
- else if (isBoolean<T>()) return parseBoolean(data.trimLeft())
111
- // @ts-ignore
112
- else if (isArrayLike<T>()) return parseArray<T>(data)
113
- // @ts-ignore
114
- else if (isNullable<T>() && char === nCode) return null
115
- // @ts-ignore
116
- else if (isFloat<T>() || isInteger<T>()) return parseNumber<T>(data.trim())
117
- // @ts-ignore
118
- else if (type instanceof unknown) return parseUnknown(data)
119
- // @ts-ignore
120
- else if (type instanceof DynamicObject) return parseDynamicObject(data)
121
- // @ts-ignore
122
- return parseObject<T>(data)
123
- }
124
- }
125
-
126
- export function serializeDynamicObject(data: DynamicObject): string {
127
- const result = new StringSink(lcbracket)
128
- const keys = DynamicObject.keys(data)
129
- const values = DynamicObject.values(data)
130
- const len: u32 = keys.length - 1
131
- if (len === -1) return '{}'
132
- for (let i: u32 = 0; i < len; i++) {
133
- result.write(`"${unchecked(keys[i])}":${serializeUnknown(unchecked(values[i]))},`)
134
- }
135
- result.write(`"${unchecked(keys[len])}":${serializeUnknown(unchecked(values[len]))}}`)
136
- return result.toString()
137
- }
138
-
139
- export function serializeUnknown(data: unknown): string {
140
- // @ts-ignore
141
- if (data.type === unknownTypes.null) {
142
- return nullVal
143
- }
144
- // @ts-ignore
145
- else if (data.type === unknownId) {
146
- // @ts-ignore
147
- return serializeUnknown(data.get<unknown>())
148
- }
149
- // @ts-ignore
150
- else if (data.type === stringId) {
151
- // @ts-ignore
152
- return serializeString(data.get<string>())
153
- }
154
- // @ts-ignore
155
- else if (data.type === unknownTypes.boolean) {
156
- // @ts-ignore
157
- return serializeBoolean(data.get<boolean>())
158
- }
159
- // @ts-ignore
160
- else if (data.type === unknownTypes.i8) {
161
- // @ts-ignore
162
- return data.get<i8>().toString()
163
- }
164
- // @ts-ignore
165
- else if (data.type === unknownTypes.i16) {
166
- // @ts-ignore
167
- return data.get<i16>().toString()
168
- }
169
- // @ts-ignore
170
- else if (data.type === unknownTypes.i32) {
171
- // @ts-ignore
172
- return data.get<i32>().toString()
173
- }
174
- // @ts-ignore
175
- else if (data.type === unknownTypes.i64) {
176
- // @ts-ignore
177
- return data.get<i64>().toString()
178
- }
179
- // @ts-ignore
180
- else if (data.type === unknownTypes.u8) {
181
- // @ts-ignore
182
- return data.get<u8>().toString()
183
- }
184
- // @ts-ignore
185
- else if (data.type === unknownTypes.u16) {
186
- // @ts-ignore
187
- return data.get<u16>().toString()
188
- }
189
- // @ts-ignore
190
- else if (data.type === unknownTypes.u32) {
191
- // @ts-ignore
192
- return data.get<u32>().toString()
193
- }
194
- // @ts-ignore
195
- else if (data.type === unknownTypes.u64) {
196
- // @ts-ignore
197
- return data.get<u64>().toString()
198
- }
199
- // @ts-ignore
200
- else if (data.type === unknownTypes.f32) {
201
- // @ts-ignore
202
- return data.get<f32>().toString()
203
- }
204
- // @ts-ignore
205
- else if (data.type === unknownTypes.f64) {
206
- // @ts-ignore
207
- return data.get<f64>().toString()
208
- }
209
- // @ts-ignore
210
- else if (data.type === objectId) {
211
- // @ts-ignore
212
- return serializeDynamicObject(data.get<DynamicObject>())
213
- }
214
- // @ts-ignore
215
- else if (data.type === arrayUnknownId) {
216
- // @ts-ignore
217
- return serializeUnknownArray(data.get<unknown[]>())
218
- }
219
- // @ts-ignore
220
- else if (data.type === arrayBoolId) {
221
- // @ts-ignore
222
- return serializeBooleanArray(data.get<bool[]>())
223
- }
224
- // @ts-ignore
225
- else if (data.type === arrayBooleanId) {
226
- // @ts-ignore
227
- return serializeBooleanArray(data.get<boolean[]>())
228
- }
229
- // @ts-ignore
230
- else if (data.type === arrayF32Id) {
231
- // @ts-ignore
232
- return serializeNumberArray<f32[]>(data.get<f32[]>())
233
- }
234
- // @ts-ignore
235
- else if (data.type === arrayF64Id) {
236
- // @ts-ignore
237
- return serializeNumberArray<f64[]>(data.get<f64[]>())
238
- }
239
- // @ts-ignore
240
- else if (data.type === arrayU8Id) {
241
- // @ts-ignore
242
- return serializeNumberArray<u8[]>(data.get<u8[]>())
243
- }
244
- // @ts-ignore
245
- else if (data.type === arrayU16Id) {
246
- // @ts-ignore
247
- return serializeNumberArray<u16[]>(data.get<u16[]>())
248
- }
249
- // @ts-ignore
250
- else if (data.type === arrayU32Id) {
251
- // @ts-ignore
252
- return serializeNumberArray<u32[]>(data.get<u32[]>())
253
- }
254
- // @ts-ignore
255
- else if (data.type === arrayU64Id) {
256
- // @ts-ignore
257
- return serializeNumberArray<u64[]>(data.get<u64[]>())
258
- }
259
- // @ts-ignore
260
- else if (data.type === arrayI8Id) {
261
- // @ts-ignore
262
- return serializeNumberArray<i8[]>(data.get<i8[]>())
263
- }
264
- // @ts-ignore
265
- else if (data.type === arrayI16Id) {
266
- // @ts-ignore
267
- return serializeNumberArray<i16[]>(data.get<i16[]>())
268
- }
269
- // @ts-ignore
270
- else if (data.type === arrayI32Id) {
271
- // @ts-ignore
272
- return serializeNumberArray<i32[]>(data.get<i32[]>())
273
- }
274
- // @ts-ignore
275
- else if (data.type === arrayI64Id) {
276
- // @ts-ignore
277
- return serializeNumberArray<i64[]>(data.get<i64[]>())
278
- }
279
- // @ts-ignore
280
- else if (data.type === arrayStringId) {
281
- // @ts-ignore
282
- return serializeStringArray(data.get<string[]>())
283
- }
284
- // @ts-ignore
285
- else {
286
- return nullVal
287
- }
288
- }
289
-
290
- export function serializeNumber<T>(data: T): string {
291
- // @ts-ignore
292
- return data.toString()
293
- }
294
-
295
- export function serializeString(data: string): string {
296
- return quote + data.replaceAll(quote, escapeQuote) + quote
297
- }
298
-
299
- export function serializeBoolean(data: number): string {
300
- return data ? trueVal : falseVal
301
- }
302
-
303
- function serializeStringArray(data: string[]): string {
304
- const result = new StringSink(lbracket)
305
- const len: u32 = data.length - 1
306
- for (let i: u32 = 0; i < len; i++) {
307
- result.write(serializeString(unchecked(data[i])))
308
- result.writeCodePoint(commaCode)
309
- }
310
- result.write(serializeString(unchecked(data[len])))
311
- result.writeCodePoint(rbracketCode)
312
- return result.toString()
313
- }
314
-
315
- function serializeNumberArray<T extends Array<any>>(data: T): string {
316
- const result = new StringSink(lbracket)
317
- const len: u32 = data.length - 1
318
- for (let i: u32 = 0; i < len; i++) {
319
- result.write(serializeNumber<valueof<T>>(unchecked(data[i])))
320
- result.writeCodePoint(commaCode)
321
- }
322
- result.write(serializeNumber<valueof<T>>(unchecked(data[len])))
323
- result.writeCodePoint(rbracketCode)
324
- return result.toString()
325
- }
326
-
327
- function serializeBooleanArray(data: boolean[]): string {
328
- const result = new StringSink(lbracket)
329
- const len: u32 = data.length - 1
330
- for (let i: u32 = 0; i < len; i++) {
331
- // @ts-ignore
332
- result.write(serializeBoolean(unchecked(data[i])))
333
- result.writeCodePoint(commaCode)
334
- }
335
- // @ts-ignore
336
- result.write(serializeBoolean(unchecked(data[len])))
337
- result.writeCodePoint(rbracketCode)
338
- return result.toString()
339
- }
340
-
341
- function serializeUnknownArray(data: unknown[]): string {
342
- const result = new StringSink(lbracket)
343
- const len: u32 = data.length - 1
344
- for (let i: u32 = 0; i < len; i++) {
345
- result.write(serializeUnknown(unchecked(data[i])))
346
- result.writeCodePoint(commaCode)
347
- }
348
- result.write(serializeUnknown(unchecked(data[len])))
349
- result.writeCodePoint(rbracketCode)
350
- return result.toString()
351
- }
352
-
353
- function serializeDeepArray<T extends Array<any>>(data: T): string {
354
- const result = new StringSink(lbracket)
355
- const len: u32 = data.length - 1
356
- for (let i: u32 = 0; i < len; i++) {
357
- result.write(serializeArray<valueof<T>>(unchecked(data[i])))
358
- result.writeCodePoint(commaCode)
359
- }
360
- result.write(serializeArray<valueof<T>>(unchecked(data[len])))
361
- result.writeCodePoint(rbracketCode)
362
- return result.toString()
363
- }
364
-
365
- function serializeObjectArray<T extends Array<any>>(data: T): string {
366
- const result = new StringSink(lbracket)
367
- const len: u32 = data.length - 1
368
- let obj!: valueof<T>
369
- for (let i: u32 = 0; i < len; i++) {
370
- obj = unchecked(data[i])
371
- if (obj.__encoded.length === 0) obj.__encode()
372
- result.write(obj.__encoded)
373
- result.writeCodePoint(commaCode)
374
- }
375
- obj = unchecked(data[len])
376
- if (obj.__encoded.length === 0) obj.__encode()
377
- result.write(obj.__encoded)
378
- result.writeCodePoint(rbracketCode)
379
- return result.toString()
380
- }
381
-
382
- function serializeDynamicObjectArray(data: DynamicObject[]): string {
383
- const result = new StringSink(lbracket)
384
- const len: u32 = data.length - 1
385
- for (let i: u32 = 0; i < len; i++) {
386
- result.write(serializeDynamicObject(unchecked(data[i])))
387
- result.writeCodePoint(commaCode)
388
- }
389
- result.write(serializeDynamicObject(unchecked(data[len])))
390
- result.writeCodePoint(rbracketCode)
391
- return result.toString()
392
- }
393
-
394
- export function serializeArray<T extends Array<any>>(data: T): string {
395
- let type!: valueof<T>
396
- const len = data.length - 1;
397
- if (len === -1) return lbracket + rbracket
398
- let result = new StringSink(lbracket);
399
- if (isString<valueof<T>>()) {
400
- for (let i = 0; i < len; i++) {
401
- result.write(serializeString(unchecked(data[i])))
402
- result.writeCodePoint(commaCode)
403
- }
404
- result.write(serializeString(unchecked(data[len])))
405
- result.writeCodePoint(rbracketCode)
406
- return result.toString()
407
- } else if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) {
408
- for (let i = 0; i < len; i++) {
409
- result.write(serializeNumber<valueof<T>>(unchecked(data[i])))
410
- result.writeCodePoint(commaCode)
411
- }
412
- result.write(serializeNumber<valueof<T>>(unchecked(data[len])))
413
- result.writeCodePoint(rbracketCode)
414
- return result.toString()
415
- } else if (isBoolean<valueof<T>>()) {
416
- for (let i = 0; i < len; i++) {
417
- result.write(serializeBoolean(unchecked(data[i])))
418
- result.writeCodePoint(commaCode)
419
- }
420
- result.write(serializeBoolean(unchecked(data[len])))
421
- result.writeCodePoint(rbracketCode)
422
- return result.toString()
423
- // @ts-ignore
424
- } else if (type instanceof unknown) {
425
- for (let i = 0; i < len; i++) {
426
- result.write(serializeUnknown(unchecked(data[i])))
427
- result.writeCodePoint(commaCode)
428
- }
429
- result.write(serializeUnknown(unchecked(data[len])))
430
- result.writeCodePoint(rbracketCode)
431
- return result.toString()
432
- } else if (isArray<valueof<T>>()) {
433
- for (let i = 0; i < len; i++) {
434
- result.write(serializeArray<valueof<T>>(unchecked(data[i])))
435
- result.writeCodePoint(commaCode)
436
- }
437
- result.write(serializeArray<valueof<T>>(unchecked(data[len])))
438
- result.writeCodePoint(rbracketCode)
439
- return result.toString()
440
- // @ts-ignore
441
- } else if (type instanceof DynamicObject) {
442
- for (let i = 0; i < len; i++) {
443
- result.write(serializeDynamicObject(unchecked(data[i])))
444
- result.writeCodePoint(commaCode)
445
- }
446
- result.write(serializeDynamicObject(unchecked(data[len])))
447
- result.writeCodePoint(rbracketCode)
448
- return result.toString()
449
- } else {
450
- for (let i = 0; i < len; i++) {
451
- const elem = unchecked(data[i])
452
- // @ts-ignore
453
- if (elem.__encoded.length === 0) elem.__encode()
454
- }
455
- // @ts-ignore
456
- result.write(elem.__encoded)
457
- result.writeCodePoint(rcbracketCode)
458
- }
459
- return result.toString()
460
- }
461
-
462
- export function parseUnknown(data: string): unknown {
463
- const char = data.charCodeAt(0)
464
- // @ts-ignore
465
- if (char === quoteCode) return unknown.wrap(parseString(data))
466
- // @ts-ignore
467
- else if (char === t_charCode) return unknownTrue
468
- else if (char === f_charCode) return unknownFalse
469
- // @ts-ignore
470
- else if (char === lbracketCode) return unknown.wrap(parseUnknownArray(data))
471
- // @ts-ignore
472
- else if (char === nCode) return unknownNull
473
- // @ts-ignore
474
- //else if (char === lcbracketCode) return parseObject<T>(data)
475
- // @ts-ignore
476
- return parseUnknownNumber(data)
477
- }
478
-
479
-
480
- export function parseBoolean(data: string): boolean {
481
- return data.charCodeAt(0) === t_charCode ? true : false
482
- }
483
-
484
- export function parseString(data: string): string {
485
- return data.slice(1, data.length - 1).replaceAll(escapeQuote, quote)
486
- }
487
-
488
- export function parseNumber<T>(data: string): T {
489
- let type: T
490
- // @ts-ignore
491
- if (type instanceof f64) return F64.parseFloat(data)
492
- // @ts-ignore
493
- else if (type instanceof f32) return F32.parseFloat(data)
494
- // @ts-ignore
495
- else if (type instanceof i32) return I32.parseInt(data)
496
- // @ts-ignore
497
- else if (type instanceof u32) return U32.parseInt(data)
498
- // @ts-ignore
499
- else if (type instanceof u64) return U64.parseInt(data)
500
- // @ts-ignore
501
- else if (type instanceof i64) return I64.parseInt(data)
502
- // @ts-ignore
503
- else if (type instanceof u8) return U8.parseInt(data)
504
- // @ts-ignore
505
- else if (type instanceof u16) return U16.parseInt(data)
506
- // @ts-ignore
507
- else if (type instanceof i16) return I16.parseInt(data)
508
- // @ts-ignore
509
- return I8.parseInt(data)
510
- }
511
-
512
- export function parseUnknownNumber(data: string): unknown {
513
- // @ts-ignore
514
- if (data.includes('.')) return unknown.wrap(F64.parseFloat(data))
515
- // @ts-ignore
516
- return unknown.wrap(I64.parseInt(data))
517
- }
518
-
519
- export function parseArray<T extends Array<unknown>>(data: string): T {
520
- let type!: valueof<T>
521
- // @ts-ignore
522
- if (isString<valueof<T>>()) return parseStringArray(data)
523
- // @ts-ignore
524
- else if (isBoolean<valueof<T>>()) return parseBooleanArray(data)
525
- // @ts-ignore
526
- else if (isArray<valueof<T>>()) return parseArrayArray<T>(data)
527
- // @ts-ignore
528
- else if (type instanceof unknown) return parseUnknownArray(data)
529
- // @ts-ignore
530
- return parseNumberArray<valueof<T>>(data)
531
- }
532
-
533
- export function parseStringArray(data: string): Array<string> {
534
- const result = new Array<string>()
535
- if (data.length === 2) return result
536
- let lastPos: u32 = 1
537
- let char: u32 = 0
538
- let instr: boolean = false
539
- for (let i: u32 = 1; i < u32(data.length - 1); i++) {
540
- char = data.charCodeAt(i)
541
- // This ignores [ and ] or { and } if they are inside a string.
542
- if (char === quoteCode && data.charCodeAt(i - 1) !== fwd_slashCode) instr = instr ? false : true
543
- if (instr === false) {
544
- // Handles whitespace after a comma
545
- if (char === WS1code || char === WS2code || char === WS3code || char === WS4code || char === WS5code) lastPos++
546
- if (char === quoteCode) {
547
- result.push(parseString(data.slice(lastPos, i + 1)))
548
- lastPos = i + 2
549
- }
550
- }
551
- }
552
- return result
553
- }
554
-
555
- export function parseBooleanArray(data: string): Array<boolean> {
556
- const result = new Array<boolean>()
557
- if (data.length === 2) return result
558
- let char: u32 = 0
559
- let instr: boolean = false
560
- for (let i: u32 = 1; i < u32(data.length - 1); i++) {
561
- char = data.charCodeAt(i)
562
- if (instr === false) {
563
- if (char === t_charCode) {
564
- result.push(true)
565
- i += 4
566
- } else {
567
- result.push(false)
568
- i += 5
569
- }
570
- }
571
- }
572
- return result
573
- }
574
-
575
- export function parseNumberArray<T>(data: string): Array<T> {
576
- const result = new Array<T>()
577
- if (data.length === 2) return result
578
- let lastPos: u32 = 0
579
- let char: u32 = 0
580
- let i: u32 = 1
581
- for (; i < u32(data.length - 1); i++) {
582
- char = data.charCodeAt(i)
583
- if (char === commaCode) {
584
- //console.log('Found number: ' + data.slice(lastPos + 1, i))
585
- result.push(parseNumber<T>(data.slice(lastPos + 1, i)))
586
- lastPos = i
587
- }
588
- }
589
- // console.log('Found number: ' + data.slice(lastPos + 1, i))
590
- result.push(parseNumber<T>(data.slice(lastPos + 1, i)))
591
- return result
592
- }
593
-
594
- export function parseUnknownArray(data: string): Array<unknown> {
595
- const result = new Array<unknown>()
596
- if (data.length === 2) return result
597
- let lastPos: i32 = 0
598
- let char: u32 = 0
599
- let depth: u32 = 0
600
- let fdepth: u32 = 0
601
- let instr: boolean = false
602
- let i: u32 = 1
603
- for (; i < u32(data.length - 1); i++) {
604
- char = data.charCodeAt(i)
605
- // This ignores [ and ] if they are inside a string.
606
- if (char === quoteCode && data.charCodeAt(i - 1) !== fwd_slashCode) {
607
- if (instr === true) {
608
- instr = false
609
- } else {
610
- instr = true
611
- }
612
- }
613
- if (instr === false) {
614
- if (char === commaCode && depth === 0 && fdepth === 0) {
615
- //console.log('Normal chunk: ' + data.slice(lastPos + 1, i))
616
- result.push(parseUnknown(data.slice(lastPos + 1, i).trim()))
617
- lastPos = i
618
- } else if (char === lbracketCode) depth++
619
- else if (char === rbracketCode) fdepth++
620
- else if (depth !== 0 && depth === fdepth) {
621
- //console.log('Deep chunk: ' + data.slice(lastPos + 1, i))
622
- result.push(unknown.wrap(parseUnknownArray(data.slice(lastPos + 1, i).trim())))
623
- // Reset the depth
624
- depth = 0
625
- fdepth = 0
626
- // Set new lastPos
627
- lastPos = i
628
- }
629
- }
630
- }
631
- // console.log('Last chunk: ' + data.slice(lastPos + 1, data.length - 1).trim())
632
- result.push(parseUnknown(data.slice(lastPos + 1, data.length - 1).trim()))
633
- return result
634
- }
635
-
636
- export function parseArrayArray<T extends Array<unknown>>(data: string): T {
637
- const result = instantiate<T>()
638
- if (data.length === 2) return result
639
- let lastPos: i32 = -1
640
- let char: u32 = 0
641
- let depth: u32 = 0
642
- let fdepth: u32 = 0
643
- let instr: u32 = 0
644
- for (let i: u32 = 1; i < u32(data.length - 1); i++) {
645
- char = data.charCodeAt(i)
646
- // This ignores [ and ] if they are inside a string.
647
- if (char === quoteCode && data.charCodeAt(i - 1) !== fwd_slashCode) instr = instr ? 0 : 1
648
- // This gets the depth of the array.
649
- if (instr === 0) {
650
- if (char === lbracketCode) depth++
651
- if (char === rbracketCode) fdepth++
652
- // If the depth and found depth are equal, that is an array. Push it.
653
- if (depth !== 0 && depth === fdepth) {
654
- result.push(
655
- // @ts-ignore
656
- parseArray<valueof<T>>(data.slice(lastPos + 2, i + 1))
657
- )
658
- // Reset the depth
659
- depth = 0
660
- fdepth = 0
661
- // Set new lastPos
662
- lastPos = i
663
- }
664
- }
665
- }
666
- // Return the final array
667
- return result
668
- }
669
-
670
- export function parseObject<T>(data: string): T {
671
- //console.log('Data ' + data)
672
- data = removeJSONWhitespace(data)
673
- const len: u32 = data.length - 1
674
- let schema: T
675
- const result = new Map<string, string>()
676
- let lastPos: u32 = 1
677
- let key: string = ''
678
- let instr: u32 = 0
679
- let char: u32 = 0
680
- let depth: u32 = 0
681
- let fdepth: u32 = 0
682
- for (let i: u32 = 1; i < len; i++) {
683
- char = data.charCodeAt(i)
684
- if (char === quoteCode && data.charCodeAt(i - 1) !== fwd_slashCode) instr = (instr ? 0 : 1)
685
- else if (instr === 0) {
686
- if (char === lcbracketCode || char === lbracketCode) depth++
687
- if (char === rcbracketCode || char === rbracketCode) fdepth++
688
- }
689
- if (depth !== 0 && depth === fdepth) {
690
- //console.log(`Deep: ${data.slice(lastPos + 1, i + 1)}`)
691
- result.set(key, data.slice(lastPos + 1, i + 1).trim())
692
- // Reset the depth
693
- depth = 0
694
- fdepth = 0
695
- // Set new lastPos
696
- lastPos = i + 1
697
- }
698
- if (depth === 0) {
699
- if (char === colonCode) {
700
- //console.log(`Key: ${data.slice(lastPos + 1, i - 1)}`)
701
- key = data.slice(lastPos + 1, i - 1).trim()
702
- lastPos = i
703
- }
704
- else if (char === commaCode) {
705
- //console.log(`Value: ${data.slice(lastPos + 1, i)}`)
706
- if ((i - lastPos) > 0) result.set(key, data.slice(lastPos + 1, i).trim())
707
- lastPos = i + 1
708
- }
709
- }
710
- }
711
- //console.log(`Trailing: ${data.slice(lastPos + 1, len)}\n\t\sValid: ${data.slice(lastPos + 1, len).length > 0}`)
712
-
713
- if ((len - lastPos) > 0) result.set(key, data.slice(lastPos + 1, len).trim())
714
- // @ts-ignore
715
- return schema.__decode(result)
716
- }
717
-
718
- export function parseDynamicObject(data: string): DynamicObject {
719
- const len: u32 = data.length - 1
720
- if (len === 1) return new DynamicObject()
721
- const result = new Map<string, unknown>()
722
- let lastPos: u32 = 1
723
- let key: string = ''
724
- let instr: u32 = 0
725
- let char: u32 = 0
726
- let depth: u32 = 0
727
- let fdepth: u32 = 0
728
- for (let i: u32 = 1; i < len; i++) {
729
- char = data.charCodeAt(i)
730
- if (char === quoteCode && data.charCodeAt(i - 1) !== fwd_slashCode) instr = (instr ? 0 : 1)
731
- else if (instr === 0) {
732
- if (char === lcbracketCode || char === lbracketCode) depth++
733
- if (char === rcbracketCode || char === rbracketCode) fdepth++
734
- }
735
- if (depth !== 0 && depth === fdepth) {
736
- result.set(key, unknown.wrap(data.slice(lastPos + 1, i + 1).trim()))
737
- // Reset the depth
738
- depth = 0
739
- fdepth = 0
740
- // Set new lastPos
741
- lastPos = i + 2
742
- }
743
- if (depth === 0) {
744
- if (char === colonCode) {
745
- key = data.slice(lastPos + 1, i - 1).trim()
746
- lastPos = i + 1
747
- }
748
- else if (char === commaCode) {
749
- if ((i - lastPos) > 0) result.set(key, unknown.wrap(data.slice(lastPos + 1, i).trim()))
750
- lastPos = i + 1
751
- }
752
- }
753
- }
754
-
755
- if ((len - lastPos) > 0) result.set(key, unknown.wrap(data.slice(lastPos + 1, len - 1).trim()))
756
- const o = new DynamicObject()
757
- // @ts-ignore
758
- o.__data = result
759
- return o
760
- }
761
-
762
- export function removeJSONWhitespace(data: string): string {
763
- let result = new StringSink()
764
- let instr = false
765
- let char = 0
766
- for (let i = 0; i < data.length; i++) {
767
- char = data.charCodeAt(i)
768
- if (char === quoteCode && data.charCodeAt(i - 1) === fwd_slashCode) {
769
- instr = !instr
770
- }
771
- if (instr === true) {
772
- result.writeCodePoint(char)
773
- } else if (instr === false && char !== empty_stringCode) {
774
- result.writeCodePoint(char)
775
- }
776
- }
777
- return result.toString()
778
- }
1
+ import { StringSink } from "as-string-sink/assembly";
2
+ import { Variant } from "as-variant/assembly";
3
+ import {
4
+ backSlashCode,
5
+ colonCode,
6
+ commaCode,
7
+ leftBraceCode,
8
+ leftBracketCode,
9
+ quoteCode,
10
+ rightBraceCode,
11
+ rightBracketCode
12
+ } from "./chars";
13
+
14
+ /**
15
+ * JSON Encoder/Decoder for AssemblyScript
16
+ */
17
+ export namespace JSON {
18
+ export type _Variant = Variant;
19
+ /**
20
+ * Stringifies valid JSON data.
21
+ * ```js
22
+ * JSON.stringify<T>(data)
23
+ * ```
24
+ * @param data T
25
+ * @returns string
26
+ */
27
+ export function stringify<T = Nullable | null>(data: T): string {
28
+ // String
29
+ if (isString(data)) {
30
+ return serializeString(<string>data);
31
+ }
32
+ // Boolean
33
+ else if (isBoolean(data)) {
34
+ return data ? "true" : "false";
35
+ }
36
+ // Null
37
+ else if (isNullable<T>() && data == null) {
38
+ return "null";
39
+ }
40
+ // Integers/Floats
41
+ // @ts-ignore
42
+ else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
43
+ // @ts-ignore
44
+ return data.toString();
45
+ }
46
+ // Class-Based serialization
47
+ // @ts-ignore
48
+ else if (isDefined(data.__JSON_Serialize)) {
49
+ //@ts-ignore
50
+ return data.__JSON_Serialize();
51
+ }
52
+ // ArrayLike
53
+ else if (isArrayLike(data)) {
54
+ let result = new StringSink("[");
55
+ if (data.length == 0) return "[]";
56
+ for (let i = 0; i < data.length - 1; i++) {
57
+ result.write(stringify(unchecked(data[i])));
58
+ result.write(",");
59
+ }
60
+ result.write(stringify(unchecked(data[data.length - 1])));
61
+ result.write("]");
62
+ return result.toString();
63
+ } else {
64
+ return "null";
65
+ }
66
+ }
67
+ /**
68
+ * Parses valid JSON strings into their original format.
69
+ * ```js
70
+ * JSON.parse<T>(data)
71
+ * ```
72
+ * @param data string
73
+ * @returns T
74
+ */
75
+ export function parse<T = Variant>(data: string): T {
76
+ let type!: T;
77
+ if (isString<T>()) {
78
+ // @ts-ignore
79
+ return parseString(data);
80
+ } else if (isBoolean<T>()) {
81
+ // @ts-ignore
82
+ return parseBoolean<T>(data);
83
+ } else if (isFloat<T>() || isInteger<T>()) {
84
+ return parseNumber<T>(data);
85
+ } else if (isArrayLike<T>()) {
86
+ return parseArray<T>(data);
87
+ // @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))
126
+ // @ts-ignore
127
+ return schema.__JSON_Deserialize(result)
128
+ } else {
129
+ // @ts-ignore
130
+ return null;
131
+ }
132
+ }
133
+ }
134
+
135
+ // @ts-ignore
136
+ @inline
137
+ function serializeString(data: string): string {
138
+ return '"' + data.replaceAll('"', '\\"') + '"';
139
+ }
140
+ // @ts-ignore
141
+ @inline
142
+ function parseString(data: string): string {
143
+ return data.slice(1, data.length - 1).replaceAll('\\"', '"');
144
+ }
145
+
146
+ // @ts-ignore
147
+ @inline
148
+ function parseBoolean<T extends boolean>(data: string): T {
149
+ if (data.length > 3 && data.startsWith("true")) return <T>true;
150
+ else if (data.length > 4 && data.startsWith("false")) return <T>false;
151
+ else throw new Error(`JSON: Cannot parse "${data}" as boolean`);
152
+ }
153
+ // @ts-ignore
154
+ @inline
155
+ function parseNumber<T>(data: string): T {
156
+ let type: T;
157
+ // @ts-ignore
158
+ if (type instanceof f64) return F64.parseFloat(data);
159
+ // @ts-ignore
160
+ else if (type instanceof f32) return F32.parseFloat(data);
161
+ // @ts-ignore
162
+ else if (type instanceof i32) return I32.parseInt(data);
163
+ // @ts-ignore
164
+ else if (type instanceof u32) return U32.parseInt(data);
165
+ // @ts-ignore
166
+ else if (type instanceof u8) return U8.parseInt(data);
167
+ // @ts-ignore
168
+ else if (type instanceof u16) return U16.parseInt(data);
169
+ // @ts-ignore
170
+ else if (type instanceof i16) return I16.parseInt(data);
171
+ // @ts-ignore
172
+ else if (type instanceof i8) return I8.parseInt(data);
173
+ else
174
+ throw new Error(
175
+ `JSON: Cannot parse invalid data into a number. Either "${data}" is not a valid number, or <${nameof<T>()}> is an invald number type.`
176
+ );
177
+ }
178
+
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;
194
+ }
195
+ }
196
+ //console.log(data.slice(lastPos, data.length - 1))
197
+ // @ts-ignore
198
+ result.push(
199
+ // @ts-ignore
200
+ parseNumber<valueof<T>>(data.slice(lastPos, data.length - 1).trimStart())
201
+ );
202
+ return result;
203
+ }
204
+
205
+ // @ts-ignore
206
+ @inline
207
+ export function parseBooleanArray<T>(data: string): T {
208
+ 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;
219
+ }
220
+ }
221
+ // @ts-ignore
222
+ result.push(
223
+ // @ts-ignore
224
+ parseBoolean<valueof<T>>(data.slice(lastPos, data.length - 1).trimStart())
225
+ );
226
+ return result;
227
+ }
228
+
229
+ // @ts-ignore
230
+ @inline
231
+ export function parseArray<T>(data: string): T {
232
+ 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
+ }
281
+ }
282
+ }
283
+ /*char = data.charCodeAt(lastPos)
284
+ // Remove preceeding whitespace
285
+ while (isSpace(char)) {
286
+ lastPos++;
287
+ char = data.charCodeAt(lastPos);
288
+ }
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
+ return result;
302
+ }
303
+
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;
321
+ }
322
+ } else {
323
+ if (char == commaCode) {
324
+ const val = data.slice(lastPos, i);
325
+ lastPos = i;
326
+ isKey = false;
327
+ //console.log(`Found Val: ${val}`)
328
+ result.push(val);
329
+ }
330
+ }
331
+ }
332
+ result.push(data.slice(lastPos, data.length - 1));
333
+ //console.log(stringify(result))
334
+ //return obj
335
+ }
336
+
337
+ class Nullable { }