Skip to content

Commit

Permalink
make simd serialization compatible with the rest
Browse files Browse the repository at this point in the history
  • Loading branch information
JairusSW committed Jan 15, 2025
1 parent 32fcf18 commit dd144e3
Show file tree
Hide file tree
Showing 19 changed files with 249 additions and 177 deletions.
2 changes: 1 addition & 1 deletion assembly/__benches__/misc.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const srcStart = changetype<usize>(str);
const srcEnd = srcStart + bytes(str);
bs.ensureSize(2048);
bench("Deserialize String (Simple)", () => {
deserializeString(str2);
deserializeStrin(str2);
});
// bench("Deserialize String (SIMD)", () => {
// (str2, bs.buffer);
Expand Down
28 changes: 0 additions & 28 deletions assembly/custom/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,6 @@ import { isSpace } from "util/string";
import { BACK_SLASH, QUOTE } from "./chars";
import { Sink } from "./sink";

// @ts-ignore: Decorator
export function isMap<T>(): bool {
let type = changetype<T>(0);
return type instanceof Map;
}

// @ts-ignore: Decorator
@inline export function unsafeCharCodeAt(data: string, pos: i32): i32 {
return load<u16>(changetype<usize>(data) + ((<usize>pos) << 1));
}

/**
* A terrible function which finds the depth of a certain array.
* Suffers no overhead besides function calling and a if/else.
* @returns depth of array
*/

// @ts-ignore: Decorator
export function getArrayDepth<T extends ArrayLike>(depth: i32 = 1): i32 {
if (!isArray<T>()) {
return 0;
} else if (isArray<valueof<T>>()) {
depth++;
return getArrayDepth<valueof<T>>(depth);
} else {
return depth;
}
}

/** Scientific Notation Integer Parsing - SNIP
* This is absolutely the fastest algorithm I could think of while adding full support for Scientific Notation
Expand Down
2 changes: 1 addition & 1 deletion assembly/deserialize/simple/arbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { deserializeObject } from "./object";
import { deserializeString } from "./string";

// @ts-ignore
@inline export function deserializeArbitrary(data: string): JSON.Value {
@inline export function deserializeArbitrary(srcStart: usize, srcEnd: usize, dst: usize = 0): JSON.Value {
const firstChar = unsafeCharCodeAt(data, 0);

if (firstChar == 34) return JSON.Value.from(deserializeString(data));
Expand Down
24 changes: 3 additions & 21 deletions assembly/deserialize/simple/date.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,12 @@
import { QUOTE } from "../../custom/chars";
import { ptrToStr } from "../../util/ptrToStr";

// @ts-ignore: Decorator valid here
@inline export function deserializeDate(dateTimeString: string): Date {
@inline export function deserializeDate(srcStart: usize, srcEnd: usize): Date {
// Use AssemblyScript's date parser
const d = Date.fromString(dateTimeString);
const d = Date.fromString(ptrToStr(srcStart, srcEnd));

// Return a new object instead of the one that the parser returned.
// This may seem redundant, but addreses the issue when Date
// is globally aliased to wasi_Date (or some other superclass).
return new Date(d.getTime());
}

// @ts-ignore: Decorator valid here
@inline export function deserializeDate_NEW(srcStart: usize, srcEnd: usize): Date {
// Use AssemblyScript's date parser
const d = Date.fromString(str(srcStart, srcEnd));

// Return a new object instead of the one that the parser returned.
// This may seem redundant, but addreses the issue when Date
// is globally aliased to wasi_Date (or some other superclass).
return new Date(d.getTime());
}

function str(start: usize, end: usize): string {
const size = end - start;
const out = __new(size, idof<string>());
memory.copy(out, start, size);
return changetype<string>(out);
}
13 changes: 3 additions & 10 deletions assembly/deserialize/simple/integer.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { snip_fast } from "../../custom/util";
import { strToInt } from "../../util/strToInt";
import { atoi } from "../../util/atoi";

// @ts-ignore: Decorator valid here
@inline export function deserializeInteger<T>(data: string): T {
// @ts-ignore
return snip_fast<T>(data);
}

// @ts-ignore: Decorator valid here
@inline export function deserializeInteger_NEW<T>(srcStart: usize, srcEnd: usize): T {
return strToInt<T>(srcStart, srcEnd);
@inline export function deserializeInteger<T>(srcStart: usize, srcEnd: usize): T {
return atoi<T>(srcStart, srcEnd);
}
54 changes: 27 additions & 27 deletions assembly/deserialize/simple/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import { JSON } from "../..";
import { BACK_SLASH, COMMA, CHAR_F, BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE, BRACE_RIGHT, BRACKET_RIGHT, CHAR_T, COLON } from "../../custom/chars";
import { isSpace } from "../../util";

export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, dst: T | null = null): T {
export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, dst: usize): T {
const out = changetype<T>(dst);
// @ts-ignore: type
if (!isString<indexof<T>>() && !isInteger<indexof<T>>()) ERROR("Map key must be of type string | number!");
dst = dst || changetype<nonnull<T>>(__new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
console.log("Data: " + str(srcStart, srcEnd));
if (!isString<indexof<T>>() && !isInteger<indexof<T>>() && !isFloat<indexof<T>>()) ERROR("Map key must also be a valid JSON key!");

const srcPtr = srcStart;
let key: string | null = null;
Expand All @@ -23,20 +22,20 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
if (code == QUOTE && load<u16>(srcStart - 2) !== BACK_SLASH) {
if (isKey) {
key = sliceTo(lastIndex, srcStart);
console.log("Key: " + key);
while (isSpace((code = load<u16>((srcStart += 2))))) {
/* empty */
}
if (code !== COLON) throw new Error("Expected ':' after key at position " + (srcStart - srcPtr).toString());
isKey = false;
} else {
isKey = true; // i don't like this
isKey = true;
lastIndex = srcStart + 2;
}
}
// isKey = !isKey;
srcStart += 2;
} else {
// @ts-ignore: type
if (isString<valueof<T>>() && code == QUOTE) {
lastIndex = srcStart;
srcStart += 2;
Expand All @@ -46,21 +45,22 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
while (isSpace(load<u16>((srcStart += 2)))) {
/* empty */
}
console.log("Value (string): " + str(lastIndex, srcStart));
dst.set(key, JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
// @ts-ignore: type
out.set(key, JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
key = null;
break;
}
srcStart += 2;
}
// @ts-ignore: type
} else if (!isBoolean<valueof<T>>() && isInteger<valueof<T>>() && code - 48 <= 9 || code == 45) {
lastIndex = srcStart;
srcStart += 2;
while (srcStart < srcEnd) {
const code = load<u16>(srcStart);
if (code == COMMA || isSpace(code) || code == BRACE_RIGHT) {
dst.set(key, JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
console.log("Value (number): " + str(lastIndex, srcStart));
// @ts-ignore: type
out.set(key, JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
while (isSpace(load<u16>((srcStart += 2)))) {
/* empty */
}
Expand All @@ -69,6 +69,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
}
srcStart += 2;
}
// @ts-ignore: type
} else if (isArray<valueof<T>>() && code == BRACKET_LEFT) {
lastIndex = srcStart;
depth++;
Expand All @@ -77,7 +78,8 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
const code = load<u16>(srcStart);
if (((code ^ BRACE_RIGHT) | (code ^ BRACKET_RIGHT)) == 32) {
if (--depth == 0) {
dst.set(key, JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
// @ts-ignore: type
out.set(key, JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
while (isSpace(load<u16>((srcStart += 2)))) {
/* empty */
}
Expand All @@ -87,6 +89,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
} else if (code == BRACKET_LEFT) depth++;
srcStart += 2;
}
// @ts-ignore: type
} else if (isDefined(changetype<nonnull<valueof<T>>>(0).__DESERIALIZE) && code == BRACE_LEFT) {
lastIndex = srcStart;
depth++;
Expand All @@ -95,7 +98,8 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
const code = load<u16>(srcStart);
if (code == BRACE_RIGHT) {
if (--depth == 0) {
dst.set(key, JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
// @ts-ignore: type
out.set(key, JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
while (isSpace(load<u16>((srcStart += 2)))) {
/* empty */
}
Expand All @@ -105,45 +109,41 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
} else if (code == BRACE_LEFT) depth++;
srcStart += 2;
}
// @ts-ignore: type
} else if (isBoolean<valueof<T>>() && code == CHAR_T) {
if (load<u64>(srcStart) == 28429475166421108) {
dst.set(key, JSON.__deserialize<valueof<T>>(srcStart, srcStart += 8));
console.log("Value (bool): " + str(srcStart - 8, srcStart));
// @ts-ignore: type
out.set(key, JSON.__deserialize<valueof<T>>(srcStart, srcStart += 8));
while (isSpace(load<u16>((srcStart += 2)))) {
/* empty */
}
key = null;
}
// @ts-ignore: type
} else if (isBoolean<valueof<T>>() && code == CHAR_F) {
if (load<u64>(srcStart, 2) == 28429466576093281) {
dst.set(key, JSON.__deserialize<valueof<T>>(srcStart, srcStart += 10));
console.log("Value (bool): " + str(srcStart - 10, srcStart));
// @ts-ignore: type
out.set(key, JSON.__deserialize<valueof<T>>(srcStart, srcStart += 10));
while (isSpace(load<u16>((srcStart += 2)))) {
/* empty */
}
key = null;
}
} else if ((isNullable<T>() || nameof<T>() == "usize") && code == CHAR_N) {
if (load<u64>(srcStart) == 30399761348886638) {
dst.set(key, JSON.__deserialize<valueof<T>>(srcStart, srcStart += 8));
console.log("Value (null): " + str(srcStart - 8, srcStart));
// @ts-ignore: type
out.set(key, JSON.__deserialize<valueof<T>>(srcStart, srcStart += 8));
while (isSpace(load<u16>((srcStart += 2)))) {
/* empty */
}
}
} else {
throw new Error("Unexpected character " + String.fromCharCode(code) +" or type " + nameof<valueof<T>>() + " does not match found type!")
// @ts-ignore: type
throw new Error("Unexpected character " + String.fromCharCode(code) + " or type " + nameof<valueof<T>>() + " does not match found type!")
}
}
}
return dst;
}

function str(start: usize, end: usize): string {
const size = end - start;
const out = __new(size, idof<string>());
memory.copy(out, start, size);
return changetype<string>(out);
return out;
}

// @ts-ignore: Decorator valid here
Expand Down
4 changes: 1 addition & 3 deletions assembly/deserialize/simple/object.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { BACK_SLASH, COMMA, CHAR_F, BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE, BRACE_RIGHT, BRACKET_RIGHT, CHAR_T, COLON } from "../../custom/chars";
import { isSpace } from "../../util";

export function deserializeObject<T>(srcStart: usize, srcEnd: usize, dst: nonnull<T> = changetype<nonnull<T>>(__new(offsetof<nonnull<T>>(), idof<nonnull<T>>()))): T {
console.log("Data: " + str(srcStart, srcEnd));

export function deserializeObject<T>(srcStart: usize, srcEnd: usize, dst: usize): T {
const srcPtr = srcStart;

let keyStart: usize = 0;
Expand Down
2 changes: 1 addition & 1 deletion assembly/deserialize/simple/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BACK_SLASH } from "../../custom/chars";
import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables";

// @ts-ignore: Decorator valid here
@inline export function deserializeString(srcStart: usize, srcEnd: usize, dst: usize = __new(srcEnd - srcStart - 4, idof<string>())): string {
@inline export function deserializeString(srcStart: usize, srcEnd: usize, dst: usize): string {
let dstPtr = dst;
let lastPtr = srcStart;
while (srcStart < srcEnd) {
Expand Down
34 changes: 34 additions & 0 deletions assembly/globals/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,37 @@ export const ESCAPE_HEX_TABLE =
14,
15, // 48-54
]) - 48;

export const POW_TEN_TABLE_32 = memory.data<u32>([
1,
10,
1_000,
10_000,
100_000,
1_000_000,
10_000_000,
100_000_000,
1_000_000_000
]);

export const POW_TEN_TABLE_64 = memory.data<u64>([
1,
10,
1_000,
10_000,
100_000,
1_000_000,
10_000_000,
100_000_000,
1_000_000_000,
10_000_000_000,
100_000_000_000,
1_000_000_000_000,
10_000_000_000_000,
100_000_000_000_000,
1_000_000_000_000_000,
10_000_000_000_000_000,
100_000_000_000_000_000,
1_000_000_000_000_000_000,
10_000_000_000_000_000_000
]);
24 changes: 17 additions & 7 deletions assembly/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/// <reference path="./index.d.ts" />
import { bs } from "as-bs";

import { serializeString } from "./serialize/simple/string";
import { serializeArray } from "./serialize/simple/array";
import { serializeMap } from "./serialize/simple/map";
Expand All @@ -9,19 +11,19 @@ import { deserializeObject } from "./deserialize/simple/object";
import { deserializeMap } from "./deserialize/simple/map";
import { deserializeDate } from "./deserialize/simple/date";
import { deserializeInteger, deserializeInteger_NEW } from "./deserialize/simple/integer";
import { deserializeString, deserializeString } from "./deserialize/simple/string";
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 { bs } from "as-bs";
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";

class Nullable {}
class Nullable { }

export type Raw = string;

Expand Down Expand Up @@ -320,7 +322,7 @@ export namespace JSON {
* Box for primitive types
*/
export class Box<T> {
constructor(public value: T) {}
constructor(public value: T) { }
/**
* Creates a reference to a primitive type
* This means that it can create a nullable primitive
Expand Down Expand Up @@ -368,7 +370,7 @@ export namespace JSON {
} else if (src instanceof JSON.Value) {
serializeArbitrary(src);
} else {
ERROR(`Could not serialize src of type ${nameof<T>()}. Make sure to add the correct decorators to classes.`);
ERROR(`Could not serialize provided data. Make sure to add the correct decorators to classes.`);
}
}
export function __deserialize<T>(srcStart: usize, srcEnd: usize, dst: usize = 0): T {
Expand All @@ -384,9 +386,17 @@ export namespace JSON {
return deserializeString(srcStart, srcEnd, dst);
} else {
let type: nonnull<T> = changetype<nonnull<T>>(0);
if (type instanceof Map) {
// @ts-ignore: declared by transform
if (isDefined(type.__DESERIALIZE)) {
return deserializeObject<T>(srcStart, srcEnd);
} else if (type instanceof Map) {
// @ts-ignore: type
return deserializeMap<T>(srcStart, srcEnd, dst);
} else if (type instanceof Date) {
// @ts-ignore: type
return deserializeMap<T>(srcStart, srcEnd, dst)
return deserializeDate(srcStart, srcEnd);
} else {
ERROR(`Could not deserialize data '${ptrToStr(srcStart, srcEnd).slice(0, 100)}' to type. Make sure to add the correct decorators to classes.`);
}
}
}
Expand Down
Loading

0 comments on commit dd144e3

Please sign in to comment.