Comparing version
@@ -0,0 +0,0 @@ { |
{ | ||
"targets": { | ||
"debug": { | ||
"outFile": "build/debug.wasm", | ||
"textFile": "build/debug.wat", | ||
"sourceMap": true, | ||
"debug": true | ||
}, | ||
"release": { | ||
"outFile": "build/release.wasm", | ||
"textFile": "build/release.wat", | ||
"sourceMap": true, | ||
"optimizeLevel": 3, | ||
"shrinkLevel": 0, | ||
"converge": false, | ||
"noAssert": false | ||
}, | ||
"test": { | ||
"outFile": "build/test.wasm", | ||
"sourceMap": true, | ||
"sourceMap": false, | ||
"optimizeLevel": 0, | ||
@@ -22,0 +7,0 @@ "shrinkLevel": 0, |
@@ -1,2 +0,3 @@ | ||
import { JSON } from ".."; | ||
import { JSON, parseBooleanArray, parseObject, parseStringArray } from ".."; | ||
@json | ||
@@ -17,2 +18,6 @@ class Vec2 { | ||
bench("Parse String", () => { | ||
blackbox(JSON.parse<string>(blackbox('"Hello"'))); | ||
}); | ||
bench("Stringify Boolean", () => { | ||
@@ -22,2 +27,6 @@ blackbox(JSON.stringify(blackbox(true))); | ||
bench("Parse Boolean", () => { | ||
blackbox(JSON.parse<boolean>(blackbox("true"))); | ||
}); | ||
bench("Stringify Integer", () => { | ||
@@ -27,2 +36,6 @@ blackbox(JSON.stringify(blackbox(314))); | ||
bench("Parse Integer", () => { | ||
blackbox(JSON.parse<i32>(blackbox("314"))); | ||
}); | ||
bench("Stringify Float", () => { | ||
@@ -32,2 +45,6 @@ blackbox(JSON.stringify(blackbox(3.14))); | ||
bench("Parse Float", () => { | ||
blackbox(JSON.parse<f32>(blackbox("3.14"))); | ||
}); | ||
bench("Stringify Object (Vec2)", () => { | ||
@@ -37,2 +54,6 @@ blackbox(JSON.stringify(vec)); | ||
bench("Parse Object (Vec2)", () => { | ||
blackbox(parseObject<Vec2>(blackbox('{"x":0.0,"y":0.0}'))); | ||
}); | ||
bench("Stringify Array", () => { | ||
@@ -42,34 +63,18 @@ blackbox(JSON.stringify(blackbox([1, 2, 3, 4, 5]))); | ||
bench("Parse String", () => { | ||
blackbox(JSON.parse<string>(blackbox('"Hello"'))); | ||
bench("Parse Array", () => { | ||
blackbox(JSON.parse<i32[]>(blackbox("[1,2,3,4]"))); | ||
}); | ||
bench("Parse Boolean", () => { | ||
blackbox(JSON.parse<boolean>(blackbox("true"))); | ||
}); | ||
bench("Parse Integer", () => { | ||
blackbox(JSON.parse<i32>(blackbox("314"))); | ||
}); | ||
bench("Parse Float", () => { | ||
blackbox(JSON.parse<f32>(blackbox("3.14"))); | ||
}); | ||
bench("Parse Object (Vec2)", () => { | ||
blackbox(JSON.parse<Vec2>(blackbox('{"x":0.0,"y":0.0}'))); | ||
}); | ||
bench("Parse Boolean Array", () => { | ||
bench("Stringify Nested Array", () => { | ||
blackbox( | ||
JSON.parse<boolean[]>(blackbox("[true,false,true,false,true]")) | ||
JSON.stringify<string[][]>( | ||
blackbox([ | ||
["a", "b", "c"], | ||
["d", "e", "f"], | ||
]) | ||
) | ||
); | ||
}); | ||
bench("Parse Integer Array", () => { | ||
blackbox(JSON.parse<u32[]>(blackbox("[1,2,3,4,5]"))); | ||
bench("Parse Nested Array", () => { | ||
blackbox(JSON.parse<string[][]>(blackbox('[["a","b","c"],["d","e","f"]]'))); | ||
}); | ||
bench("Parse Float Array", () => { | ||
blackbox(JSON.parse<f32[]>(blackbox("[1.0,2.0,3.0,4.0,5.0]"))); | ||
}); |
export const commaCode = ",".charCodeAt(0); | ||
export const quoteCode = "\"".charCodeAt(0); | ||
export const quoteCode = '"'.charCodeAt(0); | ||
export const backSlashCode = "\\".charCodeAt(0); | ||
export const forwardSlashCode = "/".charCodeAt(0); | ||
export const leftBraceCode = "{".charCodeAt(0); | ||
@@ -5,0 +6,0 @@ export const rightBraceCode = "}".charCodeAt(0); |
import { StringSink } from "as-string-sink/assembly"; | ||
import { Variant } from "as-variant/assembly"; | ||
import { isSpace } from "assemblyscript/std/assembly/util/string"; | ||
import { stringify } from "as-console/assembly"; | ||
import { | ||
@@ -7,2 +9,5 @@ backSlashCode, | ||
commaCode, | ||
eCode, | ||
fCode, | ||
forwardSlashCode, | ||
leftBraceCode, | ||
@@ -12,5 +17,6 @@ leftBracketCode, | ||
rightBraceCode, | ||
rightBracketCode | ||
rightBracketCode, | ||
tCode, | ||
} from "./chars"; | ||
import { removeWhitespace } from "./util"; | ||
import { removeWhitespace, unsafeCharCodeAt } from "./util"; | ||
@@ -20,4 +26,24 @@ /** | ||
*/ | ||
export namespace JSON { | ||
export type _Variant = Variant; | ||
export class JSON { | ||
private static parseObjectValue<T>(data: string): T { | ||
let type!: T; | ||
if (isString<T>()) { | ||
// @ts-ignore | ||
return data.replaceAll('\\"', '"'); | ||
} else if (isBoolean<T>()) { | ||
// @ts-ignore | ||
return parseBoolean<T>(data); | ||
} else if (isFloat<T>() || isInteger<T>()) { | ||
return parseNumber<T>(data); | ||
} else if (isArrayLike<T>()) { | ||
// @ts-ignore | ||
return parseArray<T>(data); | ||
// @ts-ignore | ||
} else if (isDefined(type.__JSON_Deserialize)) { | ||
return parseObject<T>(data); | ||
} else { | ||
// @ts-ignore | ||
return null; | ||
} | ||
} | ||
/** | ||
@@ -31,3 +57,3 @@ * Stringifies valid JSON data. | ||
*/ | ||
export function stringify<T = Nullable | null>(data: T): string { | ||
static stringify<T = Nullable | null>(data: T): string { | ||
// String | ||
@@ -84,4 +110,3 @@ if (isString<T>()) { | ||
*/ | ||
export function parse<T = Variant>(data: string): T { | ||
data = removeWhitespace(data); | ||
static parse<T = Variant>(data: string): T { | ||
let type!: T; | ||
@@ -97,49 +122,7 @@ if (isString<T>()) { | ||
} else if (isArrayLike<T>()) { | ||
return parseArray<T>(data); | ||
// @ts-ignore | ||
return parseArray<T>(data.trimStart()); | ||
// @ts-ignore | ||
} else if (isDefined(type.__JSON_Deserialize)) { | ||
const len: u32 = data.length - 1 | ||
let schema!: T | ||
const result = new Map<string, string>() | ||
let lastPos: u32 = 1 | ||
let key: string = '' | ||
let instr: boolean = false | ||
let char: u32 = 0 | ||
let depth: u32 = 0 | ||
let fdepth: u32 = 0 | ||
for (let i: u32 = 1; i < len; i++) { | ||
char = data.charCodeAt(i); | ||
if (instr === false && char === quoteCode) instr = true; | ||
else if (instr === true && char === quoteCode && data.charCodeAt(i - 1) !== "/".charCodeAt(0)) instr = false; | ||
if (instr === false) { | ||
if (char === leftBraceCode || char === leftBracketCode) depth++ | ||
if (char === rightBraceCode || char === rightBracketCode) fdepth++ | ||
} | ||
if (depth !== 0 && depth === fdepth) { | ||
//console.log(`Found Struct: ${data.slice(lastPos + 1, i + 1)}`) | ||
result.set(key, data.slice(lastPos + 1, i + 1)) | ||
// Reset the depth | ||
depth = 0 | ||
fdepth = 0 | ||
// Set new lastPos | ||
lastPos = i + 1 | ||
} | ||
if (!instr && depth === 0) { | ||
if (char === colonCode) { | ||
key = data.slice(lastPos + 1, i - 1) | ||
//console.log(`Found Key: ${data.slice(lastPos + 1, i - 1)}`) | ||
lastPos = i | ||
} else if (char === commaCode) { | ||
//console.log(`Found Comma: ${data.slice(lastPos + 1, i)}`) | ||
if ((i - lastPos) > 0) result.set(key, data.slice(lastPos + 1, i)) | ||
lastPos = i + 1 | ||
} | ||
} | ||
} | ||
if ((len - lastPos) > 1 && (len - lastPos) !== 0) { | ||
result.set(key, data.slice(lastPos + 1, len)) | ||
} | ||
// @ts-ignore | ||
return schema.__JSON_Deserialize(result) | ||
return parseObject<T>(data.trimStart()); | ||
} else { | ||
@@ -152,6 +135,5 @@ // @ts-ignore | ||
// @ts-ignore | ||
@inline | ||
function parseString(data: string): string { | ||
function parseString(data: string): string { | ||
return data.slice(1, data.length - 1).replaceAll('\\"', '"'); | ||
@@ -162,3 +144,3 @@ } | ||
@inline | ||
function parseBoolean<T extends boolean>(data: string): T { | ||
function parseBoolean<T extends boolean>(data: string): T { | ||
if (data.length > 3 && data.startsWith("true")) return <T>true; | ||
@@ -168,5 +150,6 @@ else if (data.length > 4 && data.startsWith("false")) return <T>false; | ||
} | ||
// @ts-ignore | ||
@inline | ||
function parseNumber<T>(data: string): T { | ||
function parseNumber<T>(data: string): T { | ||
let type: T; | ||
@@ -195,25 +178,196 @@ // @ts-ignore | ||
export function parseObject<T>(data: string): T { | ||
let schema!: T; | ||
const result = new Map<string, string>(); | ||
let key: usize = 0; | ||
let depth = 1; | ||
let char = 0; | ||
for ( | ||
let outerLoopIndex = 1; | ||
outerLoopIndex < data.length - 1; | ||
outerLoopIndex++ | ||
) { | ||
char = unsafeCharCodeAt(data, outerLoopIndex); | ||
if (char === leftBracketCode) { | ||
for ( | ||
let arrayValueIndex = outerLoopIndex; | ||
arrayValueIndex < data.length - 1; | ||
arrayValueIndex++ | ||
) { | ||
char = unsafeCharCodeAt(data, arrayValueIndex); | ||
if (char === leftBracketCode) { | ||
depth = depth << 1; | ||
} else if (char === rightBracketCode) { | ||
depth = depth >> 1; | ||
if (depth === 1) { | ||
++arrayValueIndex; | ||
result.set( | ||
changetype<string>(key), | ||
data.slice(outerLoopIndex, arrayValueIndex) | ||
); | ||
outerLoopIndex = arrayValueIndex; | ||
key = 0; | ||
break; | ||
} | ||
} | ||
} | ||
} else if (char === leftBraceCode) { | ||
for ( | ||
let objectValueIndex = outerLoopIndex; | ||
objectValueIndex < data.length - 1; | ||
objectValueIndex++ | ||
) { | ||
char = unsafeCharCodeAt(data, objectValueIndex); | ||
if (char === leftBraceCode) { | ||
depth = depth << 1; | ||
} else if (char === rightBraceCode) { | ||
depth = depth >> 1; | ||
if (depth === 1) { | ||
++objectValueIndex; | ||
result.set( | ||
changetype<string>(key), | ||
data.slice(outerLoopIndex, objectValueIndex) | ||
); | ||
outerLoopIndex = objectValueIndex; | ||
key = 0; | ||
break; | ||
} | ||
} | ||
} | ||
} else if (char === quoteCode) { | ||
for ( | ||
let stringValueIndex = ++outerLoopIndex; | ||
stringValueIndex < data.length - 1; | ||
stringValueIndex++ | ||
) { | ||
char = unsafeCharCodeAt(data, stringValueIndex); | ||
if ( | ||
char === quoteCode && | ||
unsafeCharCodeAt(data, stringValueIndex - 1) !== backSlashCode | ||
) { | ||
if (key === 0) { | ||
key = changetype<usize>( | ||
data.slice(outerLoopIndex, stringValueIndex) | ||
); | ||
} else { | ||
result.set( | ||
changetype<string>(key), | ||
data.slice(outerLoopIndex, stringValueIndex) | ||
); | ||
key = 0; | ||
} | ||
outerLoopIndex = ++stringValueIndex; | ||
break; | ||
} | ||
} | ||
} else if ( | ||
char === tCode && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "r".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "u".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === eCode | ||
) { | ||
result.set(changetype<string>(key), "true"); | ||
key = 0; | ||
} else if ( | ||
char === fCode && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "a".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "l".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "s".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === eCode | ||
) { | ||
result.set(changetype<string>(key), "false"); | ||
key = 0; | ||
} else if (char >= 48 && char <= 57) { | ||
let numberValueIndex = ++outerLoopIndex; | ||
for (; numberValueIndex < data.length - 1; numberValueIndex++) { | ||
char = unsafeCharCodeAt(data, numberValueIndex); | ||
if ( | ||
char === commaCode || | ||
isSpace(char) || | ||
numberValueIndex == data.length - 2 | ||
) { | ||
result.set( | ||
changetype<string>(key), | ||
data.slice(outerLoopIndex - 1, numberValueIndex) | ||
); | ||
outerLoopIndex = numberValueIndex; | ||
key = 0; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
// @ts-ignore | ||
return schema.__JSON_Deserialize(result); | ||
} | ||
// @ts-ignore | ||
@inline | ||
export function parseNumberArray<T>(data: string): T { | ||
// @ts-ignore | ||
export function parseArray<T extends unknown[]>(data: string): T { | ||
// TODO: Replace with opt | ||
let type!: valueof<T>; | ||
if (type instanceof String) { | ||
return <T>parseStringArray(data); | ||
} else if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) { | ||
// @ts-ignore | ||
return parseNumberArray<T>(data); | ||
} else if (isBoolean<valueof<T>>()) { | ||
// @ts-ignore | ||
return parseBooleanArray<T>(data); | ||
} else if (isArrayLike<valueof<T>>()) { | ||
// @ts-ignore | ||
return parseArrayArray<T>(data); | ||
// @ts-ignore | ||
} else if (isDefined(type.__JSON_Deserialize)) { | ||
// @ts-ignore | ||
return parseObjectArray<T>(data); | ||
} | ||
} | ||
// @ts-ignore | ||
@inline | ||
export function parseStringArray(data: string): string[] { | ||
const result: string[] = []; | ||
let lastPos = 0; | ||
let instr = false; | ||
for (let i = 1; i < data.length - 1; i++) { | ||
if (unsafeCharCodeAt(data, i) === quoteCode) { | ||
if (instr === false) { | ||
instr = true; | ||
lastPos = i; | ||
} else if (unsafeCharCodeAt(data, i - 1) !== backSlashCode) { | ||
instr = false; | ||
result.push(data.slice(lastPos + 1, i).replaceAll('\\"', '"')); | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
// @ts-ignore | ||
@inline | ||
export function parseBooleanArray<T extends boolean[]>(data: string): T { | ||
const result = instantiate<T>(); | ||
if (data.length == 0) return result; | ||
let lastPos: u32 = 1; | ||
let i: u32 = 1; | ||
let char: u32 = 0; | ||
for (; i < u32(data.length - 1); i++) { | ||
char = data.charCodeAt(i); | ||
if (char == commaCode) { | ||
// console.log(data.slice(lastPos, i)) | ||
// @ts-ignore | ||
result.push(parseNumber<valueof<T>>(data.slice(lastPos, i).trim())); | ||
lastPos = ++i; | ||
let lastPos = 1; | ||
let char = 0; | ||
for (let i = 1; i < data.length - 1; i++) { | ||
char = unsafeCharCodeAt(data, i); | ||
/*// if char == "t" && i+3 == "e" | ||
if (char === tCode && data.charCodeAt(i + 3) === eCode) { | ||
//i += 3; | ||
result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i+2))); | ||
//i++; | ||
} else if (char === fCode && data.charCodeAt(i + 4) === eCode) { | ||
//i += 4; | ||
result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i+3))); | ||
//i++; | ||
}*/ | ||
if (char === tCode || char === fCode) { | ||
lastPos = i; | ||
} else if (char === eCode) { | ||
i++; | ||
result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i))); | ||
} | ||
} | ||
//console.log(data.slice(lastPos, data.length - 1)) | ||
// @ts-ignore | ||
result.push( | ||
// @ts-ignore | ||
parseNumber<valueof<T>>(data.slice(lastPos, data.length - 1).trimStart()) | ||
); | ||
return result; | ||
@@ -224,21 +378,23 @@ } | ||
@inline | ||
export function parseBooleanArray<T>(data: string): T { | ||
export function parseNumberArray<T extends number[]>(data: string): T { | ||
const result = instantiate<T>(); | ||
if (data.length == 0) return result; | ||
let lastPos: u32 = 1; | ||
let i: u32 = 1; | ||
let char: u32 = 0; | ||
for (; i < u32(data.length - 1); i++) { | ||
char = data.charCodeAt(i); | ||
if (char == commaCode) { | ||
// @ts-ignore | ||
result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i).trimStart())); | ||
lastPos = ++i; | ||
let lastPos = 0; | ||
let char = 0; | ||
let i = 1; | ||
for (; i < data.length - 1; i++) { | ||
char = unsafeCharCodeAt(data, i); | ||
if (lastPos === 0 && char >= 48 && char <= 57) { | ||
lastPos = i; | ||
} else if ((isSpace(char) || char == commaCode) && lastPos > 0) { | ||
result.push(parseNumber<valueof<T>>(data.slice(lastPos, i))); | ||
lastPos = 0; | ||
} | ||
} | ||
// @ts-ignore | ||
result.push( | ||
// @ts-ignore | ||
parseBoolean<valueof<T>>(data.slice(lastPos, data.length - 1).trimStart()) | ||
); | ||
for (; i > lastPos; i--) { | ||
char = unsafeCharCodeAt(data, i); | ||
if (char !== rightBracketCode) { | ||
result.push(parseNumber<valueof<T>>(data.slice(lastPos, i + 1))); | ||
break; | ||
} | ||
} | ||
return result; | ||
@@ -249,75 +405,60 @@ } | ||
@inline | ||
export function parseArray<T>(data: string): T { | ||
export function parseArrayArray<T extends unknown[][]>(data: string): T { | ||
const result = instantiate<T>(); | ||
data = data.trim(); | ||
let len: u32 = data.length - 1; | ||
let lastPos: u32 = 1; | ||
let i: u32 = 1; | ||
let char: u32 = 0; | ||
// Is struct such as Object, or Array | ||
let isStruct: boolean = false; | ||
let isStr: boolean = false; | ||
//let offset: u32 = 0; | ||
// Depth for finding matching brackets | ||
let inDepth: u32 = 0; | ||
let outDepth: u32 = 0; | ||
for (; i < len; i++) { | ||
char = data.charCodeAt(i); | ||
if (char == quoteCode && data.charCodeAt(i - 1) != backSlashCode) | ||
isStr = !isStr; | ||
if (char == leftBraceCode || char == leftBracketCode) { | ||
inDepth++; | ||
isStruct = true; | ||
} else if (char == rightBraceCode || char == rightBracketCode) { | ||
outDepth++; | ||
isStruct = true; | ||
let char = 0; | ||
let lastPos = 0; | ||
let depth = 1; | ||
let i = 1; | ||
// Find start of bracket | ||
//for (; unsafeCharCodeAt(data, i) !== leftBracketCode; i++) {} | ||
//i++; | ||
for (; i < data.length - 1; i++) { | ||
char = unsafeCharCodeAt(data, i); | ||
if (char === leftBracketCode) { | ||
if (depth === 1) { | ||
lastPos = i; | ||
} | ||
// Shifting is 6% faster than incrementing | ||
depth = depth << 1; | ||
} else if (char === rightBracketCode) { | ||
depth = depth >> 1; | ||
if (depth === 1) { | ||
i++; | ||
result.push(JSON.parse<valueof<T>>(data.slice(lastPos, i))); | ||
} | ||
} | ||
if (!isStr) { | ||
if (!isStruct) { | ||
// This removes whitespace before and after an element | ||
/*if (offset != 0 && isSpace(char)) { | ||
lastPos++; | ||
} else { | ||
if (isSpace(char)) offset++; | ||
}*/ | ||
// This checks to see if we are dealing with structures such as Objects and Arrays | ||
if (char == commaCode) { | ||
// @ts-ignore | ||
result.push(JSON.parse<valueof<T>>(data.slice(lastPos, i).trim())); | ||
//offset = 0; | ||
lastPos = i + 1; | ||
} | ||
} else { | ||
if (inDepth == outDepth) { | ||
i++; | ||
//console.log(`Struct-${data.slice(lastPos, i).trim()}-`) | ||
lastPos = i + 1; | ||
inDepth = 0; | ||
outDepth = 0; | ||
isStruct = false; | ||
} | ||
} | ||
return result; | ||
} | ||
// @ts-ignore | ||
@inline | ||
export function parseObjectArray<T extends unknown[][]>(data: string): T { | ||
const result = instantiate<T>(); | ||
let char = 0; | ||
let lastPos = 1; | ||
let depth = 1; | ||
let i = 1; | ||
// Find start of bracket | ||
//for (; unsafeCharCodeAt(data, i) !== leftBracketCode; i++) { } | ||
//i++; | ||
for (; i < data.length - 1; i++) { | ||
char = unsafeCharCodeAt(data, i); | ||
if (char === leftBraceCode) { | ||
if (depth === 1) { | ||
lastPos = i; | ||
} | ||
// Shifting is 6% faster than incrementing | ||
depth = depth << 1; | ||
} else if (char === rightBraceCode) { | ||
depth = depth >> 1; | ||
if (depth === 1) { | ||
i++; | ||
result.push(JSON.parse<valueof<T>>(data.slice(lastPos, i))); | ||
} | ||
} | ||
} | ||
/*char = data.charCodeAt(lastPos) | ||
// Remove preceeding whitespace | ||
while (isSpace(char)) { | ||
lastPos++; | ||
char = data.charCodeAt(lastPos); | ||
} | ||
char = data.charCodeAt(--len); | ||
while (isSpace(char)) { | ||
len--; | ||
char = data.charCodeAt(len); | ||
}*/ | ||
// @ts-ignore | ||
// Handle empty arrays | ||
data = data.slice(lastPos, len).trim(); | ||
// @ts-ignore | ||
if (data.length != 0) result.push(JSON.parse<valueof<T>>(data)); | ||
//if (data.length != 0) console.log(`Trailing-${data.slice(lastPos, len).trim()}-`) | ||
return result; | ||
} | ||
class Nullable { } | ||
class Nullable {} |
import "wasi"; | ||
import { JSON } from "."; | ||
import { | ||
JSON, | ||
parseArrayArray, | ||
parseNumberArray, | ||
parseObject, | ||
parseObjectArray, | ||
} from "."; | ||
import { removeWhitespace } from "./util"; | ||
@@ -8,4 +14,4 @@ | ||
class Vec2 { | ||
x: f32 | ||
y: f32 | ||
x: f32; | ||
y: f32; | ||
} | ||
@@ -16,7 +22,8 @@ | ||
class Player { | ||
firstName: string | ||
lastName: string | ||
lastActive: i32[] | ||
age: i32 | ||
pos: Vec2 | ||
firstName: string; | ||
lastName: string; | ||
lastActive: i32[]; | ||
age: i32; | ||
pos: Vec2; | ||
isVerified: boolean; | ||
} | ||
@@ -31,48 +38,51 @@ | ||
x: -3.4, | ||
y: 1.2 | ||
} | ||
} | ||
y: 1.2, | ||
}, | ||
isVerified: true, | ||
}; | ||
const stringified = JSON.stringify<Player>(data); | ||
// { | ||
// "firstName": "Emmet", | ||
// "lastName": "West", | ||
// "lastActive": [8, 27, 2022], | ||
// "age": 23, | ||
// "pos": { | ||
// "x": -3.4000000953674318, | ||
// "y": 1.2000000476837159 | ||
// } | ||
// } | ||
console.log(`Stringified: ${stringified}`); | ||
console.log(`Whitespace: ${removeWhitespace(`{ | ||
"firstName": "Emmet", | ||
"lastName": "West", | ||
"lastActive": [8, 27, 2022], | ||
"age": 23, | ||
"pos": { | ||
"x": -3.4000000953674318, | ||
"y": 1.2000000476837159 | ||
} | ||
}`)}`) | ||
const parsed = JSON.parse<Player>(`{ | ||
"firstName": "Emmet", | ||
"lastName": "West", | ||
"lastActive": [8, 27, 2022], | ||
"age": 23, | ||
"pos": { | ||
"x": -3.4000000953674318, | ||
"y": 1.2000000476837159 | ||
} | ||
}`); | ||
// Player { | ||
// firstName: "Emmet", | ||
// lastName: "West", | ||
// lastActive: [8, 27, 2022], | ||
// age: 23, | ||
// pos: { | ||
// x: -3.4000000953674318, | ||
// y: 1.2000000476837159 | ||
// } | ||
// } | ||
console.log(`Parsed: ${JSON.stringify(parsed)}`); | ||
const serialized = JSON.stringify<Player>(data); | ||
console.log("Serialized: " + serialized); | ||
const deserialized = JSON.parse<Player>(serialized); | ||
console.log("Deserialized: " + JSON.stringify(deserialized)); | ||
/* | ||
const parsed = JSON.parse<Player>(stringified); | ||
console.log("Vec2 Parse: " + JSON.stringify<Player>(parsed)); | ||
console.log( | ||
`Parsed String Array: ${JSON.stringify( | ||
JSON.parse<string[]>(`\n[ "hello" , "world" ] `) | ||
)}` | ||
); | ||
console.log( | ||
`Parsed Boolean Array: ${JSON.stringify( | ||
JSON.parse<boolean[]>(`\n[ false , true ] `) | ||
)}` | ||
); | ||
console.log( | ||
`Parsed Number Array: ${JSON.stringify( | ||
JSON.parse<i32[]>(`[ 1 , 2\n ,3\n\t ]`) | ||
)}` | ||
); | ||
console.log( | ||
JSON.stringify<Vec2>( | ||
load<Vec2>(changetype<usize>(data), offsetof<Player>("pos")) | ||
) | ||
); | ||
console.log( | ||
JSON.stringify<string[][]>( | ||
parseArrayArray<string[][]>('[["a","b","c"],["d","e","f"]]') | ||
) | ||
); | ||
console.log( | ||
JSON.stringify<Player[][]>( | ||
parseObjectArray<Player[][]>( | ||
'[{"firstName":"Emmet","lastName":"West","lastActive":[8,7],"age":23,"pos":{"x":-3.4000000953674318,"y":1.2000000476837159}}]' | ||
) | ||
) | ||
); | ||
*/ |
import { StringSink } from "as-string-sink/assembly"; | ||
import { isSpace } from "assemblyscript/std/assembly/util/string"; | ||
import { backSlashCode, quoteCode } from "./chars"; | ||
// @ts-ignore | ||
@inline | ||
export function unsafeCharCodeAt(data: string, pos: i32): i32 { | ||
return load<u16>(changetype<usize>(data) + ((<usize>pos) << 1)); | ||
} | ||
export function removeWhitespace(data: string): string { | ||
const result = new StringSink() | ||
let instr = false; | ||
for (let i = 0; i < data.length; i++) { | ||
const char = data.charCodeAt(i); | ||
if (instr === false && char === quoteCode) instr = true | ||
else if (instr === true && char === quoteCode && data.charCodeAt(i - 1) !== backSlashCode) instr = false; | ||
const result = new StringSink(); | ||
let instr = false; | ||
for (let i = 0; i < data.length; i++) { | ||
const char = data.charCodeAt(i); | ||
if (instr === false && char === quoteCode) instr = true; | ||
else if ( | ||
instr === true && | ||
char === quoteCode && | ||
data.charCodeAt(i - 1) !== backSlashCode | ||
) | ||
instr = false; | ||
if (instr === false) { | ||
if (!isSpace(char)) result.write(data.charAt(i)) | ||
} else { | ||
result.write(data.charAt(i)) | ||
} | ||
if (instr === false) { | ||
if (!isSpace(char)) result.write(data.charAt(i)); | ||
} else { | ||
result.write(data.charAt(i)); | ||
} | ||
return result.toString() | ||
} | ||
} | ||
return result.toString(); | ||
} |
{ | ||
"name": "json-as", | ||
"version": "0.4.4", | ||
"version": "0.4.5", | ||
"description": "JSON encoder/decoder for AssemblyScript", | ||
@@ -17,3 +17,4 @@ "types": "assembly/index.ts", | ||
"test:lunatic": "lunatic ./build/test.wasm", | ||
"test:wasm3": "wasm3 ./build/test.wasm" | ||
"test:wasm3": "wasm3 ./build/test.wasm", | ||
"prettier": "as-prettier -w ." | ||
}, | ||
@@ -41,3 +42,3 @@ "devDependencies": { | ||
"deserialize", | ||
"dynamic", | ||
"dynamic", | ||
"serde" | ||
@@ -44,0 +45,0 @@ ], |
@@ -56,2 +56,3 @@ # AS-JSON | ||
pos: Vec2 | ||
isVerified: boolean | ||
} | ||
@@ -67,3 +68,4 @@ | ||
y: 1.2 | ||
} | ||
}, | ||
isVerified: true | ||
} | ||
@@ -75,3 +77,3 @@ | ||
// "lastName": "West", | ||
// "lastActive": [8, 27, 2022], | ||
// "lastActive": [8, 27, 2022], | ||
// "age": 23, | ||
@@ -81,3 +83,4 @@ // "pos": { | ||
// "y": 1.2000000476837159 | ||
// } | ||
// }, | ||
// "isVerified": true | ||
// } | ||
@@ -95,3 +98,4 @@ console.log(`Stringified: ${stringified}`); | ||
// y: 1.2000000476837159 | ||
// } | ||
// }, | ||
// isVerified: true | ||
// } | ||
@@ -110,3 +114,3 @@ console.log(`Parsed: ${JSON.stringify(parsed)}`); | ||
- Does this support whitespace? | ||
Yes, as-json supports whitespace although the current implementation is deathly slow | ||
Yes, as-json supports whitespace! | ||
- How fast is it? | ||
@@ -113,0 +117,0 @@ Really fast. For example, here are some benchmarks for ser/de a Vec2 with as-json |
@@ -1,2 +0,2 @@ | ||
import { ClassDecorator, registerDecorator } from "visitor-as/dist/decorator.js"; | ||
import { ClassDecorator, registerDecorator, } from "visitor-as/dist/decorator.js"; | ||
import { getName } from "visitor-as/dist/utils.js"; | ||
@@ -54,3 +54,4 @@ import { SimpleParser } from "visitor-as/dist/index.js"; | ||
return { | ||
${ // @ts-ignore | ||
${ | ||
// @ts-ignore | ||
this.decodeStmts.join("")} | ||
@@ -57,0 +58,0 @@ } |
{ | ||
"name": "@json-as/transform", | ||
"version": "0.4.4", | ||
"version": "0.4.5", | ||
"description": "JSON encoder/decoder for AssemblyScript", | ||
@@ -11,3 +11,2 @@ "main": "./lib/index.js", | ||
"license": "MIT", | ||
"scripts": {}, | ||
"devDependencies": {}, | ||
@@ -14,0 +13,0 @@ "dependencies": { |
import { | ||
ClassDeclaration, | ||
FieldDeclaration, | ||
MethodDeclaration, | ||
Source | ||
ClassDeclaration, | ||
FieldDeclaration, | ||
MethodDeclaration, | ||
Source, | ||
} from "assemblyscript/dist/assemblyscript"; | ||
import { ClassDecorator, registerDecorator } from "visitor-as/dist/decorator.js"; | ||
import { | ||
ClassDecorator, | ||
registerDecorator, | ||
} from "visitor-as/dist/decorator.js"; | ||
import { getName } from "visitor-as/dist/utils.js"; | ||
@@ -12,45 +15,48 @@ import { SimpleParser } from "visitor-as/dist/index.js"; | ||
class AsJSONTransform extends ClassDecorator { | ||
public currentClass!: ClassDeclaration; | ||
public sources: Source[] = []; | ||
public encodeStmts: string[] = []; | ||
public decodeStmts: string[] = []; | ||
public currentClass!: ClassDeclaration; | ||
public sources: Source[] = []; | ||
public encodeStmts: string[] = []; | ||
public decodeStmts: string[] = []; | ||
visitMethodDeclaration(node: MethodDeclaration): void {} | ||
visitFieldDeclaration(node: FieldDeclaration): void { | ||
const name = getName(node); | ||
if (!node.type) { | ||
throw new Error(`Field ${name} is missing a type declaration`); | ||
} | ||
visitMethodDeclaration(node: MethodDeclaration): void {} | ||
visitFieldDeclaration(node: FieldDeclaration): void { | ||
const name = getName(node); | ||
if (!node.type) { | ||
throw new Error(`Field ${name} is missing a type declaration`); | ||
} | ||
const type = getName(node.type); | ||
const type = getName(node.type); | ||
// @ts-ignore | ||
this.encodeStmts.push( | ||
`"${name}":\${JSON.stringify<${type}>(this.${name})},` | ||
); | ||
// @ts-ignore | ||
this.encodeStmts.push( | ||
`"${name}":\${JSON.stringify<${type}>(this.${name})},` | ||
); | ||
// @ts-ignore | ||
this.decodeStmts.push( | ||
`${name}: JSON.parse<${type}>(values.get("${name}")),\n` | ||
); | ||
// @ts-ignore | ||
this.decodeStmts.push( | ||
`${name}: JSON.parse<${type}>(values.get("${name}")),\n` | ||
); | ||
} | ||
visitClassDeclaration(node: ClassDeclaration): void { | ||
if (!node.members) { | ||
return; | ||
} | ||
visitClassDeclaration(node: ClassDeclaration): void { | ||
if (!node.members) { | ||
return; | ||
} | ||
this.currentClass = node; | ||
this.currentClass = node; | ||
const name = getName(node); | ||
const name = getName(node); | ||
this.visit(node.members); | ||
this.visit(node.members); | ||
const serializedProp = `__JSON_Serialized: string = "";` | ||
const serializedProp = `__JSON_Serialized: string = "";`; | ||
let serializeFunc = `` | ||
let serializeFunc = ``; | ||
if (this.encodeStmts.length > 0) { | ||
const stmt = this.encodeStmts[this.encodeStmts.length - 1]! | ||
this.encodeStmts[this.encodeStmts.length - 1] = stmt!.slice(0, stmt.length - 1) | ||
serializeFunc = ` | ||
if (this.encodeStmts.length > 0) { | ||
const stmt = this.encodeStmts[this.encodeStmts.length - 1]!; | ||
this.encodeStmts[this.encodeStmts.length - 1] = stmt!.slice( | ||
0, | ||
stmt.length - 1 | ||
); | ||
serializeFunc = ` | ||
@inline | ||
@@ -60,5 +66,5 @@ __JSON_Serialize(): string { | ||
} | ||
` | ||
} else { | ||
serializeFunc = ` | ||
`; | ||
} else { | ||
serializeFunc = ` | ||
@inline | ||
@@ -68,31 +74,39 @@ __JSON_Serialize(): string { | ||
} | ||
` | ||
} | ||
`; | ||
} | ||
const deserializeFunc = ` | ||
const deserializeFunc = ` | ||
@inline | ||
__JSON_Deserialize(values: Map<string, string>): ${name} { | ||
return { | ||
${// @ts-ignore | ||
this.decodeStmts.join("")} | ||
${ | ||
// @ts-ignore | ||
this.decodeStmts.join("") | ||
} | ||
} | ||
} | ||
`; | ||
this.encodeStmts = []; | ||
this.decodeStmts = []; | ||
//console.log(serializeFunc, deserializeFunc) | ||
const serializedProperty = SimpleParser.parseClassMember(serializedProp, node); | ||
node.members.push(serializedProperty); | ||
this.encodeStmts = []; | ||
this.decodeStmts = []; | ||
//console.log(serializeFunc, deserializeFunc) | ||
const serializedProperty = SimpleParser.parseClassMember( | ||
serializedProp, | ||
node | ||
); | ||
node.members.push(serializedProperty); | ||
const serializeMethod = SimpleParser.parseClassMember(serializeFunc, node); | ||
node.members.push(serializeMethod); | ||
const serializeMethod = SimpleParser.parseClassMember(serializeFunc, node); | ||
node.members.push(serializeMethod); | ||
const deserializeMethod = SimpleParser.parseClassMember(deserializeFunc, node); | ||
node.members.push(deserializeMethod); | ||
} | ||
get name(): string { | ||
return "json"; | ||
} | ||
const deserializeMethod = SimpleParser.parseClassMember( | ||
deserializeFunc, | ||
node | ||
); | ||
node.members.push(deserializeMethod); | ||
} | ||
get name(): string { | ||
return "json"; | ||
} | ||
} | ||
export default registerDecorator(new AsJSONTransform()); | ||
export default registerDecorator(new AsJSONTransform()); |
51023
5.07%1374
10.27%114
3.64%