diff --git a/CHANGELOG b/CHANGELOG index fdc898f..71eeda6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,7 +28,7 @@ v0.9.17 - A schema's parent's fields should be included properly v0.9.18 - Should be able to use @alias and @omit*** or JSON.Raw v0.9.19 - Fix arguments in @omitif declarations not working properly v0.9.20 - Strings were being received with quotes attached via the toString functionality. Removed that. -v0.9.21 - Fix #89 +v0.9.22 - Fix #89 [UNRELEASED] v1.0.0 - Allow nullable primitives diff --git a/README.md b/README.md index 36c0eab..99bf3ec 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ __| || __|| || | | ___ | _ || __| | | ||__ || | || | | ||___|| ||__ | |_____||_____||_____||_|___| |__|__||_____| -v0.9.21 +v0.9.22 diff --git a/asconfig.json b/asconfig.json index 60cc864..83b8baf 100644 --- a/asconfig.json +++ b/asconfig.json @@ -31,5 +31,6 @@ "options": { "transform": ["./transform", "as-test/transform"], "disableWarning": [226] - } + }, + "extends": "./node_modules/@assemblyscript/wasi-shim/asconfig.json" } diff --git a/assembly/__benches__/string.bench.ts b/assembly/__benches__/string.bench.ts index 1b074d7..a1e9073 100644 --- a/assembly/__benches__/string.bench.ts +++ b/assembly/__benches__/string.bench.ts @@ -1,18 +1,21 @@ import { bs } from "../custom/bs"; import { serializeBool, serializeBool_BS } from "../serialize/bool" -import { serialize_simd, serializeString, serializeString_BS } from "../serialize/string"; +import { serialize_simd_v1, serializeString, serializeString_BS } from "../serialize/string"; const out = memory.data(65536); -bench("Serialize String", () => { - blackbox(serializeString("hello \"world abc")); -}); +bench("UTF-16 to UTF-8", () => { + blackbox(String.UTF8.encode(blackbox("hello world"))) +}) +// bench("Serialize String Native", () => { +// blackbox(serializeString("hello \"world abc")); +// }); -bench("Serialize String BS", () => { - serializeString_BS("hello \"world abc"); - bs.reset(); -}); +// bench("Serialize String Sink", () => { +// serializeString_BS("hello \"world abc"); +// bs.reset(); +// }); -bench("Serialize String SIMD", () => { - serialize_simd("hello \"world abc", out); -}) \ No newline at end of file +// bench("Serialize String SIMD", () => { +// serialize_simd_v1("hello \"world abc", out); +// }) \ No newline at end of file diff --git a/assembly/index.ts b/assembly/index.ts index e159be5..977e9a7 100644 --- a/assembly/index.ts +++ b/assembly/index.ts @@ -13,11 +13,10 @@ import { deserializeFloat } from "./deserialize/float"; import { deserializeObject } from "./deserialize/object"; import { deserializeMap } from "./deserialize/map"; import { deserializeDate } from "./deserialize/date"; -import { BRACKET_LEFT, NULL_WORD } from "./custom/chars"; +import { NULL_WORD } from "./custom/chars"; import { deserializeInteger } from "./deserialize/integer"; import { deserializeString } from "./deserialize/string"; import { Sink } from "./custom/sink"; -import { bs } from "./custom/bs"; import { getArrayDepth } from "./custom/util"; /** @@ -157,179 +156,6 @@ export namespace JSON { @inline static from(value: T): Box { return new Box(value); } - @inline - @operator("==") - eq(other: this): bool { - if (isNullable() && changetype(this) == 0) { - if (changetype(other) == 0) return true; - } - return this.value == other.value; - } - - @inline - @operator("!=") - notEq(other: this): bool { - if (isNullable() && changetype(this) == 0) { - if (changetype(this) == changetype(other)) return true; - } - return this.value != other.value; - } - - @inline - @operator(">") - gt(other: this): bool { - return this._val > other._val; - } - - @inline - @operator(">=") - ge(other: this): bool { - return this._val >= other._val; - } - - @inline - @operator("<") - lt(other: this): bool { - return this._val < other._val; - } - - @inline - @operator("<=") - le(other: this): bool { - return this._val <= other._val; - } - - @inline - @operator(">>") - shr(other: this): this { - // @ts-ignore - return instantiate(this._val >> other._val); - } - - @inline - @operator(">>>") - shr_u(other: this): this { - // @ts-ignore - return instantiate(this._val >>> other._val); - } - - @inline - @operator("<<") - shl(other: this): this { - // @ts-ignore - return instantiate(this._val << other._val); - } - - @inline - @operator("&") - and(other: this): this { - // @ts-ignore - return instantiate(this._val & other._val); - } - - @inline - @operator("|") - or(other: this): this { - // @ts-ignore - return instantiate(this._val | other._val); - } - - @inline - @operator("^") - xor(other: this): this { - // @ts-ignore - return instantiate(this._val ^ other._val); - } - - @inline - @operator("+") - add(other: this): this { - // @ts-ignore - return instantiate(this._val + other._val); - } - - @inline - @operator("-") - sub(other: this): this { - // @ts-ignore - return instantiate(this._val - other._val); - } - - @inline - @operator("*") - mul(other: this): this { - // @ts-ignore - return instantiate(this._val * other._val); - } - - @inline - @operator("/") - div(other: this): this { - // @ts-ignore - return instantiate(this._val / other._val); - } - - @inline - @operator("**") - pow(other: this): this { - // @ts-ignore - return instantiate((this._val ** other._val) as T); - } - - @inline - @operator("%") - rem(other: this): this { - // @ts-ignore - return instantiate(this._val % other._val); - } - - @inline - @operator.prefix("!") - isEmpty(): bool { - return !this._val; - } - - @inline - @operator.prefix("~") - not(): this { - return instantiate(~this._val); - } - - @inline - @operator.prefix("+") - pos(): this { - return instantiate(+this._val); - } - - @inline - @operator.prefix("-") - neg(): this { - return instantiate(-this._val); - } - - @operator.prefix("++") - preInc(): this { - // @ts-ignore - ++this._val; - return this; - } - - @operator.prefix("--") - preDec(): this { - // @ts-ignore - --this._val; - return this; - } - - @operator.postfix("++") - postInc(): this { - return this.clone().preInc(); - } - - @operator.postfix("--") - postDec(): this { - return this.clone().preDec(); - } } /** @@ -403,7 +229,7 @@ export namespace JSON { return deserializeArray>(data); } let type: nonnull = changetype>(0); - // @ts-ignore: Defined by trasnform + // @ts-ignore: Defined by transform if (isDefined(type.__DESERIALIZE)) { // @ts-ignore return deserializeObject>(data.trimStart()); diff --git a/assembly/serialize/string.ts b/assembly/serialize/string.ts index af090dc..a70e94c 100644 --- a/assembly/serialize/string.ts +++ b/assembly/serialize/string.ts @@ -41,8 +41,13 @@ function needsEscaping(data: string): bool { return v128.any_true(running); } +/** + * A prototype SIMD implementation for string serialization which can only work in 128-byte (or 16 chars with wtf-16). + * + * A faster version could perhaps look like the following: + */ // @ts-ignore: Decorator -@inline export function serialize_simd(src: string, dst: usize): void { +@inline export function serialize_simd_v1(src: string, dst: usize): void { let src_ptr = changetype(src); let dst_ptr = changetype(dst) + 2; @@ -58,13 +63,12 @@ function needsEscaping(data: string): bool { const concat_indices = v128.or(quote_indices, backslash_indices); const escape_indices = i16x8.lt_u(currentBlock, i16x8.splat(32)); - + if (v128.any_true(v128.or(escape_indices, concat_indices))) { const mask = i16x8.bitmask(concat_indices); const anomalies = popcnt(mask); const start_index = (clz(mask) & ~1) + 2 // This essentially floors to the nearest even integer - //console.log(start_index.toString()) if (anomalies === 1) { memory.copy(dst_ptr, src_ptr, start_index >> 1); store(dst_ptr + start_index, 34); @@ -72,20 +76,71 @@ function needsEscaping(data: string): bool { } if (v128.any_true(escape_indices)) { - + } - //vis(src_ptr, mask); dst_ptr += 16; src_ptr += 16; } else { v128.store(dst_ptr, currentBlock); - //vis(src_ptr, 0); src_ptr += 16; dst_ptr += 16; } } } +const back_slash_reg = i16x8.splat(92); // "\" +const quote_reg = i16x8.splat(34); // "\"" + +// @ts-ignore: Decorator +@inline export function serialize_simd_v2(src: string, dst: usize): void { + let src_ptr = changetype(src); + let dst_ptr = changetype(dst); + + let i = 0; + const len = src.length; + + while (i < len) { + const block = v128.load16x4_u(src_ptr); + console.log("block: " + prt(block)); + const backslash_mask = i16x8.eq(block, back_slash_reg); + const quote_mask = i16x8.eq(block, quote_reg); + const is_quote_or_backslash = v128.or(quote_mask, backslash_mask); + console.log("mask: " + prt10(is_quote_or_backslash)) + + + // store(dst_ptr, expanded); + src_ptr += 8; + dst_ptr += 16; + i += 8; + } +} + +function prt(obj: v128): string { + let out = ""; + out += i16x8.extract_lane_u(obj, 0).toString() + " "; + out += i16x8.extract_lane_u(obj, 1).toString() + " "; + out += i16x8.extract_lane_u(obj, 2).toString() + " "; + out += i16x8.extract_lane_u(obj, 3).toString() + " "; + out += i16x8.extract_lane_u(obj, 4).toString() + " "; + out += i16x8.extract_lane_u(obj, 5).toString() + " "; + out += i16x8.extract_lane_u(obj, 6).toString() + " "; + out += i16x8.extract_lane_u(obj, 7).toString(); + return out; +} + +function prt10(obj: v128): string { + let out = ""; + out += (i16x8.extract_lane_u(obj, 0) ? "1" : "0") + " "; + out += (i16x8.extract_lane_u(obj, 1) ? "1" : "0") + " "; + out += (i16x8.extract_lane_u(obj, 2) ? "1" : "0") + " "; + out += (i16x8.extract_lane_u(obj, 3) ? "1" : "0") + " "; + out += (i16x8.extract_lane_u(obj, 4) ? "1" : "0") + " "; + out += (i16x8.extract_lane_u(obj, 5) ? "1" : "0") + " "; + out += (i16x8.extract_lane_u(obj, 6) ? "1" : "0") + " "; + out += i16x8.extract_lane_u(obj, 7) ? "1" : "0"; + return out; +} + function vis(src_ptr: usize, mask: i32): void { let chars = ""; let bits = ""; @@ -98,6 +153,7 @@ function vis(src_ptr: usize, mask: i32): void { console.log(chars); console.log(bits); } + // @ts-ignore: Decorator @inline export function serializeString(data: string): string { if (!needsEscaping(data)) { diff --git a/assembly/test.ts b/assembly/test.ts index 73f2840..e32cd8e 100644 --- a/assembly/test.ts +++ b/assembly/test.ts @@ -5,4 +5,4 @@ const out = new ArrayBuffer(1024); serialize_simd("hello \"world abc", out); -console.log(String.UTF16.decode(out)) \ No newline at end of file +console.log(String.UTF16.decode(out)); \ No newline at end of file diff --git a/assembly/util/strings.ts b/assembly/util/strings.ts new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json index bde9b81..fb93767 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-as", - "version": "0.9.21", + "version": "0.9.22", "description": "The only JSON library you'll need for AssemblyScript. SIMD enabled", "types": "assembly/index.ts", "author": "Jairus Tanaka", @@ -11,14 +11,15 @@ "Frankk Taylor", "lekiano", "Florian Guitton", - "Matt Johnson-Pint" + "Matt Johnson-Pint", + "Tomáš Hromada" ], "license": "MIT", "scripts": { "test": "ast test && rm -rf ./build/", "pretest": "rm -rf ./build/ && ast build", "bench": "astral --enable simd --runtime stub", - "build:test": "rm -rf ./build/ && JSON_DEBUG=true asc assembly/test.ts --transform ./transform -o ./build/test.wasm --enable simd", + "build:test": "rm -rf ./build/ && JSON_DEBUG=true asc assembly/test.ts --transform ./transform -o ./build/test.wasm --enable simd --enable relaxed-simd", "build:transform": "tsc -p ./transform", "test:wasmtime": "wasmtime ./build/test.wasm", "test:wavm": "wavm run ./build/test.wasm", diff --git a/transform/package.json b/transform/package.json index b567c69..eb9d6d1 100644 --- a/transform/package.json +++ b/transform/package.json @@ -1,6 +1,6 @@ { "name": "@json-as/transform", - "version": "0.9.21", + "version": "0.9.22", "description": "The only JSON library you'll need for AssemblyScript. SIMD enabled", "main": "./lib/index.js", "author": "Jairus Tanaka",