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