Comparing version
@@ -75,2 +75,2 @@ import { JSON } from ".."; | ||
blackbox(JSON.parse<f32>(blackbox("3.14"))); | ||
}); | ||
}); |
@@ -1,6 +0,6 @@ | ||
import { JSON } from ".." | ||
import { JSON } from ".."; | ||
function canSerde<T>(data: T): void { | ||
const serialized = JSON.stringify<T>(data); | ||
const deserialized = JSON.stringify<T>(JSON.parse<T>(serialized)); | ||
expect(serialized).toBe(deserialized); | ||
const serialized = JSON.stringify<T>(data); | ||
const deserialized = JSON.stringify<T>(JSON.parse<T>(serialized)); | ||
expect(serialized).toBe(deserialized); | ||
} | ||
@@ -10,101 +10,150 @@ | ||
@json | ||
class Vec2 { | ||
x: f32; | ||
y: f32; | ||
class Vec3 { | ||
x: f32; | ||
y: f32; | ||
z: f32; | ||
} | ||
// @ts-ignore | ||
@json | ||
class Player { | ||
firstName: string; | ||
lastName: string; | ||
lastActive: i32[]; | ||
age: i32; | ||
pos: Vec3 | null; | ||
isVerified: boolean; | ||
} | ||
class Nullable {} | ||
type Null = Nullable | null | ||
type Null = Nullable | null; | ||
describe("Ser/de Nulls", () => { | ||
canSerde<Null>(null) | ||
}) | ||
canSerde<Null>(null); | ||
}); | ||
describe("Ser/de Numbers", () => { | ||
it("should ser/de integers", () => { | ||
canSerde<i32>(0) | ||
it("should ser/de integers", () => { | ||
canSerde<i32>(0); | ||
canSerde<u32>(100) | ||
canSerde<u64>(101) | ||
canSerde<i32>(-100) | ||
canSerde<i64>(-101) | ||
}); | ||
canSerde<u32>(100); | ||
canSerde<u64>(101); | ||
canSerde<i32>(-100); | ||
canSerde<i64>(-101); | ||
}); | ||
it("should ser/de floats", () => { | ||
canSerde<f64>(7.23) | ||
canSerde<f64>(10e2) | ||
canSerde<f64>(10E2) | ||
it("should ser/de floats", () => { | ||
canSerde<f64>(7.23); | ||
canSerde<f64>(10e2); | ||
canSerde<f64>(10e2); | ||
canSerde<f64>(123456e-5) | ||
canSerde<f64>(123456e-5); | ||
canSerde<f64>(123456E-5) | ||
canSerde<f64>(123456e-5); | ||
canSerde<f64>(0.0) | ||
canSerde<f64>(7.23) | ||
}); | ||
canSerde<f64>(0.0); | ||
canSerde<f64>(7.23); | ||
}); | ||
it("should ser/de booleans", () => { | ||
canSerde<bool>(true) | ||
canSerde<bool>(false) | ||
canSerde<boolean>(true) | ||
canSerde<boolean>(false) | ||
}); | ||
it("should ser/de booleans", () => { | ||
canSerde<bool>(true); | ||
canSerde<bool>(false); | ||
canSerde<boolean>(true); | ||
canSerde<boolean>(false); | ||
}); | ||
it("should ser/de strings", () => { | ||
canSerde<string>('abcdefg') | ||
canSerde<string>('st"ring" w""ith quotes"') | ||
canSerde<string>('string \t\r\\"with ran\tdom spa\nces and \nnewlines\n\n\n') | ||
canSerde<string>('string with colon : comma , brace [ ] bracket { } and quote " and other quote \\"') | ||
}); | ||
it("should ser/de strings", () => { | ||
canSerde<string>("abcdefg"); | ||
canSerde<string>('st"ring" w""ith quotes"'); | ||
canSerde<string>( | ||
'string \t\r\\"with ran\tdom spa\nces and \nnewlines\n\n\n' | ||
); | ||
canSerde<string>( | ||
'string with colon : comma , brace [ ] bracket { } and quote " and other quote \\"' | ||
); | ||
}); | ||
}); | ||
}) | ||
describe("Ser/de Array", () => { | ||
it("should ser/de integer arrays", () => { | ||
canSerde<u32[]>([0, 100, 101]) | ||
canSerde<u64[]>([0, 100, 101]) | ||
it("should ser/de integer arrays", () => { | ||
canSerde<u32[]>([0, 100, 101]); | ||
canSerde<u64[]>([0, 100, 101]); | ||
canSerde<i32[]>([0, 100, 101, -100, -101]) | ||
canSerde<i64[]>([0, 100, 101, -100, -101]) | ||
}); | ||
canSerde<i32[]>([0, 100, 101, -100, -101]); | ||
canSerde<i64[]>([0, 100, 101, -100, -101]); | ||
}); | ||
it("should ser/de float arrays", () => { | ||
canSerde<f64[]>([7.23, 10e2, 10E2, 123456e-5, 123456E-5, 0.0, 7.23]) | ||
}) | ||
it("should ser/de float arrays", () => { | ||
canSerde<f64[]>([7.23, 10e2, 10e2, 123456e-5, 123456e-5, 0.0, 7.23]); | ||
}); | ||
it("should ser/de boolean arrays", () => { | ||
canSerde<bool[]>([true, false]) | ||
canSerde<boolean[]>([true, false]) | ||
}) | ||
it("should ser/de boolean arrays", () => { | ||
canSerde<bool[]>([true, false]); | ||
canSerde<boolean[]>([true, false]); | ||
}); | ||
it("should ser/de string arrays", () => { | ||
canSerde<string[]>(["abcdefg", "st\"ring\" w\"\"ith quotes\"", "string \t\r\"with ran\tdom spa\nces and \nnewlines\n\n\n", "string with colon : comma , brace [ ] bracket { } and quote \" and other quote \""]) | ||
}); | ||
it("should ser/de string arrays", () => { | ||
canSerde<string[]>([ | ||
"abcdefg", | ||
'st"ring" w""ith quotes"', | ||
'string \t\r"with ran\tdom spa\nces and \nnewlines\n\n\n', | ||
'string with colon : comma , brace [ ] bracket { } and quote " and other quote "', | ||
]); | ||
}); | ||
it("should ser/de nested integer arrays", () => { | ||
canSerde<i64[][]>([[100, 101], [-100, -101], [0]]) | ||
}); | ||
it("should ser/de nested integer arrays", () => { | ||
canSerde<i64[][]>([[100, 101], [-100, -101], [0]]); | ||
}); | ||
it("should ser/de float arrays", () => { | ||
canSerde<f64[][]>([[7.23], [10e2], [10E2], [123456e-5], [123456E-5], [0.0], [7.23]]) | ||
}) | ||
it("should ser/de float arrays", () => { | ||
canSerde<f64[][]>([ | ||
[7.23], | ||
[10e2], | ||
[10e2], | ||
[123456e-5], | ||
[123456e-5], | ||
[0.0], | ||
[7.23], | ||
]); | ||
}); | ||
it("should ser/de boolean arrays", () => { | ||
canSerde<bool[][]>([[true], [false]]) | ||
canSerde<boolean[][]>([[true], [false]]) | ||
}) | ||
it("should ser/de boolean arrays", () => { | ||
canSerde<bool[][]>([[true], [false]]); | ||
canSerde<boolean[][]>([[true], [false]]); | ||
}); | ||
it("should ser/de string arrays", () => { | ||
canSerde<string[][]>([["abcdefg"], ["st\"ring\" w\"\"ith quotes\""], ["string \t\r\\\"with ran\tdom spa\nces and \nnewlines\n\n\n"], ["string with colon : comma , brace [ ] bracket { } and quote \" and other quote \\\""]]) | ||
}); | ||
it("should ser/de string arrays", () => { | ||
canSerde<string[][]>([ | ||
["abcdefg"], | ||
['st"ring" w""ith quotes"'], | ||
['string \t\r\\"with ran\tdom spa\nces and \nnewlines\n\n\n'], | ||
[ | ||
'string with colon : comma , brace [ ] bracket { } and quote " and other quote \\"', | ||
], | ||
]); | ||
}); | ||
}); | ||
describe("Ser/de Objects", () => { | ||
it("should ser/de Vec2 Objects", () => { | ||
canSerde<Vec2>({ | ||
x: 3.4, | ||
y: 1.2 | ||
}) | ||
}) | ||
}) | ||
it("should ser/de Vec3 Objects", () => { | ||
canSerde<Vec3>({ | ||
x: 3.4, | ||
y: 1.2, | ||
z: 8.3 | ||
}); | ||
}); | ||
it("should ser/de deep objects", () => { | ||
canSerde<Player>({ | ||
firstName: "Emmet", | ||
lastName: "West", | ||
lastActive: [8, 27, 2022], | ||
age: 23, | ||
pos: { | ||
x: 3.4, | ||
y: 1.2, | ||
z: 8.3 | ||
}, | ||
isVerified: true, | ||
}); | ||
}); | ||
}); |
@@ -1,1 +0,1 @@ | ||
export { JSON } from "./json"; | ||
export { JSON } from "./json"; |
import { StringSink } from "as-string-sink/assembly"; | ||
import { isSpace } from "util/string"; | ||
import { Type } from "./type"; | ||
import { | ||
backSlashCode, | ||
colonCode, | ||
commaCode, | ||
eCode, | ||
fCode, | ||
forwardSlashCode, | ||
lCode, | ||
leftBraceCode, | ||
leftBracketCode, | ||
nCode, | ||
quoteCode, | ||
rightBraceCode, | ||
rightBracketCode, | ||
tCode, | ||
uCode, | ||
backSlashCode, | ||
commaCode, | ||
eCode, | ||
fCode, | ||
leftBraceCode, | ||
leftBracketCode, | ||
nCode, | ||
quoteCode, | ||
rightBraceCode, | ||
rightBracketCode, | ||
tCode, | ||
} from "./chars"; | ||
@@ -27,116 +22,116 @@ import { unsafeCharCodeAt } from "./util"; | ||
export class JSON { | ||
/** | ||
* Stringifies valid JSON data. | ||
* ```js | ||
* JSON.stringify<T>(data) | ||
* ``` | ||
* @param data T | ||
* @returns string | ||
*/ | ||
static stringify<T>(data: T): string { | ||
// String | ||
if (isString<T>()) { | ||
return '"' + (<string>data).replaceAll('"', '\\"') + '"'; | ||
} | ||
// Boolean | ||
else if (isBoolean<T>()) { | ||
return data ? "true" : "false"; | ||
} | ||
// Nullable | ||
else if (isNullable<T>() && data == null) { | ||
return "null"; | ||
} | ||
// Integers/Floats | ||
/** | ||
* Stringifies valid JSON data. | ||
* ```js | ||
* JSON.stringify<T>(data) | ||
* ``` | ||
* @param data T | ||
* @returns string | ||
*/ | ||
static stringify<T>(data: T): string { | ||
// String | ||
if (isString<T>()) { | ||
return '"' + (<string>data).replaceAll('"', '\\"') + '"'; | ||
} | ||
// Boolean | ||
else if (isBoolean<T>()) { | ||
return data ? "true" : "false"; | ||
} | ||
// Nullable | ||
else if (isNullable<T>() && data == null) { | ||
return "null"; | ||
} | ||
// Integers/Floats | ||
// @ts-ignore | ||
else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) { | ||
// @ts-ignore | ||
return data.toString(); | ||
} | ||
// Class-Based serialization | ||
// @ts-ignore | ||
else if (isDefined(data.__JSON_Serialize)) { | ||
// @ts-ignore | ||
//if (isNullable<T>()) return "null"; | ||
// @ts-ignore | ||
return data.__JSON_Serialize(); | ||
} | ||
// ArrayLike | ||
else if (isArrayLike<T>()) { | ||
let result = new StringSink("["); | ||
// @ts-ignore | ||
if (data.length == 0) return "[]"; | ||
// @ts-ignore | ||
for (let i = 0; i < data.length - 1; i++) { | ||
// @ts-ignore | ||
else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) { | ||
// @ts-ignore | ||
return data.toString(); | ||
} | ||
// Class-Based serialization | ||
// @ts-ignore | ||
else if (isDefined(data.__JSON_Serialize)) { | ||
// @ts-ignore | ||
//if (isNullable<T>()) return "null"; | ||
// @ts-ignore | ||
return data.__JSON_Serialize(); | ||
} | ||
// ArrayLike | ||
else if (isArrayLike<T>()) { | ||
let result = new StringSink("["); | ||
// @ts-ignore | ||
if (data.length == 0) return "[]"; | ||
// @ts-ignore | ||
for (let i = 0; i < data.length - 1; i++) { | ||
// @ts-ignore | ||
result.write(JSON.stringify(unchecked(data[i])) + ","); | ||
} | ||
// @ts-ignore | ||
result.write(JSON.stringify(unchecked(data[data.length - 1]))); | ||
result.write("]"); | ||
return result.toString(); | ||
} else { | ||
return "null"; | ||
} | ||
result.write(JSON.stringify(unchecked(data[i])) + ","); | ||
} | ||
// @ts-ignore | ||
result.write(JSON.stringify(unchecked(data[data.length - 1]))); | ||
result.write("]"); | ||
return result.toString(); | ||
} else { | ||
return "null"; | ||
} | ||
/** | ||
* Parses valid JSON strings into their original format. | ||
* ```js | ||
* JSON.parse<T>(data) | ||
* ``` | ||
* @param data string | ||
* @returns T | ||
*/ | ||
static parse<T>(data: string): T { | ||
let type!: T; | ||
if (isString<T>()) { | ||
// @ts-ignore | ||
return parseString(data); | ||
} 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.trimStart()); | ||
// @ts-ignore | ||
} else if (isNullable<T>() && data == "null") { | ||
// @ts-ignore | ||
return null | ||
// @ts-ignore | ||
} else if (isDefined(type.__JSON_Deserialize)) { | ||
return parseObject<T>(data.trimStart()); | ||
} else { | ||
// @ts-ignore | ||
return null; | ||
} | ||
} | ||
/** | ||
* Parses valid JSON strings into their original format. | ||
* ```js | ||
* JSON.parse<T>(data) | ||
* ``` | ||
* @param data string | ||
* @returns T | ||
*/ | ||
static parse<T>(data: string): T { | ||
let type!: T; | ||
if (isString<T>()) { | ||
// @ts-ignore | ||
return parseString(data); | ||
} 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.trimStart()); | ||
// @ts-ignore | ||
} else if (isNullable<T>() && data == "null") { | ||
// @ts-ignore | ||
return null; | ||
// @ts-ignore | ||
} else if (isDefined(type.__JSON_Deserialize)) { | ||
return parseObject<T>(data.trimStart()); | ||
} else { | ||
// @ts-ignore | ||
return null; | ||
} | ||
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 (isNullable<T>() && data == "null") { | ||
// @ts-ignore | ||
return null | ||
// @ts-ignore | ||
} else if (isDefined(type.__JSON_Deserialize)) { | ||
// @ts-ignore | ||
//if (isNullable<T>()) return null; | ||
return parseObject<T>(data); | ||
} else { | ||
// @ts-ignore | ||
//return null; | ||
throw new Error(`Could not parse value: ${data}`) | ||
} | ||
} | ||
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 (isNullable<T>() && data == "null") { | ||
// @ts-ignore | ||
return null; | ||
// @ts-ignore | ||
} else if (isDefined(type.__JSON_Deserialize)) { | ||
// @ts-ignore | ||
//if (isNullable<T>()) return null; | ||
return parseObject<T>(data); | ||
} else { | ||
// @ts-ignore | ||
//return null; | ||
throw new Error(`Could not parse value: ${data}`); | ||
} | ||
} | ||
} | ||
@@ -146,4 +141,4 @@ | ||
@inline | ||
function parseString(data: string): string { | ||
return data.slice(1, data.length - 1).replaceAll('\\"', '"'); | ||
function parseString(data: string): string { | ||
return data.slice(1, data.length - 1).replaceAll('\\"', '"'); | ||
} | ||
@@ -153,6 +148,6 @@ | ||
@inline | ||
function parseBoolean<T extends boolean>(data: string): T { | ||
if (data.length > 3 && data.startsWith("true")) return <T>true; | ||
else if (data.length > 4 && data.startsWith("false")) return <T>false; | ||
else throw new Error(`JSON: Cannot parse "${data}" as boolean`); | ||
function parseBoolean<T extends boolean>(data: string): T { | ||
if (data.length > 3 && data.startsWith("true")) return <T>true; | ||
else if (data.length > 4 && data.startsWith("false")) return <T>false; | ||
else throw new Error(`JSON: Cannot parse "${data}" as boolean`); | ||
} | ||
@@ -162,28 +157,28 @@ | ||
@inline | ||
function parseNumber<T>(data: string): T { | ||
let type: T; | ||
// @ts-ignore | ||
if (type instanceof f64) return F64.parseFloat(data); | ||
// @ts-ignore | ||
else if (type instanceof f32) return F32.parseFloat(data); | ||
// @ts-ignore | ||
else if (type instanceof u64) return U64.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof u32) return U32.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof u8) return U8.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof u16) return U16.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof i64) return I64.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof i32) return I32.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof i16) return I16.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof i8) return I8.parseInt(data); | ||
else | ||
throw new Error( | ||
`JSON: Cannot parse invalid data into a number. Either "${data}" is not a valid number, or <${nameof<T>()}> is an invald number type.` | ||
); | ||
function parseNumber<T>(data: string): T { | ||
let type: T; | ||
// @ts-ignore | ||
if (type instanceof f64) return F64.parseFloat(data); | ||
// @ts-ignore | ||
else if (type instanceof f32) return F32.parseFloat(data); | ||
// @ts-ignore | ||
else if (type instanceof u64) return U64.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof u32) return U32.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof u8) return U8.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof u16) return U16.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof i64) return I64.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof i32) return I32.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof i16) return I16.parseInt(data); | ||
// @ts-ignore | ||
else if (type instanceof i8) return I8.parseInt(data); | ||
else | ||
throw new Error( | ||
`JSON: Cannot parse invalid data into a number. Either "${data}" is not a valid number, or <${nameof<T>()}> is an invald number type.` | ||
); | ||
} | ||
@@ -193,118 +188,112 @@ | ||
@inline | ||
export function parseObject<T>(data: string): T { | ||
let schema: T = changetype<T>(__new(offsetof<T>(), idof<T>())); | ||
const result = new Map<string, string>(); | ||
let key = ""; | ||
let isKey = false; | ||
let depth = 1; | ||
let char = 0; | ||
let outerLoopIndex = 1 | ||
for (; outerLoopIndex < data.length - 1; outerLoopIndex++) { | ||
char = unsafeCharCodeAt(data, outerLoopIndex); | ||
export function parseObject<T>(data: string): T { | ||
let schema: T = changetype<T>(__new(offsetof<T>(), idof<T>())); | ||
const result = new Map<string, string>(); | ||
let key = ""; | ||
let isKey = false; | ||
let depth = 1; | ||
let char = 0; | ||
let outerLoopIndex = 1; | ||
for (; 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) { | ||
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( | ||
key, | ||
data.slice(outerLoopIndex, arrayValueIndex) | ||
); | ||
outerLoopIndex = arrayValueIndex; | ||
isKey = false; | ||
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( | ||
key, | ||
data.slice(outerLoopIndex, objectValueIndex) | ||
); | ||
outerLoopIndex = objectValueIndex; | ||
isKey = false; | ||
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 (isKey === false) { | ||
key = data.slice(outerLoopIndex, stringValueIndex); | ||
isKey = true; | ||
} else { | ||
result.set( | ||
key, | ||
data.slice(outerLoopIndex, stringValueIndex) | ||
); | ||
isKey = false; | ||
} | ||
outerLoopIndex = ++stringValueIndex; | ||
break; | ||
} | ||
} | ||
} else if (char == nCode) { | ||
result.set( | ||
key, | ||
"null" | ||
) | ||
depth = depth << 1; | ||
} else if (char === rightBracketCode) { | ||
depth = depth >> 1; | ||
if (depth === 1) { | ||
++arrayValueIndex; | ||
result.set(key, data.slice(outerLoopIndex, arrayValueIndex)); | ||
outerLoopIndex = arrayValueIndex; | ||
isKey = false; | ||
} else if ( | ||
char === tCode && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "r".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "u".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === eCode | ||
) { | ||
result.set(key, "true"); | ||
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(key, data.slice(outerLoopIndex, objectValueIndex)); | ||
outerLoopIndex = objectValueIndex; | ||
isKey = false; | ||
} 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 | ||
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 | ||
) { | ||
result.set(key, "false"); | ||
if (isKey === false) { | ||
key = data.slice(outerLoopIndex, stringValueIndex); | ||
isKey = true; | ||
} else { | ||
result.set(key, data.slice(outerLoopIndex, stringValueIndex)); | ||
isKey = false; | ||
} else if ((char >= 48 && char <= 57) || char === 45) { | ||
let numberValueIndex = ++outerLoopIndex; | ||
for (; numberValueIndex < data.length; numberValueIndex++) { | ||
char = unsafeCharCodeAt(data, numberValueIndex); | ||
if ( | ||
char === commaCode || | ||
char === rightBraceCode || | ||
isSpace(char) | ||
) { | ||
result.set( | ||
key, | ||
data.slice(outerLoopIndex - 1, numberValueIndex) | ||
); | ||
outerLoopIndex = numberValueIndex; | ||
isKey = false; | ||
break; | ||
} | ||
} | ||
} | ||
outerLoopIndex = ++stringValueIndex; | ||
break; | ||
} | ||
} | ||
} else if (char == nCode) { | ||
result.set(key, "null"); | ||
isKey = false; | ||
} else if ( | ||
char === tCode && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "r".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === "u".charCodeAt(0) && | ||
unsafeCharCodeAt(data, ++outerLoopIndex) === eCode | ||
) { | ||
result.set(key, "true"); | ||
isKey = false; | ||
} 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(key, "false"); | ||
isKey = false; | ||
} else if ((char >= 48 && char <= 57) || char === 45) { | ||
let numberValueIndex = ++outerLoopIndex; | ||
for (; numberValueIndex < data.length; numberValueIndex++) { | ||
char = unsafeCharCodeAt(data, numberValueIndex); | ||
if (char === commaCode || char === rightBraceCode || isSpace(char)) { | ||
result.set(key, data.slice(outerLoopIndex - 1, numberValueIndex)); | ||
outerLoopIndex = numberValueIndex; | ||
isKey = false; | ||
break; | ||
} | ||
} | ||
} | ||
// @ts-ignore | ||
const deserialized = changetype<nonnull<T>>(schema).__JSON_Deserialize<T>(result) | ||
heap.free(changetype<usize>(schema)); | ||
return deserialized; | ||
} | ||
// @ts-ignore | ||
//const deserialized = | ||
//changetype<nonnull<T>>(schema).__JSON_Deserialize<T>(result); | ||
//heap.free(changetype<usize>(schema)); | ||
return changetype<nonnull<T>>(schema).__JSON_Deserialize<T>(result); | ||
} | ||
@@ -314,22 +303,21 @@ | ||
@inline | ||
// @ts-ignore | ||
export function parseArray<T extends unknown[]>(data: string): T { | ||
let type!: valueof<T>; | ||
if (type instanceof String) { | ||
return <T>parseStringArray(data); | ||
} else if (isBoolean<valueof<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 (isBoolean<valueof<T>>()) { | ||
// @ts-ignore | ||
return parseBooleanArray<T>(data); | ||
} else if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) { | ||
// @ts-ignore | ||
return parseNumberArray<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); | ||
} | ||
return parseBooleanArray<T>(data); | ||
} else if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) { | ||
// @ts-ignore | ||
return parseNumberArray<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); | ||
} | ||
} | ||
@@ -339,18 +327,18 @@ | ||
@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('\\"', '"')); | ||
} | ||
} | ||
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; | ||
} | ||
return result; | ||
} | ||
@@ -360,9 +348,9 @@ | ||
@inline | ||
export function parseBooleanArray<T extends boolean[]>(data: string): T { | ||
const result = instantiate<T>(); | ||
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" | ||
export function parseBooleanArray<T extends boolean[]>(data: string): T { | ||
const result = instantiate<T>(); | ||
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) { | ||
@@ -377,10 +365,10 @@ //i += 3; | ||
}*/ | ||
if (char === tCode || char === fCode) { | ||
lastPos = i; | ||
} else if (char === eCode) { | ||
i++; | ||
result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i))); | ||
} | ||
if (char === tCode || char === fCode) { | ||
lastPos = i; | ||
} else if (char === eCode) { | ||
i++; | ||
result.push(parseBoolean<valueof<T>>(data.slice(lastPos, i))); | ||
} | ||
return result; | ||
} | ||
return result; | ||
} | ||
@@ -390,24 +378,24 @@ | ||
@inline | ||
export function parseNumberArray<T extends number[]>(data: string): T { | ||
const result = instantiate<T>(); | ||
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) || char === 45) { | ||
lastPos = i; | ||
} else if ((isSpace(char) || char == commaCode) && lastPos > 0) { | ||
result.push(parseNumber<valueof<T>>(data.slice(lastPos, i))); | ||
lastPos = 0; | ||
} | ||
export function parseNumberArray<T extends number[]>(data: string): T { | ||
const result = instantiate<T>(); | ||
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) || char === 45) { | ||
lastPos = i; | ||
} else if ((isSpace(char) || char == commaCode) && lastPos > 0) { | ||
result.push(parseNumber<valueof<T>>(data.slice(lastPos, i))); | ||
lastPos = 0; | ||
} | ||
for (; i > lastPos - 1; i--) { | ||
char = unsafeCharCodeAt(data, i); | ||
if (char !== rightBracketCode) { | ||
result.push(parseNumber<valueof<T>>(data.slice(lastPos, i + 1))); | ||
break; | ||
} | ||
} | ||
for (; i > lastPos - 1; i--) { | ||
char = unsafeCharCodeAt(data, i); | ||
if (char !== rightBracketCode) { | ||
result.push(parseNumber<valueof<T>>(data.slice(lastPos, i + 1))); | ||
break; | ||
} | ||
return result; | ||
} | ||
return result; | ||
} | ||
@@ -417,28 +405,28 @@ | ||
@inline | ||
export function parseArrayArray<T extends unknown[][]>(data: string): T { | ||
const result = instantiate<T>(); | ||
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))); | ||
} | ||
} | ||
export function parseArrayArray<T extends unknown[][]>(data: string): T { | ||
const result = instantiate<T>(); | ||
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))); | ||
} | ||
} | ||
return result; | ||
} | ||
return result; | ||
} | ||
@@ -448,28 +436,28 @@ | ||
@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))); | ||
} | ||
} | ||
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))); | ||
} | ||
} | ||
return result; | ||
} | ||
return result; | ||
} |
@@ -8,5 +8,6 @@ import "wasi"; | ||
@json | ||
class Vec2 { | ||
class Vec3 { | ||
x: f32; | ||
y: f32; | ||
z: f32; | ||
} | ||
@@ -21,3 +22,3 @@ | ||
age: i32; | ||
pos: Vec2 | null; | ||
pos: Vec3 | null; | ||
isVerified: boolean; | ||
@@ -34,2 +35,3 @@ } | ||
y: 1.2, | ||
z: 8.3 | ||
}, | ||
@@ -39,10 +41,5 @@ isVerified: true, | ||
const vec: Vec2 = { | ||
x: 3.4, | ||
y: 1.2 | ||
} | ||
const serializedPlayer = JSON.stringify<Player>(player); | ||
console.log("Serialized Player: " + serializedPlayer); | ||
const deserializedPlayer = JSON.parse<Player>(serializedPlayer); | ||
console.log("Deserialized Player: " + JSON.stringify(deserializedPlayer)) | ||
console.log("Deserialized Player: " + JSON.stringify(deserializedPlayer)); |
{ | ||
"name": "json-as", | ||
"version": "0.5.2", | ||
"version": "0.5.3", | ||
"description": "JSON encoder/decoder for AssemblyScript", | ||
@@ -5,0 +5,0 @@ "types": "assembly/index.ts", |
@@ -68,2 +68,3 @@ # AS-JSON | ||
## Performance | ||
@@ -70,0 +71,0 @@ |
{ | ||
"name": "@json-as/transform", | ||
"version": "0.5.2", | ||
"version": "0.5.3", | ||
"description": "JSON encoder/decoder for AssemblyScript", | ||
@@ -5,0 +5,0 @@ "main": "./lib/index.js", |
@@ -41,3 +41,5 @@ import { | ||
// @ts-ignore | ||
this.checkDecodeStmts.push(' if (!values.has("${name}")) throw new Error("Key "${name}" was not found. Cannot instantiate object.");\n') | ||
this.checkDecodeStmts.push( | ||
' if (!values.has("${name}")) throw new Error("Key "${name}" was not found. Cannot instantiate object.");\n' | ||
); | ||
} | ||
@@ -80,7 +82,11 @@ visitClassDeclaration(node: ClassDeclaration): void { | ||
__JSON_Deserialize<T>(values: Map<string, string>): T { | ||
${process.argv.includes("--debugJSON") ? this.checkDecodeStmts.join("else") : ""} | ||
${ | ||
process.argv.includes("--debugJSON") | ||
? this.checkDecodeStmts.join("else") | ||
: "" | ||
} | ||
return { | ||
${ | ||
// @ts-ignore | ||
this.decodeStmts.join("") | ||
// @ts-ignore | ||
this.decodeStmts.join("") | ||
} | ||
@@ -87,0 +93,0 @@ } |
1063
3.3%85
1.19%39682
-4.73%23
-4.17%