Comparing version
@@ -8,2 +8,16 @@ import { JSON } from ".." | ||
// @ts-ignore | ||
@json | ||
class Vec2 { | ||
x: f32; | ||
y: f32; | ||
} | ||
class Nullable {} | ||
type Null = Nullable | null | ||
describe("Ser/de Nulls", () => { | ||
canSerde<Null>(null) | ||
}) | ||
describe("Ser/de Numbers", () => { | ||
@@ -86,2 +100,12 @@ it("should ser/de integers", () => { | ||
}); | ||
}); | ||
}); | ||
describe("Ser/de Objects", () => { | ||
it("should ser/de Vec2 Objects", () => { | ||
canSerde<Vec2>({ | ||
x: 3.4, | ||
y: 1.2 | ||
}) | ||
}) | ||
}) |
@@ -18,1 +18,2 @@ export const commaCode = ",".charCodeAt(0); | ||
export const sCode = "s".charCodeAt(0); | ||
export const nCode = "n".charCodeAt(0); |
@@ -1,460 +0,1 @@ | ||
import { StringSink } from "as-string-sink/assembly"; | ||
import { Variant } from "as-variant/assembly"; | ||
import { isSpace } from "util/string"; | ||
import { | ||
backSlashCode, | ||
colonCode, | ||
commaCode, | ||
eCode, | ||
fCode, | ||
forwardSlashCode, | ||
leftBraceCode, | ||
leftBracketCode, | ||
quoteCode, | ||
rightBraceCode, | ||
rightBracketCode, | ||
tCode, | ||
} from "./chars"; | ||
import { removeWhitespace, unsafeCharCodeAt } from "./util"; | ||
/** | ||
* JSON Encoder/Decoder for AssemblyScript | ||
*/ | ||
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)) { | ||
// @ts-ignore | ||
if (isNullable<T>()) return null; | ||
return parseObject<T>(data); | ||
} else { | ||
// @ts-ignore | ||
return null; | ||
} | ||
} | ||
/** | ||
* Stringifies valid JSON data. | ||
* ```js | ||
* JSON.stringify<T>(data) | ||
* ``` | ||
* @param data T | ||
* @returns string | ||
*/ | ||
static stringify<T = Nullable | null>(data: T): string { | ||
// String | ||
if (isString<T>()) { | ||
return '"' + (<string>data).replaceAll('"', '\\"') + '"'; | ||
} | ||
// Boolean | ||
else if (isBoolean<T>()) { | ||
return data ? "true" : "false"; | ||
} | ||
// 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 | ||
result.write(JSON.stringify(unchecked(data[i])) + ","); | ||
} | ||
// @ts-ignore | ||
result.write(JSON.stringify(unchecked(data[data.length - 1]))); | ||
result.write("]"); | ||
return result.toString(); | ||
} | ||
// Null | ||
else if (isNullable<T>() && data == null) { | ||
return "null"; | ||
} else { | ||
return "null"; | ||
} | ||
} | ||
/** | ||
* Parses valid JSON strings into their original format. | ||
* ```js | ||
* JSON.parse<T>(data) | ||
* ``` | ||
* @param data string | ||
* @returns T | ||
*/ | ||
static parse<T = Variant>(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 (isDefined(type.__JSON_Deserialize)) { | ||
return parseObject<T>(data.trimStart()); | ||
} else { | ||
// @ts-ignore | ||
return null; | ||
} | ||
} | ||
} | ||
// @ts-ignore | ||
@inline | ||
function parseString(data: string): string { | ||
return data.slice(1, data.length - 1).replaceAll('\\"', '"'); | ||
} | ||
// @ts-ignore | ||
@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`); | ||
} | ||
// @ts-ignore | ||
@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.` | ||
); | ||
} | ||
// @ts-ignore | ||
@inline | ||
export function parseObject<T>(data: string): T { | ||
let schema!: T; | ||
const result = new Map<string, string>(); | ||
let key = ""; | ||
let isKey = false; | ||
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( | ||
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 === 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 - 1; numberValueIndex++) { | ||
char = unsafeCharCodeAt(data, numberValueIndex); | ||
if ( | ||
char === commaCode || | ||
isSpace(char) || | ||
numberValueIndex == data.length - 2 | ||
) { | ||
result.set( | ||
key, | ||
data.slice(outerLoopIndex - 1, numberValueIndex) | ||
); | ||
outerLoopIndex = numberValueIndex; | ||
isKey = false; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
// @ts-ignore | ||
return schema.__JSON_Deserialize(result); | ||
} | ||
// @ts-ignore | ||
@inline | ||
// @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); | ||
} | ||
} | ||
// @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>(); | ||
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))); | ||
} | ||
} | ||
return result; | ||
} | ||
// @ts-ignore | ||
@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; | ||
} | ||
} | ||
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; | ||
} | ||
// @ts-ignore | ||
@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))); | ||
} | ||
} | ||
} | ||
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))); | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
class Nullable { } | ||
export { JSON } from "./json"; |
@@ -5,2 +5,3 @@ import "wasi"; | ||
} from "."; | ||
import { parseObject } from "./json"; | ||
@@ -10,6 +11,3 @@ // @ts-ignore | ||
class Vec2 { | ||
/** | ||
* x > 4 && x < 100 | ||
*/ | ||
private x: f32 = 0; | ||
x: f32; | ||
y: f32; | ||
@@ -25,3 +23,3 @@ } | ||
age: i32; | ||
pos: Vec2; | ||
pos: Vec2 | null; | ||
isVerified: boolean; | ||
@@ -36,2 +34,3 @@ } | ||
pos: { | ||
x: 3.4, | ||
y: 1.2, | ||
@@ -43,13 +42,9 @@ }, | ||
const vec: Vec2 = { | ||
y: 0.0 | ||
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)); | ||
const serializedVec2 = JSON.stringify<Vec2>(vec); | ||
console.log("Serialized Vec2: " + serializedVec2); | ||
const deserializedVec2 = JSON.parse<Vec2>(serializedVec2); | ||
console.log("Deserialized Vec2: " + JSON.stringify(deserializedVec2)); | ||
console.log("Deserialized Player: " + JSON.stringify(deserializedPlayer)) |
{ | ||
"name": "json-as", | ||
"version": "0.5.0", | ||
"version": "0.5.1", | ||
"description": "JSON encoder/decoder for AssemblyScript", | ||
@@ -31,4 +31,3 @@ "types": "assembly/index.ts", | ||
"dependencies": { | ||
"as-string-sink": "^0.5.0", | ||
"as-variant": "^0.4.0" | ||
"as-string-sink": "^0.5.0" | ||
}, | ||
@@ -35,0 +34,0 @@ "repository": { |
@@ -10,2 +10,3 @@ import { ClassDecorator, registerDecorator, } from "visitor-as/dist/decorator.js"; | ||
this.decodeStmts = []; | ||
this.checkDecodeStmts = []; | ||
} | ||
@@ -21,3 +22,3 @@ visitMethodDeclaration() { } | ||
} | ||
const type = getName(node.type); | ||
let type = getName(node.type); | ||
// @ts-ignore | ||
@@ -27,2 +28,4 @@ this.encodeStmts.push(`"${name}":\${JSON.stringify<${type}>(this.${name})},`); | ||
this.decodeStmts.push(`${name}: JSON.parseObjectValue<${type}>(values.get("${name}")),\n`); | ||
// @ts-ignore | ||
this.checkDecodeStmts.push(' if (!values.has("${name}")) throw new Error("Key "${name}" was not found. Cannot instantiate object.");\n'); | ||
} | ||
@@ -34,6 +37,5 @@ visitClassDeclaration(node) { | ||
this.currentClass = node; | ||
const name = getName(node); | ||
this.visit(node.members); | ||
const serializedProp = `__JSON_Serialized: string = "";`; | ||
let serializeFunc = ``; | ||
const serializedProp = '__JSON_Serialized: string = "";'; | ||
let serializeFunc = ""; | ||
if (this.encodeStmts.length > 0) { | ||
@@ -59,3 +61,4 @@ const stmt = this.encodeStmts[this.encodeStmts.length - 1]; | ||
@inline | ||
__JSON_Deserialize(values: Map<string, string>): ${name} { | ||
__JSON_Deserialize<T>(values: Map<string, string>): T { | ||
${process.argv.includes("--debugJSON") ? this.checkDecodeStmts.join("else") : ""} | ||
return { | ||
@@ -70,3 +73,2 @@ ${ | ||
this.decodeStmts = []; | ||
//console.log(serializeFunc, deserializeFunc) | ||
const serializedProperty = SimpleParser.parseClassMember(serializedProp, node); | ||
@@ -73,0 +75,0 @@ node.members.push(serializedProperty); |
{ | ||
"name": "@json-as/transform", | ||
"version": "0.5.0", | ||
"version": "0.5.1", | ||
"description": "JSON encoder/decoder for AssemblyScript", | ||
@@ -5,0 +5,0 @@ "main": "./lib/index.js", |
@@ -18,2 +18,3 @@ import { | ||
public decodeStmts: string[] = []; | ||
public checkDecodeStmts: string[] = []; | ||
@@ -29,4 +30,3 @@ visitMethodDeclaration(): void {} | ||
const type = getName(node.type); | ||
let type = getName(node.type); | ||
// @ts-ignore | ||
@@ -41,2 +41,5 @@ this.encodeStmts.push( | ||
); | ||
// @ts-ignore | ||
this.checkDecodeStmts.push(' if (!values.has("${name}")) throw new Error("Key "${name}" was not found. Cannot instantiate object.");\n') | ||
} | ||
@@ -50,9 +53,7 @@ visitClassDeclaration(node: ClassDeclaration): void { | ||
const name = getName(node); | ||
this.visit(node.members); | ||
const serializedProp = `__JSON_Serialized: string = "";`; | ||
const serializedProp = '__JSON_Serialized: string = "";'; | ||
let serializeFunc = ``; | ||
let serializeFunc = ""; | ||
@@ -79,10 +80,10 @@ if (this.encodeStmts.length > 0) { | ||
} | ||
const deserializeFunc = ` | ||
@inline | ||
__JSON_Deserialize(values: Map<string, string>): ${name} { | ||
__JSON_Deserialize<T>(values: Map<string, string>): T { | ||
${process.argv.includes("--debugJSON") ? this.checkDecodeStmts.join("else") : ""} | ||
return { | ||
${ | ||
// @ts-ignore | ||
this.decodeStmts.join("") | ||
// @ts-ignore | ||
this.decodeStmts.join("") | ||
} | ||
@@ -94,3 +95,2 @@ } | ||
this.decodeStmts = []; | ||
//console.log(serializeFunc, deserializeFunc) | ||
const serializedProperty = SimpleParser.parseClassMember( | ||
@@ -97,0 +97,0 @@ serializedProp, |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1
-50%24
4.35%41622
-20.36%1028
-27.5%1
Infinity%- Removed
- Removed