Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JavaScript] Implement the standard protocol #1286

Merged
merged 4 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion javascript/benchmark/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
const Fury = require("@furyjs/fury");
const utils = require("../test/util");
const hps = require('@furyjs/hps');
const fury = new Fury.default({ hps, refTracking: false, useLatin1: true, useSliceString: true });
const fury = new Fury.default({ hps, refTracking: false, useSliceString: true });
const Benchmark = require("benchmark");
const protobuf = require("protobufjs");
const path = require('path');
Expand Down
Binary file added javascript/benchmark/sample.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions javascript/packages/fury/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,19 @@ export default class {
serialize: (data: ToRecordType<T>) => {
return this.fury.serialize(data, serializer);
},
serializeVolatile: (data: ToRecordType<T>) => {
return this.fury.serializeVolatile(data, serializer);
},
deserialize: (bytes: Uint8Array) => {
return this.fury.deserialize(bytes, serializer) as ToRecordType<T>;
},
};
}

serializeVolatile(v: any, serialize?: Serializer) {
return this.fury.serializeVolatile(v, serialize);
}

serialize(v: any, serialize?: Serializer) {
return this.fury.serialize(v, serialize);
}
Expand Down
50 changes: 42 additions & 8 deletions javascript/packages/fury/lib/classResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,39 @@ import { BinaryWriter } from "./writer";
const USESTRINGVALUE = 0;
const USESTRINGID = 1;

class Lazystring {
private string: string | null = null;
private start: number | null = null;
private len: number | null = null;

static fromPair(start: number, len: number) {
const result = new Lazystring();
result.start = start;
result.len = len;
return result;
}

static fromString(str: string) {
const result = new Lazystring();
result.string = str;
return result;
}

toString(binaryReader: BinaryReader) {
if (this.string == null) {
const str = binaryReader.stringUtf8At(this.start!, this.len!);
return str;
}
return this.string;
}
}

export default class SerializerResolver {
private internalSerializer: Serializer[] = new Array(300);
private customSerializer: { [key: string]: Serializer } = {
};

private readStringPool: string[] = [];
private readStringPool: Lazystring[] = [];
private writeStringCount = 0;
private writeStringIndex: number[] = [];

Expand Down Expand Up @@ -139,22 +166,29 @@ export default class SerializerResolver {
const flag = binaryReader.uint8();
if (flag === USESTRINGVALUE) {
binaryReader.skip(8); // The tag hash is not needed at the moment.
const str = binaryReader.stringUtf8(binaryReader.int16());
return str;
return binaryReader.stringUtf8(binaryReader.int16());
} else {
return this.readStringPool[binaryReader.int16()];
return this.readStringPool[binaryReader.int16()].toString(binaryReader);
}
}

readTag(binaryReader: BinaryReader) {
const flag = binaryReader.uint8();
if (flag === USESTRINGVALUE) {
binaryReader.skip(8); // The tag hash is not needed at the moment.
const str = binaryReader.stringUtf8(binaryReader.int16());
this.readStringPool.push(str);
return str;
const start = binaryReader.getCursor();
const len = binaryReader.int16();
binaryReader.skip(len);
this.readStringPool.push(Lazystring.fromPair(start, len));
const idx = this.readStringPool.length;
return () => {
return this.readStringPool[idx - 1].toString(binaryReader);
};
} else {
return this.readStringPool[binaryReader.int16()];
const idx = binaryReader.int16();
return () => {
return this.readStringPool[idx].toString(binaryReader);
};
}
}
}
28 changes: 28 additions & 0 deletions javascript/packages/fury/lib/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export class OwnershipError extends Error {
constructor(message: string) {
super(message);
this.name = this.constructor.name;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}
24 changes: 21 additions & 3 deletions javascript/packages/fury/lib/fury.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { BinaryWriter } from "./writer";
import { BinaryReader } from "./reader";
import { ReferenceResolver } from "./referenceResolver";
import { ConfigFlags, Serializer, Config, InternalSerializerType, Language } from "./type";
import { OwnershipError } from "./error";

export default (config: Config) => {
const binaryReader = BinaryReader(config);
Expand All @@ -38,6 +39,7 @@ export default (config: Config) => {
classResolver,
binaryReader,
binaryWriter,
serializeVolatile,
};
classResolver.init(fury);

Expand Down Expand Up @@ -71,10 +73,17 @@ export default (config: Config) => {
}
}

function serialize<T = any>(data: T, serializer?: Serializer) {
function serializeInternal<T = any>(data: T, serializer?: Serializer) {
try {
binaryWriter.reset();
} catch (e) {
if (e instanceof OwnershipError) {
throw new Error("Permission denied. To release the serialization ownership, you must call the dispose function returned by serializeVolatile.");
}
throw e;
}
referenceResolver.reset();
classResolver.reset();
binaryWriter.reset();
let bitmap = 0;
if (data === null) {
bitmap |= ConfigFlags.isNullFlag;
Expand All @@ -92,7 +101,16 @@ export default (config: Config) => {
classResolver.getSerializerById(InternalSerializerType.ANY).write(data);
}
binaryWriter.setUint32Position(cursor, binaryWriter.getCursor()); // nativeObjects start offsets;
return binaryWriter.dump();
return binaryWriter;
}

function serialize<T = any>(data: T, serializer?: Serializer) {
return serializeInternal(data, serializer).dump();
}

function serializeVolatile<T = any>(data: T, serializer?: Serializer) {
return serializeInternal(data, serializer).dumpAndOwn();
}

return fury;
};
8 changes: 4 additions & 4 deletions javascript/packages/fury/lib/internalSerializer/number.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { InternalSerializerType, RefFlags, Fury } from "../type";

export const uInt8Serializer = (fury: Fury) => {
const { binaryWriter, binaryReader, referenceResolver } = fury;
const { int8: writeInt8, uint8: writeUInt8 } = binaryWriter;
const { uint8: writeUInt8 } = binaryWriter;
const { uint8: readUInt8 } = binaryReader;
return {
...referenceResolver.deref(() => {
Expand Down Expand Up @@ -107,7 +107,7 @@ export const int8Serializer = (fury: Fury) => {

export const uInt16Serializer = (fury: Fury) => {
const { binaryWriter, binaryReader, referenceResolver } = fury;
const { int8: writeInt8, uint16: writeUInt16 } = binaryWriter;
const { uint16: writeUInt16 } = binaryWriter;
const { uint16: readUInt16 } = binaryReader;

return {
Expand Down Expand Up @@ -151,7 +151,7 @@ export const int16Serializer = (fury: Fury) => {

export const uInt32Serializer = (fury: Fury) => {
const { binaryWriter, binaryReader, referenceResolver } = fury;
const { int8: writeInt8, uint32: writeUInt32 } = binaryWriter;
const { uint32: writeUInt32 } = binaryWriter;
const { uint32: readUInt32 } = binaryReader;

return {
Expand Down Expand Up @@ -195,7 +195,7 @@ export const int32Serializer = (fury: Fury) => {

export const uInt64Serializer = (fury: Fury) => {
const { binaryWriter, binaryReader, referenceResolver } = fury;
const { int8: writeInt8, uint64: writeUInt64 } = binaryWriter;
const { uint64: writeUInt64 } = binaryWriter;
const { uint64: readUInt64 } = binaryReader;

return {
Expand Down
9 changes: 7 additions & 2 deletions javascript/packages/fury/lib/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,20 @@ export const BinaryReader = (config: Config) => {
return result;
}

function stringUtf8At(start: number, len: number) {
return buffer.utf8Slice(start, start + len);
}

function stringUtf8(len: number) {
const result = buffer.utf8Slice(cursor, cursor + len);
cursor += len;
return result;
}

function stringOfVarInt32() {
const useLatin1 = config.useLatin1 ? uint8() === LATIN1 : false;
const isLatin1 = uint8() === LATIN1;
const len = varInt32();
return useLatin1 ? stringLatin1(len) : stringUtf8(len);
return isLatin1 ? stringLatin1(len) : stringUtf8(len);
}

function stringLatin1Fast(len: number) {
Expand Down Expand Up @@ -167,6 +171,7 @@ export const BinaryReader = (config: Config) => {
bufferRef,
uint8,
reset,
stringUtf8At,
stringUtf8,
stringLatin1,
stringOfVarInt32,
Expand Down
1 change: 0 additions & 1 deletion javascript/packages/fury/lib/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ export interface Hps {
export interface Config {
hps?: Hps
refTracking?: boolean
useLatin1?: boolean
useSliceString?: boolean
}

Expand Down
26 changes: 20 additions & 6 deletions javascript/packages/fury/lib/writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import { Config, LATIN1, UTF8 } from "./type";
import { PlatformBuffer, alloc, strByteLength } from "./platformBuffer";
import { OwnershipError } from "./error";

const MAX_POOL_SIZE = 1024 * 1024 * 3; // 3MB

Expand All @@ -28,6 +29,7 @@ export const BinaryWriter = (config: Config) => {
let arrayBuffer: PlatformBuffer;
let dataView: DataView;
let reserved = 0;
let locked = false;

function initPoll() {
byteLength = 1024 * 100;
Expand All @@ -49,6 +51,9 @@ export const BinaryWriter = (config: Config) => {
}

function reset() {
if (locked) {
throw new OwnershipError("Ownership of writer was held by dumpAndOwn, but not released");
}
cursor = 0;
reserved = 0;
}
Expand Down Expand Up @@ -170,9 +175,7 @@ export const BinaryWriter = (config: Config) => {
return function (v: string) {
const isLatin1 = detectIsLatin1(v);
const len = isLatin1 ? v.length : strByteLength(v);
if (config.useLatin1) {
dataView.setUint8(cursor++, isLatin1 ? LATIN1 : UTF8);
}
dataView.setUint8(cursor++, isLatin1 ? LATIN1 : UTF8);
varInt32(len);
reserve(len);
if (isLatin1) {
Expand All @@ -191,9 +194,7 @@ export const BinaryWriter = (config: Config) => {
function stringOfVarInt32Slow(v: string) {
const len = strByteLength(v);
const isLatin1 = len === v.length;
if (config.useLatin1) {
dataView.setUint8(cursor++, isLatin1 ? LATIN1 : UTF8);
}
dataView.setUint8(cursor++, isLatin1 ? LATIN1 : UTF8);
varInt32(len);
reserve(len);
if (isLatin1) {
Expand Down Expand Up @@ -236,6 +237,18 @@ export const BinaryWriter = (config: Config) => {
return result;
}

function dumpAndOwn() {
locked = true;
return {
get() {
return arrayBuffer.subarray(0, cursor);
},
dispose() {
locked = false;
},
};
}

function getCursor() {
return cursor;
}
Expand Down Expand Up @@ -278,5 +291,6 @@ export const BinaryWriter = (config: Config) => {
int32,
getCursor,
setUint32Position,
dumpAndOwn,
};
};
2 changes: 1 addition & 1 deletion javascript/test/array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ describe('array', () => {
}
};

const fury = new Fury({ refTracking: true, useLatin1: true });
const fury = new Fury({ refTracking: true });
const serializer = fury.registerSerializer(description).serializer;
const input = fury.serialize({
a7: ["hello", "world", null]
Expand Down
Loading
Loading