/// import { bs } from "../modules/as-bs"; import { serializeString } from "./serialize/simple/string"; import { serializeArray } from "./serialize/simple/array"; import { serializeMap } from "./serialize/simple/map"; import { deserializeBoolean } from "./deserialize/simple/bool"; import { deserializeArray } from "./deserialize/simple/array"; import { deserializeFloat } from "./deserialize/simple/float"; import { deserializeObject } from "./deserialize/simple/object"; import { deserializeMap } from "./deserialize/simple/map"; import { deserializeDate } from "./deserialize/simple/date"; import { deserializeInteger } from "./deserialize/simple/integer"; import { deserializeString } from "./deserialize/simple/string"; import { serializeArbitrary } from "./serialize/simple/arbitrary"; import { Sink } from "./custom/sink"; import { NULL_WORD, QUOTE } from "./custom/chars"; import { dtoa_buffered, itoa_buffered } from "util/number"; import { serializeBool } from "./serialize/simple/bool"; import { serializeInteger } from "./serialize/simple/integer"; import { serializeFloat } from "./serialize/simple/float"; import { serializeObject } from "./serialize/simple/object"; import { ptrToStr } from "./util/ptrToStr"; import { bytes } from "./util"; export type Raw = string; /** * Offset of the 'storage' property in the JSON.Value class. */ // @ts-ignore: Decorator valid here @inline const STORAGE = offsetof("storage"); /** * JSON Encoder/Decoder for AssemblyScript */ export namespace JSON { /** * Serializes valid JSON data * ```js * JSON.stringify(data) * ``` * @param data T * @returns string */ export function stringify(data: T, out: string | null = null): string { if (isBoolean()) { if (out) { if (data == true) { out = changetype(__renew(changetype(out), 8)); store(changetype(out), 28429475166421108); } else { out = changetype(__renew(changetype(out), 10)); store(changetype(out), 32370086184550502); store(changetype(out), 101, 8); } return out; } return data ? "true" : "false"; } else if (isInteger() && nameof() == "usize" && data == 0) { if (out) { out = changetype(__renew(changetype(out), 8)); store(changetype(out), 30399761348886638); return out; } return NULL_WORD; } else if (isInteger(data)) { if (out) { out = changetype(__renew(changetype(out), sizeof() << 3)); // @ts-ignore const bytes = itoa_buffered(changetype(out), data) << 1; return (out = changetype(__renew(changetype(out), bytes))); } return data.toString(); } else if (isFloat(data)) { if (out) { out = changetype(__renew(changetype(out), 64)); // @ts-ignore const bytes = dtoa_buffered(changetype(out), data) << 1; return (out = changetype(__renew(changetype(out), bytes))); } return data.toString(); // @ts-ignore: Function is generated by transform } else if (isNullable() && changetype(data) == 0) { if (out) { out = changetype(__renew(changetype(out), 8)); store(changetype(out), 30399761348886638); return out; } return NULL_WORD; // @ts-ignore } else if (isString>()) { if (out) { out = changetype(__renew(changetype(out), bytes(data) + 4)); // const oldSize = bs.byteLength; const oldBuf = bs.buffer; const newSize = bytes(data) + 4; const newBuf = __new(newSize, idof()); bs.setBuffer(newBuf); serializeString(changetype(data)); bs.setBuffer(oldBuf); return changetype(newBuf); } serializeString(changetype(data)); return bs.out(); // @ts-ignore: Supplied by transform } else if (isDefined(data.__SERIALIZE) && isDefined(data.__ALLOCATE)) { // @ts-ignore data.__ALLOCATE(); // @ts-ignore data.__SERIALIZE(changetype(data)); return bs.out(); // @ts-ignore: Supplied by transform } else if (data instanceof Date) { out = out ? changetype(__renew(changetype(out), 52)) : changetype(__new(52, idof())); store(changetype(out), QUOTE); memory.copy(changetype(out) + 2, changetype(data.toISOString()), 48); store(changetype(out), 50); return out; } else if (data instanceof Array) { // @ts-ignore serializeArray(changetype>(data)); return bs.out(); } else if (data instanceof Map) { // @ts-ignore serializeMap(changetype>(data)); return bs.out(); } else if (data instanceof JSON.Value) { serializeArbitrary(data); return bs.out(); } else if (data instanceof JSON.Box) { return JSON.stringify(data.value); } else { ERROR(`Could not serialize data of type ${nameof()}. Make sure to add the correct decorators to classes.`); } } /** * Parses valid JSON strings into their original format * ```js * JSON.parse(data) * ``` * @param data string * @returns T */ export function parse(data: string): T { const dataSize = bytes(data); const dataPtr = changetype(data); if (isBoolean()) { return deserializeBoolean(dataPtr, dataPtr + dataSize) as T; } else if (isInteger()) { return deserializeInteger(dataPtr, dataPtr + dataSize); } else if (isFloat()) { return deserializeFloat(dataPtr, dataPtr + dataSize); } else if (isNullable() && data.length == 4 && data == "null") { // @ts-ignore return null; } else if (isString()) { // @ts-ignore return deserializeString(dataPtr, dataPtr + dataSize); } else if (isArray()) { // @ts-ignore return deserializeArray>(dataPtr, dataPtr + dataSize, changetype(instantiate())); } let type: nonnull = changetype>(0); // @ts-ignore: Defined by transform if (isDefined(type.__DESERIALIZE) && isDefined(type.__INITIALIZE)) { const out = __new(offsetof>(), idof>()); // @ts-ignore changetype>(out).__INITIALIZE(); // @ts-ignore return deserializeObject>(dataPtr, dataPtr + dataSize, out); } else if (type instanceof Map) { // @ts-ignore return deserializeMap>(dataPtr, dataPtr + dataSize); } else if (type instanceof Date) { // @ts-ignore return deserializeDate(dataPtr, dataPtr + dataSize); } else if (type instanceof JSON.Box) { // @ts-ignore return new JSON.Box(JSON.parse>(data)); } else { ERROR(`Could not deserialize data ${data} to type ${nameof()}. Make sure to add the correct decorators to classes.`); } } /** * Enum representing the different types supported by JSON. */ export enum Types { Raw = 0, U8 = 1, U16 = 2, U32 = 3, U64 = 4, F32 = 5, F64 = 6, Bool = 7, String = 8, Object = 9, Array = 10, Struct = 11, } export type Raw = string; export class Value { static METHODS: Map = new Map(); public type: i32; // @ts-ignore private storage: u64; private constructor() { unreachable(); } /** * Creates an JSON.Value instance from a given value. * @param value - The value to be encapsulated. * @returns An instance of JSON.Value. */ @inline static from(value: T): JSON.Value { if (value instanceof JSON.Value) { return value; } const out = changetype(__new(offsetof(), idof())); out.set(value); return out; } /** * Sets the value of the JSON.Value instance. * @param value - The value to be set. */ @inline set(value: T): void { if (isBoolean()) { this.type = JSON.Types.Bool; store(changetype(this), value, STORAGE); } else if (value instanceof u8 || value instanceof i8) { this.type = JSON.Types.U8; store(changetype(this), value, STORAGE); } else if (value instanceof u16 || value instanceof i16) { this.type = JSON.Types.U16; store(changetype(this), value, STORAGE); } else if (value instanceof u32 || value instanceof i32) { this.type = JSON.Types.U32; store(changetype(this), value, STORAGE); } else if (value instanceof u64 || value instanceof i64) { this.type = JSON.Types.U64; store(changetype(this), value, STORAGE); } else if (value instanceof f32) { this.type = JSON.Types.F64; store(changetype(this), value, STORAGE); } else if (value instanceof f64) { this.type = JSON.Types.F64; store(changetype(this), value, STORAGE); } else if (isString()) { this.type = JSON.Types.String; store(changetype(this), value, STORAGE); } else if (value instanceof Map) { if (idof() !== idof>()) { abort("Maps must be of type Map!"); } this.type = JSON.Types.Struct; store(changetype(this), value, STORAGE); // @ts-ignore } else if (isDefined(value.__SERIALIZE)) { this.type = idof() + JSON.Types.Struct; // @ts-ignore if (!JSON.Value.METHODS.has(idof())) JSON.Value.METHODS.set(idof(), value.__SERIALIZE.index); // @ts-ignore store(changetype(this), value, STORAGE); // @ts-ignore } else if (isArray() && idof>() == idof()) { // @ts-ignore: T satisfies constraints of any[] this.type = JSON.Types.Array; store(changetype(this), value, STORAGE); } } /** * Gets the value of the JSON.Value instance. * @returns The encapsulated value. */ @inline get(): T { return load(changetype(this), STORAGE); } /** * Converts the JSON.Value to a string representation. * @returns The string representation of the JSON.Value. */ toString(): string { switch (this.type) { case JSON.Types.U8: return this.get().toString(); case JSON.Types.U16: return this.get().toString(); case JSON.Types.U32: return this.get().toString(); case JSON.Types.U64: return this.get().toString(); case JSON.Types.String: return '"' + this.get() + '"'; case JSON.Types.Bool: return this.get() ? "true" : "false"; case JSON.Types.Array: { const arr = this.get(); if (!arr.length) return "[]"; const out = Sink.fromStringLiteral("["); const end = arr.length - 1; for (let i = 0; i < end; i++) { const element = unchecked(arr[i]); out.write(element.toString()); out.write(","); } const element = unchecked(arr[end]); out.write(element.toString()); out.write("]"); return out.toString(); } default: { const fn = JSON.Value.METHODS.get(this.type - JSON.Types.Struct); const value = this.get(); return call_indirect(fn, 0, value); } } } } /** * Box for primitive types */ export class Box { constructor(public value: T) {} /** * Creates a reference to a primitive type * This means that it can create a nullable primitive * ```js * JSON.stringify | null>(null); * // null * ``` * @param from T * @returns Box */ @inline static from(value: T): Box { return new Box(value); } } export function __serialize(src: T): void { if (isBoolean()) { serializeBool(src as bool); } else if (isInteger() && nameof() == "usize" && src == 0) { bs.ensureSize(8); store(bs.offset, 30399761348886638); bs.offset += 8; } else if (isInteger()) { // @ts-ignore serializeInteger(src); } else if (isFloat(src)) { // @ts-ignore serializeFloat(src); // @ts-ignore: Function is generated by transform } else if (isNullable() && changetype(src) == 0) { bs.ensureSize(8); store(bs.offset, 30399761348886638); bs.offset += 8; } else if (isString>()) { serializeString(src as string); // @ts-ignore: Supplied by transform } else if (isDefined(src.__SERIALIZE)) { // @ts-ignore serializeObject(changetype>(src)); } else if (src instanceof Date) { // @ts-ignore serializeDate(changetype>(src)); } else if (src instanceof Array) { // @ts-ignore serializeArray(changetype>(src)); } else if (src instanceof Map) { // @ts-ignore serializeMap(changetype>(src)); } else if (src instanceof JSON.Value) { serializeArbitrary(src); } else if (src instanceof JSON.Box) { __serialize(src.value); } else { ERROR(`Could not serialize provided data. Make sure to add the correct decorators to classes.`); } } export function __deserialize(srcStart: usize, srcEnd: usize, dst: usize = 0): T { if (isBoolean()) { // @ts-ignore: type return deserializeBoolean(srcStart, srcEnd); } else if (isInteger()) { return deserializeInteger(srcStart, srcEnd); } else if (isFloat()) { return deserializeFloat(srcStart, srcEnd); } else if (isString()) { // @ts-ignore: type return deserializeString(srcStart, srcEnd, dst); } else if (isArray()) { // @ts-ignore return deserializeArray(srcStart, srcEnd, dst); } else { let type: nonnull = changetype>(0); // @ts-ignore: declared by transform if (isDefined(type.__DESERIALIZE)) { return deserializeObject(srcStart, srcEnd, dst); } else if (type instanceof Map) { // @ts-ignore: type return deserializeMap(srcStart, srcEnd, dst); } else if (type instanceof Date) { // @ts-ignore: type return deserializeDate(srcStart, srcEnd); } } throw new Error(`Could not deserialize data '${ptrToStr(srcStart, srcEnd).slice(0, 100)}' to type. Make sure to add the correct decorators to classes.`); } }