Skip to content

Commit

Permalink
switch to central buffer
Browse files Browse the repository at this point in the history
  • Loading branch information
JairusSW committed Jan 1, 2025
1 parent b338951 commit 48ef2d1
Show file tree
Hide file tree
Showing 41 changed files with 5,036 additions and 5,140 deletions.
4 changes: 2 additions & 2 deletions assembly/__benches__/misc.bench.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { deserializeString_SIMD } from "../deserialize/simd/string";
import { serializeString_SIMD } from "../serialize/simd/string";
import { bench } from "as-bench/assembly/index"
import { bench } from "as-bench/assembly/index";
const str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()\\\"\t\r\f\n\u0000';
const str2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()\\\\\\"\\t\\r\\f\\n\\u0000"';

Expand All @@ -22,4 +22,4 @@ bench("Serialize String (SIMD)", () => {
bench("Deserialize String (SIMD)", () => {
// ~4.03GB/s
deserializeString_SIMD(str2, changetype<usize>(out));
});
});
23 changes: 9 additions & 14 deletions assembly/__tests__/simd/string.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,24 @@ const deserialize_simd = (data: string): string => String.UTF16.decodeUnsafe(out
describe("Should serialize strings", () => {
expect(serialize_simd("abcdefg")).toBe('"abcdefg"');

expect(serialize_simd('st"ring" w""ith quotes"'))
.toBe('"st\\"ring\\" w\\"\\"ith quotes\\""');
expect(serialize_simd('st"ring" w""ith quotes"')).toBe('"st\\"ring\\" w\\"\\"ith quotes\\""');

expect(serialize_simd('string "with random spa\nces and \nnewlines\n\n\n'))
.toBe('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"');
expect(serialize_simd('string "with random spa\nces and \nnewlines\n\n\n')).toBe('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"');

expect(serialize_simd('string with colon : comma , brace [ ] bracket { } and quote " and other quote "'))
.toBe('"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\""');
expect(serialize_simd('string with colon : comma , brace [ ] bracket { } and quote " and other quote "')).toBe('"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\""');

expect(serialize_simd("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f\u000f\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"))
.toBe("\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u000f\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\"")
expect(serialize_simd("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f\u000f\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f")).toBe('"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u000f\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f"');
});

describe("Should deserialize strings", () => {
expect(deserialize_simd('"abcdefg"')).toBe("abcdefg");
expect(deserialize_simd('"st\\"ring\\" w\\"\\"ith quotes\\""'))
.toBe('st"ring" w""ith quotes"');
expect(deserialize_simd('"st\\"ring\\" w\\"\\"ith quotes\\""')).toBe('st"ring" w""ith quotes"');

expect(deserialize_simd('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"'))
.toBe('string "with random spa\nces and \nnewlines\n\n\n');
expect(deserialize_simd('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"')).toBe('string "with random spa\nces and \nnewlines\n\n\n');

expect(deserialize_simd('"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\""'))
.toBe('string with colon : comma , brace [ ] bracket { } and quote " and other quote "');
expect(deserialize_simd('"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\""')).toBe('string with colon : comma , brace [ ] bracket { } and quote " and other quote "');

expect(deserialize_simd('"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u000f\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f"')).toBe("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f\u000f\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f");
});

run();
3 changes: 3 additions & 0 deletions assembly/__tests__/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export class ObjWithStrangeKey<T> {
data!: T;
}


@json
export class ObjectWithStringArray {
sa!: string[];
Expand Down Expand Up @@ -86,10 +87,12 @@ export type Null = Nullable | null;
export class OmitIf {
x: i32 = 1;


@omitif("this.y == -1")
y: i32 = -1;
z: i32 = 1;


@omitnull()
foo: string | null = null;
}
221 changes: 41 additions & 180 deletions assembly/custom/bs.ts
Original file line number Diff line number Diff line change
@@ -1,202 +1,63 @@
import { dtoa_buffered, itoa_buffered } from "util/number";
import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
// @ts-ignore
@inline const MAX_LEN: usize = 1024;
const STORE: usize[] = [];
let STORE_LEN: usize = 0;
const CACHE = memory.data(i32(MAX_LEN));
// Configurable amount of referenceable strings
let POINTER = changetype<usize>(CACHE);
// @ts-ignore
@inline const MAX_CACHE = CACHE + MAX_LEN;
import { bytes } from "../util/bytes";
import { nextPowerOf2 } from "../util/nextPowerOf2";

export namespace bs {
// @ts-ignore
@inline export function write_int<T extends number>(num: T): void {
POINTER += itoa_buffered(POINTER, num) << 1;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_int_u<T extends number>(num: T): void {
POINTER += itoa_buffered(POINTER, num) << 1;
}

// @ts-ignore
@inline export function write_fl<T extends number>(num: T): void {
POINTER += dtoa_buffered(POINTER, num) << 1;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_fl_u<T extends number>(num: T): void {
POINTER += dtoa_buffered(POINTER, num) << 1;
}

// @ts-ignore
@inline export function write_b(buf: usize, bytes: usize = changetype<OBJECT>(buf - TOTAL_OVERHEAD).rtSize): void {
memory.copy(POINTER, buf, bytes);
POINTER += bytes;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_b_u(buf: usize, bytes: usize = changetype<OBJECT>(buf - TOTAL_OVERHEAD).rtSize): void {
memory.copy(POINTER, buf, bytes);
POINTER += bytes;
}
let maxOffset: usize = 0;

// @ts-ignore
@inline export function write_s(str: string, bytes: usize = changetype<OBJECT>(changetype<usize>(str) - TOTAL_OVERHEAD).rtSize): void {
memory.copy(POINTER, changetype<usize>(str), bytes);
POINTER += bytes;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_s_u(str: string, bytes: usize = changetype<OBJECT>(changetype<usize>(str) - TOTAL_OVERHEAD).rtSize): void {
memory.copy(POINTER, changetype<usize>(str), bytes);
POINTER += bytes;
}

// @ts-ignore
@inline export function write_s_se(str: string, start: usize, end: usize): void {
const bytes = end - start;
memory.copy(POINTER, changetype<usize>(str) + start, bytes);
POINTER += bytes;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_s_se_u(str: string, start: usize, end: usize): void {
const bytes = end - start;
memory.copy(POINTER, changetype<usize>(str) + start, bytes);
POINTER += bytes;
}

// @ts-ignore
@inline export function write_8(char: i32): void {
store<u8>(POINTER, char);
POINTER += 2;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_8_u(char: i32): void {
store<u8>(POINTER, char);
//POINTER += 2;
}

// @ts-ignore
@inline export function write_16(char: i32): void {
store<u16>(POINTER, char);
POINTER += 2;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_16_u(char: i32): void {
store<u16>(POINTER, char);
//POINTER += 2;
}

// @ts-ignore
@inline export function write_32(chars: i32): void {
store<u32>(POINTER, chars);
POINTER += 4;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_32_u(chars: i32): void {
store<u32>(POINTER, chars);
//POINTER += 4;
}

// @ts-ignore
@inline export function write_64(chars: i64): void {
store<u64>(POINTER, chars);
POINTER += 8;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_64_u(chars: i64): void {
store<u64>(POINTER, chars);
POINTER += 8;
}

// @ts-ignore
@inline export function write_128(chars: v128): void {
store<v128>(POINTER, chars);
POINTER += 16;
if (MAX_CACHE <= POINTER) bs.shrink();
}

// @ts-ignore
@inline export function write_128_n(chars: v128, n: usize): void {
store<v128>(POINTER, chars);
POINTER += n;
if (MAX_CACHE <= POINTER) bs.shrink();
}
/**
* This serves as the central buffer
*/
export namespace bs {
export let buffer: usize = 0;
export let offset: usize = 0;
export let byteLength: usize = 0;

// @ts-ignore
@inline export function write_128_u(chars: v128): void {
store<v128>(POINTER, chars);
//POINTER += 16;
//if (MAX_CACHE <= POINTER) bs.shrink();
@inline export function setBuffer<T>(data: T): void {
buffer = changetype<usize>(data);
offset = changetype<usize>(data);
byteLength = bytes(data);
maxOffset = byteLength + buffer;
}

// @ts-ignore
@inline export function shrink(): void {
const len = POINTER - CACHE;
STORE_LEN += len;
const out = __new(len, idof<ArrayBuffer>());
memory.copy(out, CACHE, len);
bs.reset();
STORE.push(out);
@inline export function ensureCapacity(size: u32): void {
const newSize = offset + size;
if (newSize > maxOffset) {
const newPtr = __renew(buffer, (byteLength = nextPowerOf2(newSize - buffer)));
offset = offset - buffer + newPtr;
maxOffset = newPtr + byteLength;
buffer = newPtr;
}
}

// @ts-ignore
@inline export function out<T>(): T {
const len = POINTER - CACHE;
let out = __new(len + STORE_LEN, idof<T>());

memory.copy(out, CACHE, len);
if (STORE_LEN) {
out += len;
for (let i = 0; i < STORE.length; i++) {
const ptr = changetype<usize>(unchecked(STORE[i]));
const storeLen = changetype<OBJECT>(ptr - TOTAL_OVERHEAD).rtSize;
memory.copy(out, ptr, storeLen);
//__unpin(ptr);
out += storeLen;
}
STORE_LEN = 0;
@inline export function ensureSize(size: u32): void {
const newSize = offset + size;
if (newSize > maxOffset) {
const newPtr = __renew(buffer, (byteLength = newSize - buffer));
offset = offset - buffer + newPtr;
maxOffset = newPtr + byteLength;
buffer = newPtr;
}
bs.reset();

return changetype<T>(out);
}

// @ts-ignore
@inline export function out_u<T>(): T {
const len = POINTER - CACHE;
const out = __new(len + STORE_LEN, idof<T>());

memory.copy(out, CACHE, len);
bs.reset();

return changetype<T>(out);
@inline export function shrink(): void {
byteLength = offset - buffer;
buffer = __renew(buffer, byteLength);
maxOffset = byteLength + buffer;
}

// @ts-ignore
@inline export function _out(out: usize): void {
memory.copy(out, CACHE, POINTER - CACHE);
@inline export function shrinkTo<T>(): T {
shrink();
offset = buffer;
return changetype<T>(buffer);
}

// @ts-ignore
@inline export function reset(): void {
POINTER = CACHE;
@inline export function to<T>(): T {
offset = buffer;
return changetype<T>(buffer);
}
}
32 changes: 16 additions & 16 deletions assembly/custom/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ import { E_INVALIDLENGTH } from "util/error";

// @ts-ignore: Decorator valid here
@inline export function ensureCapacity<T>(obj: T, newSize: usize): usize {
const ptr = changetype<usize>(obj);
const oldCapacity = changetype<OBJECT>(ptr - TOTAL_OVERHEAD).rtSize;
if (newSize > oldCapacity) {
if (newSize > BLOCK_MAXSIZE) throw new RangeError(E_INVALIDLENGTH);
const newCapacity = max(min(oldCapacity << 1, BLOCK_MAXSIZE), newSize);
const newObj = __renew(ptr, newCapacity);
return newObj;
}
return ptr;
const ptr = changetype<usize>(obj);
const oldCapacity = changetype<OBJECT>(ptr - TOTAL_OVERHEAD).rtSize;
if (newSize > oldCapacity) {
if (newSize > BLOCK_MAXSIZE) throw new RangeError(E_INVALIDLENGTH);
const newCapacity = max(min(oldCapacity << 1, BLOCK_MAXSIZE), newSize);
const newObj = __renew(ptr, newCapacity);
return newObj;
}
return ptr;
}

// @ts-ignore: Decorator valid here
@inline export function setCapacity<T>(obj: T, oldCapacity: usize, newCapacity: usize): usize {
const ptr = changetype<usize>(obj);
if (newCapacity > oldCapacity) {
if (newCapacity > BLOCK_MAXSIZE) throw new RangeError(E_INVALIDLENGTH);
return __renew(ptr, newCapacity);
}
return ptr;
}
const ptr = changetype<usize>(obj);
if (newCapacity > oldCapacity) {
if (newCapacity > BLOCK_MAXSIZE) throw new RangeError(E_INVALIDLENGTH);
return __renew(ptr, newCapacity);
}
return ptr;
}
5 changes: 2 additions & 3 deletions assembly/custom/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isSpace } from "util/string";
import { BACK_SLASH, QUOTE } from "./chars";
import { BACK_SLASH, QUOTE } from "./chars";
import { Sink } from "./sink";

// @ts-ignore: Decorator
Expand Down Expand Up @@ -307,7 +307,6 @@ export function getArrayDepth<T extends ArrayLike>(depth: i32 = 1): i32 {
return false;
}


// @ts-ignore: Decorator
@inline export function _intTo16(int: i32): i32 {
if (int < 10) {
Expand Down Expand Up @@ -341,4 +340,4 @@ export function getArrayDepth<T extends ArrayLike>(depth: i32 = 1): i32 {
// @ts-ignore: Decorator valid here
@inline export function nextPowerOf2(n: u32): u32 {
return 1 << (32 - clz(n - 1));
}
}
Loading

0 comments on commit 48ef2d1

Please sign in to comment.