refactor(struct): replace bluebird with native Promise

This commit is contained in:
Simon Chan 2022-05-14 18:10:47 +08:00
parent 1ee03486a6
commit 7d5445aeae
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD
8 changed files with 1357 additions and 1336 deletions

File diff suppressed because it is too large Load diff

View file

@ -66,7 +66,9 @@ export async function deserializeLogMessage(stream: StructAsyncDeserializeStream
await stream.read(entry.headerSize - LoggerEntry.size); await stream.read(entry.headerSize - LoggerEntry.size);
const priority = (await stream.read(1))[0] as LogPriority; const priority = (await stream.read(1))[0] as LogPriority;
const payload = await stream.read(entry.payloadSize - 1); const payload = await stream.read(entry.payloadSize - 1);
return { ...entry, priority, payload }; (entry as any).priority = priority;
(entry as any).payload = payload;
return entry as LogMessage;
} }
export interface LogSize { export interface LogSize {

View file

@ -34,7 +34,6 @@
}, },
"dependencies": { "dependencies": {
"@yume-chan/dataview-bigint-polyfill": "^0.0.15", "@yume-chan/dataview-bigint-polyfill": "^0.0.15",
"bluebird": "^3.7.2",
"tslib": "^2.3.1" "tslib": "^2.3.1"
}, },
"devDependencies": { "devDependencies": {

View file

@ -2,7 +2,7 @@
import type { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions } from './basic/index.js'; import type { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions } from './basic/index.js';
import { StructDefaultOptions, StructValue } from './basic/index.js'; import { StructDefaultOptions, StructValue } from './basic/index.js';
import { Syncbird } from "./syncbird.js"; import { SyncPromise } from "./sync-promise.js";
import { BigIntFieldDefinition, BigIntFieldType, BufferFieldSubType, FixedLengthBufferLikeFieldDefinition, NumberFieldDefinition, NumberFieldType, StringBufferFieldSubType, Uint8ArrayBufferFieldSubType, VariableLengthBufferLikeFieldDefinition, type FixedLengthBufferLikeFieldOptions, type LengthField, type VariableLengthBufferLikeFieldOptions } from './types/index.js'; import { BigIntFieldDefinition, BigIntFieldType, BufferFieldSubType, FixedLengthBufferLikeFieldDefinition, NumberFieldDefinition, NumberFieldType, StringBufferFieldSubType, Uint8ArrayBufferFieldSubType, VariableLengthBufferLikeFieldDefinition, type FixedLengthBufferLikeFieldOptions, type LengthField, type VariableLengthBufferLikeFieldOptions } from './types/index.js';
import type { Evaluate, Identity, Overwrite, ValueOrPromise } from "./utils.js"; import type { Evaluate, Identity, Overwrite, ValueOrPromise } from "./utils.js";
@ -545,13 +545,15 @@ export class Struct<
const value = new StructValue(); const value = new StructValue();
Object.defineProperties(value.value, this._extra); Object.defineProperties(value.value, this._extra);
return Syncbird return SyncPromise
.each(this._fields, ([name, definition]) => { .each(this._fields, ([name, definition]) => {
return Syncbird.resolve( return SyncPromise
definition.deserialize(this.options, stream as any, value) .try(() => {
).then(fieldValue => { return definition.deserialize(this.options, stream as any, value);
value.set(name, fieldValue); })
}); .then(fieldValue => {
value.set(name, fieldValue);
});
}) })
.then(() => { .then(() => {
const object = value.value; const object = value.value;

View file

@ -1,28 +1,26 @@
// cspell: ignore syncbird export class SyncPromise<T> extends Promise<T> {
export class Syncbird<T> extends Promise<T> {
private resolved: boolean; private resolved: boolean;
private rejected: boolean; private rejected: boolean;
private result: unknown; private result: unknown;
public static override resolve(): Syncbird<void>; public static override resolve(): SyncPromise<void>;
public static override resolve<T>(value: T | PromiseLike<T>): Syncbird<T>; public static override resolve<T>(value: T | PromiseLike<T>): SyncPromise<T>;
public static override resolve<T>(value?: T | PromiseLike<T>): Syncbird<T> { public static override resolve<T>(value?: T | PromiseLike<T>): SyncPromise<T> {
return new Syncbird((resolve, reject) => { return new SyncPromise((resolve, reject) => {
resolve(value!); resolve(value!);
}); });
} }
public static each<T>(array: T[], callback: (item: T, index: number) => Syncbird<void>): Syncbird<void> { public static each<T>(array: T[], callback: (item: T, index: number) => SyncPromise<void>): SyncPromise<void> {
return array.reduce((prev, item, index) => { return array.reduce((prev, item, index) => {
return prev.then(() => { return prev.then(() => {
return callback(item, index); return callback(item, index);
}); });
}, Syncbird.resolve()); }, SyncPromise.resolve());
} }
public static try<T>(executor: () => T | PromiseLike<T>): Syncbird<T> { public static try<T>(executor: () => T | PromiseLike<T>): SyncPromise<T> {
return new Syncbird((resolve, reject) => { return new SyncPromise((resolve, reject) => {
try { try {
resolve(executor()); resolve(executor());
} catch (e) { } catch (e) {
@ -55,7 +53,20 @@ export class Syncbird<T> extends Promise<T> {
if (typeof value === 'object' && if (typeof value === 'object' &&
value !== null && value !== null &&
'then' in value && 'then' in value &&
typeof value.then === 'function') { typeof value.then === 'function'
) {
if (value instanceof SyncPromise) {
if (value.resolved) {
resolved = true;
result = value.result;
return;
} else if (value.rejected) {
rejected = true;
result = value.result;
return;
}
}
resolve(value); resolve(value);
return; return;
} }
@ -103,10 +114,10 @@ export class Syncbird<T> extends Promise<T> {
public override then<TResult1 = T, TResult2 = never>( public override then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined, onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
): Syncbird<TResult1 | TResult2> { ): SyncPromise<TResult1 | TResult2> {
if (this.resolved) { if (this.resolved) {
if (onfulfilled) { if (onfulfilled) {
return new Syncbird((resolve, reject) => { return new SyncPromise((resolve, reject) => {
try { try {
resolve(onfulfilled(this.result as T)); resolve(onfulfilled(this.result as T));
} catch (e) { } catch (e) {
@ -114,23 +125,23 @@ export class Syncbird<T> extends Promise<T> {
} }
}); });
} }
return this as unknown as Syncbird<TResult1 | TResult2>; return this as unknown as SyncPromise<TResult1 | TResult2>;
} }
if (this.rejected) { if (this.rejected) {
if (onrejected) { if (onrejected) {
return new Syncbird((resolve, reject) => { return new SyncPromise((resolve, reject) => {
try { try {
resolve(onrejected(this.result as any)); resolve(onrejected(this.result));
} catch (e) { } catch (e) {
reject(e); reject(e);
} }
}); });
} }
return this as unknown as Syncbird<TResult1 | TResult2>; return this as unknown as SyncPromise<TResult1 | TResult2>;
} }
return Syncbird.resolve(super.then(onfulfilled, onrejected)); return super.then(onfulfilled, onrejected) as unknown as SyncPromise<TResult1 | TResult2>;
} }
public override catch<TResult = never>( public override catch<TResult = never>(
@ -145,7 +156,7 @@ export class Syncbird<T> extends Promise<T> {
} }
if (this.rejected) { if (this.rejected) {
return this.result as any; throw this.result;
} }
return this as Promise<T>; return this as Promise<T>;

View file

@ -1,102 +1,105 @@
// cspell: ignore syncbird // cspell: ignore syncbird
import { getBigInt64, getBigUint64, setBigInt64, setBigUint64 } from '@yume-chan/dataview-bigint-polyfill/esm/fallback.js'; import { getBigInt64, getBigUint64, setBigInt64, setBigUint64 } from '@yume-chan/dataview-bigint-polyfill/esm/fallback.js';
import { StructFieldDefinition, StructFieldValue, StructValue, type StructAsyncDeserializeStream, type StructDeserializeStream, type StructOptions } from "../basic/index.js"; import { StructFieldDefinition, StructFieldValue, StructValue, type StructAsyncDeserializeStream, type StructDeserializeStream, type StructOptions } from "../basic/index.js";
import { Syncbird } from "../syncbird.js"; import { SyncPromise } from "../sync-promise.js";
import type { ValueOrPromise } from "../utils.js"; import type { ValueOrPromise } from "../utils.js";
type DataViewBigInt64Getter = (dataView: DataView, byteOffset: number, littleEndian: boolean | undefined) => bigint; type DataViewBigInt64Getter = (dataView: DataView, byteOffset: number, littleEndian: boolean | undefined) => bigint;
type DataViewBigInt64Setter = (dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) => void; type DataViewBigInt64Setter = (dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) => void;
export class BigIntFieldType { export class BigIntFieldType {
public readonly TTypeScriptType!: bigint; public readonly TTypeScriptType!: bigint;
public readonly size: number; public readonly size: number;
public readonly getter: DataViewBigInt64Getter; public readonly getter: DataViewBigInt64Getter;
public readonly setter: DataViewBigInt64Setter; public readonly setter: DataViewBigInt64Setter;
public constructor( public constructor(
size: number, size: number,
getter: DataViewBigInt64Getter, getter: DataViewBigInt64Getter,
setter: DataViewBigInt64Setter, setter: DataViewBigInt64Setter,
) { ) {
this.size = size; this.size = size;
this.getter = getter; this.getter = getter;
this.setter = setter; this.setter = setter;
} }
public static readonly Int64 = new BigIntFieldType(8, getBigInt64, setBigInt64); public static readonly Int64 = new BigIntFieldType(8, getBigInt64, setBigInt64);
public static readonly Uint64 = new BigIntFieldType(8, getBigUint64, setBigUint64); public static readonly Uint64 = new BigIntFieldType(8, getBigUint64, setBigUint64);
} }
export class BigIntFieldDefinition< export class BigIntFieldDefinition<
TType extends BigIntFieldType = BigIntFieldType, TType extends BigIntFieldType = BigIntFieldType,
TTypeScriptType = TType["TTypeScriptType"], TTypeScriptType = TType["TTypeScriptType"],
> extends StructFieldDefinition< > extends StructFieldDefinition<
void, void,
TTypeScriptType TTypeScriptType
> { > {
public readonly type: TType; public readonly type: TType;
public constructor(type: TType, _typescriptType?: TTypeScriptType) { public constructor(type: TType, _typescriptType?: TTypeScriptType) {
super(); super();
this.type = type; this.type = type;
} }
public getSize(): number { public getSize(): number {
return this.type.size; return this.type.size;
} }
public create( public create(
options: Readonly<StructOptions>, options: Readonly<StructOptions>,
struct: StructValue, struct: StructValue,
value: TTypeScriptType, value: TTypeScriptType,
): BigIntFieldValue<this> { ): BigIntFieldValue<this> {
return new BigIntFieldValue(this, options, struct, value); return new BigIntFieldValue(this, options, struct, value);
} }
public override deserialize( public override deserialize(
options: Readonly<StructOptions>, options: Readonly<StructOptions>,
stream: StructDeserializeStream, stream: StructDeserializeStream,
struct: StructValue, struct: StructValue,
): BigIntFieldValue<this>; ): BigIntFieldValue<this>;
public override deserialize( public override deserialize(
options: Readonly<StructOptions>, options: Readonly<StructOptions>,
stream: StructAsyncDeserializeStream, stream: StructAsyncDeserializeStream,
struct: StructValue, struct: StructValue,
): Promise<BigIntFieldValue<this>>; ): Promise<BigIntFieldValue<this>>;
public override deserialize( public override deserialize(
options: Readonly<StructOptions>, options: Readonly<StructOptions>,
stream: StructDeserializeStream | StructAsyncDeserializeStream, stream: StructDeserializeStream | StructAsyncDeserializeStream,
struct: StructValue, struct: StructValue,
): ValueOrPromise<BigIntFieldValue<this>> { ): ValueOrPromise<BigIntFieldValue<this>> {
return Syncbird.try(() => { return SyncPromise
return stream.read(this.getSize()); .try(() => {
}).then(array => { return stream.read(this.getSize());
const view = new DataView(array.buffer, array.byteOffset, array.byteLength); })
const value = this.type.getter( .then(array => {
view, const view = new DataView(array.buffer, array.byteOffset, array.byteLength);
0, const value = this.type.getter(
options.littleEndian view,
); 0,
return this.create(options, struct, value as any); options.littleEndian
}).valueOrPromise(); );
} return this.create(options, struct, value as any);
} })
.valueOrPromise();
export class BigIntFieldValue< }
TDefinition extends BigIntFieldDefinition<BigIntFieldType, any>, }
> extends StructFieldValue<TDefinition> {
public serialize(dataView: DataView, offset: number): void { export class BigIntFieldValue<
this.definition.type.setter( TDefinition extends BigIntFieldDefinition<BigIntFieldType, any>,
dataView, > extends StructFieldValue<TDefinition> {
offset, public serialize(dataView: DataView, offset: number): void {
this.value!, this.definition.type.setter(
this.options.littleEndian dataView,
); offset,
} this.value!,
} this.options.littleEndian
);
}
}

View file

@ -1,7 +1,7 @@
// cspell: ignore syncbird // cspell: ignore syncbird
import { StructFieldDefinition, StructFieldValue, StructValue, type StructAsyncDeserializeStream, type StructDeserializeStream, type StructOptions } from '../../basic/index.js'; import { StructFieldDefinition, StructFieldValue, StructValue, type StructAsyncDeserializeStream, type StructDeserializeStream, type StructOptions } from '../../basic/index.js';
import { Syncbird } from "../../syncbird.js"; import { SyncPromise } from "../../sync-promise.js";
import { decodeUtf8, encodeUtf8, type ValueOrPromise } from "../../utils.js"; import { decodeUtf8, encodeUtf8, type ValueOrPromise } from "../../utils.js";
/** /**
@ -130,17 +130,20 @@ export abstract class BufferLikeFieldDefinition<
stream: StructDeserializeStream | StructAsyncDeserializeStream, stream: StructDeserializeStream | StructAsyncDeserializeStream,
struct: StructValue, struct: StructValue,
): ValueOrPromise<BufferLikeFieldValue<this>> { ): ValueOrPromise<BufferLikeFieldValue<this>> {
return Syncbird.try(() => { return SyncPromise
const size = this.getDeserializeSize(struct); .try(() => {
if (size === 0) { const size = this.getDeserializeSize(struct);
return EMPTY_UINT8_ARRAY; if (size === 0) {
} else { return EMPTY_UINT8_ARRAY;
return stream.read(size); } else {
} return stream.read(size);
}).then(array => { }
const value = this.type.toValue(array); })
return this.create(options, struct, value, array); .then(array => {
}).valueOrPromise(); const value = this.type.toValue(array);
return this.create(options, struct, value, array);
})
.valueOrPromise();
} }
} }

View file

@ -1,115 +1,115 @@
// cspell: ignore syncbird // cspell: ignore syncbird
import { StructFieldDefinition, StructFieldValue, StructValue, type StructAsyncDeserializeStream, type StructDeserializeStream, type StructOptions } from '../basic/index.js'; import { StructFieldDefinition, StructFieldValue, StructValue, type StructAsyncDeserializeStream, type StructDeserializeStream, type StructOptions } from '../basic/index.js';
import { Syncbird } from "../syncbird.js"; import { SyncPromise } from "../sync-promise.js";
import type { ValueOrPromise } from "../utils.js"; import type { ValueOrPromise } from "../utils.js";
export type DataViewGetters = export type DataViewGetters =
{ [TKey in keyof DataView]: TKey extends `get${string}` ? TKey : never }[keyof DataView]; { [TKey in keyof DataView]: TKey extends `get${string}` ? TKey : never }[keyof DataView];
export type DataViewSetters = export type DataViewSetters =
{ [TKey in keyof DataView]: TKey extends `set${string}` ? TKey : never }[keyof DataView]; { [TKey in keyof DataView]: TKey extends `set${string}` ? TKey : never }[keyof DataView];
export class NumberFieldType { export class NumberFieldType {
public readonly TTypeScriptType!: number; public readonly TTypeScriptType!: number;
public readonly size: number; public readonly size: number;
public readonly dataViewGetter: DataViewGetters; public readonly dataViewGetter: DataViewGetters;
public readonly dataViewSetter: DataViewSetters; public readonly dataViewSetter: DataViewSetters;
public constructor( public constructor(
size: number, size: number,
dataViewGetter: DataViewGetters, dataViewGetter: DataViewGetters,
dataViewSetter: DataViewSetters dataViewSetter: DataViewSetters
) { ) {
this.size = size; this.size = size;
this.dataViewGetter = dataViewGetter; this.dataViewGetter = dataViewGetter;
this.dataViewSetter = dataViewSetter; this.dataViewSetter = dataViewSetter;
} }
public static readonly Int8 = new NumberFieldType(1, 'getInt8', 'setInt8'); public static readonly Int8 = new NumberFieldType(1, 'getInt8', 'setInt8');
public static readonly Uint8 = new NumberFieldType(1, 'getUint8', 'setUint8'); public static readonly Uint8 = new NumberFieldType(1, 'getUint8', 'setUint8');
public static readonly Int16 = new NumberFieldType(2, 'getInt16', 'setInt16'); public static readonly Int16 = new NumberFieldType(2, 'getInt16', 'setInt16');
public static readonly Uint16 = new NumberFieldType(2, 'getUint16', 'setUint16'); public static readonly Uint16 = new NumberFieldType(2, 'getUint16', 'setUint16');
public static readonly Int32 = new NumberFieldType(4, 'getInt32', 'setInt32'); public static readonly Int32 = new NumberFieldType(4, 'getInt32', 'setInt32');
public static readonly Uint32 = new NumberFieldType(4, 'getUint32', 'setUint32'); public static readonly Uint32 = new NumberFieldType(4, 'getUint32', 'setUint32');
} }
export class NumberFieldDefinition< export class NumberFieldDefinition<
TType extends NumberFieldType = NumberFieldType, TType extends NumberFieldType = NumberFieldType,
TTypeScriptType = TType["TTypeScriptType"], TTypeScriptType = TType["TTypeScriptType"],
> extends StructFieldDefinition< > extends StructFieldDefinition<
void, void,
TTypeScriptType TTypeScriptType
> { > {
public readonly type: TType; public readonly type: TType;
public constructor(type: TType, _typescriptType?: TTypeScriptType) { public constructor(type: TType, _typescriptType?: TTypeScriptType) {
super(); super();
this.type = type; this.type = type;
} }
public getSize(): number { public getSize(): number {
return this.type.size; return this.type.size;
} }
public create( public create(
options: Readonly<StructOptions>, options: Readonly<StructOptions>,
struct: StructValue, struct: StructValue,
value: TTypeScriptType, value: TTypeScriptType,
): NumberFieldValue<this> { ): NumberFieldValue<this> {
return new NumberFieldValue(this, options, struct, value); return new NumberFieldValue(this, options, struct, value);
} }
public override deserialize( public override deserialize(
options: Readonly<StructOptions>, options: Readonly<StructOptions>,
stream: StructDeserializeStream, stream: StructDeserializeStream,
struct: StructValue, struct: StructValue,
): NumberFieldValue<this>; ): NumberFieldValue<this>;
public override deserialize( public override deserialize(
options: Readonly<StructOptions>, options: Readonly<StructOptions>,
stream: StructAsyncDeserializeStream, stream: StructAsyncDeserializeStream,
struct: StructValue, struct: StructValue,
): Promise<NumberFieldValue<this>>; ): Promise<NumberFieldValue<this>>;
public override deserialize( public override deserialize(
options: Readonly<StructOptions>, options: Readonly<StructOptions>,
stream: StructDeserializeStream | StructAsyncDeserializeStream, stream: StructDeserializeStream | StructAsyncDeserializeStream,
struct: StructValue, struct: StructValue,
): ValueOrPromise<NumberFieldValue<this>> { ): ValueOrPromise<NumberFieldValue<this>> {
return Syncbird return SyncPromise
.try(() => { .try(() => {
return stream.read(this.getSize()); return stream.read(this.getSize());
}) })
.then(array => { .then(array => {
const view = new DataView(array.buffer, array.byteOffset, array.byteLength); const view = new DataView(array.buffer, array.byteOffset, array.byteLength);
const value = view[this.type.dataViewGetter]( const value = view[this.type.dataViewGetter](
0, 0,
options.littleEndian options.littleEndian
); );
return this.create(options, struct, value as any); return this.create(options, struct, value as any);
}) })
.valueOrPromise(); .valueOrPromise();
} }
} }
export class NumberFieldValue< export class NumberFieldValue<
TDefinition extends NumberFieldDefinition<NumberFieldType, any>, TDefinition extends NumberFieldDefinition<NumberFieldType, any>,
> extends StructFieldValue<TDefinition> { > extends StructFieldValue<TDefinition> {
public serialize(dataView: DataView, offset: number): void { public serialize(dataView: DataView, offset: number): void {
// `setBigInt64` requires a `bigint` while others require `number` // `setBigInt64` requires a `bigint` while others require `number`
// So `dataView[DataViewSetters]` requires `bigint & number` // So `dataView[DataViewSetters]` requires `bigint & number`
// and that is, `never` // and that is, `never`
(dataView[this.definition.type.dataViewSetter] as any)( (dataView[this.definition.type.dataViewSetter] as any)(
offset, offset,
this.value!, this.value!,
this.options.littleEndian this.options.littleEndian
); );
} }
} }