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