mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-05 02:39:26 +02:00
feat(struct): include polyfill for bigint types
This commit is contained in:
parent
09e42bb0e7
commit
594648d033
12 changed files with 251 additions and 92 deletions
|
@ -4,7 +4,11 @@ Polyfill for `DataView#getBigInt64`, `DataView#getBigUint64`, `DataView#setBigIn
|
|||
|
||||
Requires native `BigInt` support.
|
||||
|
||||
## Import functions
|
||||
## Flavors
|
||||
|
||||
It ships with 3 flavors:
|
||||
|
||||
### Pure: provide alternative implementations.
|
||||
|
||||
```ts
|
||||
import { getBigInt64, getBigUint64, setBigInt64, setBigUint64 } from '@yume-chan/dataview-bigint-polyfill';
|
||||
|
@ -15,8 +19,24 @@ setBigInt64(dataView, byteOffset, value, littleEndian);
|
|||
setBigUint64(dataView, byteOffset, value, littleEndian);
|
||||
```
|
||||
|
||||
## Polyfill `DataView`
|
||||
### Fallback: use native implementations if available.
|
||||
|
||||
```ts
|
||||
import { getBigInt64, getBigUint64, setBigInt64, setBigUint64 } from '@yume-chan/dataview-bigint-polyfill/esm/fallback';
|
||||
|
||||
getBigInt64(dataView, byteOffset, littleEndian);
|
||||
getBigUint64(dataView, byteOffset, littleEndian);
|
||||
setBigInt64(dataView, byteOffset, value, littleEndian);
|
||||
setBigUint64(dataView, byteOffset, value, littleEndian);
|
||||
```
|
||||
|
||||
### Polyfill: patch `DataView.prototype` when native support is not available.
|
||||
|
||||
```ts
|
||||
import '@yume-chan/dataview-bigint-polyfill/esm/polyfill.js';
|
||||
|
||||
dataView.getBigInt64(byteOffset, littleEndian);
|
||||
dataView.getBigUint64(byteOffset, littleEndian);
|
||||
dataView.setBigInt64(byteOffset, value, littleEndian);
|
||||
dataView.setBigUint64(byteOffset, value, littleEndian);
|
||||
```
|
||||
|
|
21
libraries/dataview-bigint-polyfill/src/fallback.ts
Normal file
21
libraries/dataview-bigint-polyfill/src/fallback.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { getBigInt64 as fallbackGetBigInt64, getBigUint64 as fallbackGetBigUint64, setBigInt64 as fallbackSetBigInt64, setBigUint64 as fallbackSetBigUint64 } from './pure';
|
||||
|
||||
export const getBigInt64 =
|
||||
'getBigInt64' in DataView.prototype ?
|
||||
(dataView: DataView, byteOffset: number, littleEndian: boolean | undefined) => dataView.getBigInt64(byteOffset, littleEndian) :
|
||||
fallbackGetBigInt64;
|
||||
|
||||
export const getBigUint64 =
|
||||
'getBigUint64' in DataView.prototype ?
|
||||
(dataView: DataView, byteOffset: number, littleEndian: boolean | undefined) => dataView.getBigUint64(byteOffset, littleEndian) :
|
||||
fallbackGetBigUint64;
|
||||
|
||||
export const setBigInt64 =
|
||||
'setBigInt64' in DataView.prototype ?
|
||||
(dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) => dataView.setBigInt64(byteOffset, value, littleEndian) :
|
||||
fallbackSetBigInt64;
|
||||
|
||||
export const setBigUint64 =
|
||||
'setBigUint64' in DataView.prototype ?
|
||||
(dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) => dataView.setBigUint64(byteOffset, value, littleEndian) :
|
||||
fallbackSetBigUint64;
|
|
@ -1,5 +1,20 @@
|
|||
const BigInt32 = BigInt(32);
|
||||
|
||||
export function getBigInt64(dataView: DataView, byteOffset: number, littleEndian: boolean | undefined): bigint {
|
||||
const littleEndianMask = Number(!!littleEndian);
|
||||
const bigEndianMask = Number(!littleEndian);
|
||||
|
||||
return (
|
||||
BigInt(
|
||||
dataView.getInt32(byteOffset, littleEndian) * bigEndianMask +
|
||||
dataView.getInt32(byteOffset + 4, littleEndian) * littleEndianMask
|
||||
) << BigInt32) |
|
||||
BigInt(
|
||||
dataView.getUint32(byteOffset, littleEndian) * littleEndianMask +
|
||||
dataView.getUint32(byteOffset + 4, littleEndian) * bigEndianMask
|
||||
);
|
||||
}
|
||||
|
||||
export function getBigUint64(dataView: DataView, byteOffset: number, littleEndian: boolean | undefined): bigint {
|
||||
const a = dataView.getUint32(byteOffset, littleEndian);
|
||||
const b = dataView.getUint32(byteOffset + 4, littleEndian);
|
||||
|
@ -14,27 +29,6 @@ export function getBigUint64(dataView: DataView, byteOffset: number, littleEndia
|
|||
BigInt(a * littleEndianMask + b * bigEndianMask);
|
||||
};
|
||||
|
||||
export function setBigUint64(dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) {
|
||||
const hi = Number(value >> BigInt32);
|
||||
const lo = Number(value & BigInt(0xFFFFFFFF));
|
||||
|
||||
if (littleEndian) {
|
||||
dataView.setUint32(byteOffset + 4, hi, littleEndian);
|
||||
dataView.setUint32(byteOffset, lo, littleEndian);
|
||||
} else {
|
||||
dataView.setUint32(byteOffset, hi, littleEndian);
|
||||
dataView.setUint32(byteOffset + 4, lo, littleEndian);
|
||||
}
|
||||
};
|
||||
|
||||
export function getBigInt64(dataView: DataView, byteOffset: number, littleEndian: boolean | undefined): bigint {
|
||||
const littleEndianMask = Number(!!littleEndian);
|
||||
const bigEndianMask = Number(!littleEndian);
|
||||
|
||||
return (BigInt(dataView.getInt32(byteOffset, littleEndian) * bigEndianMask + dataView.getInt32(byteOffset + 4, littleEndian) * littleEndianMask) << BigInt32) |
|
||||
BigInt(dataView.getUint32(byteOffset, littleEndian) * littleEndianMask + dataView.getUint32(byteOffset + 4, littleEndian) * bigEndianMask);
|
||||
}
|
||||
|
||||
export function setBigInt64(dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) {
|
||||
const hi = Number(value >> BigInt32);
|
||||
const lo = Number(value & BigInt(0xFFFFFFFF));
|
||||
|
@ -47,3 +41,16 @@ export function setBigInt64(dataView: DataView, byteOffset: number, value: bigin
|
|||
dataView.setUint32(byteOffset + 4, lo, littleEndian);
|
||||
}
|
||||
}
|
||||
|
||||
export function setBigUint64(dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) {
|
||||
const hi = Number(value >> BigInt32);
|
||||
const lo = Number(value & BigInt(0xFFFFFFFF));
|
||||
|
||||
if (littleEndian) {
|
||||
dataView.setUint32(byteOffset + 4, hi, littleEndian);
|
||||
dataView.setUint32(byteOffset, lo, littleEndian);
|
||||
} else {
|
||||
dataView.setUint32(byteOffset, hi, littleEndian);
|
||||
dataView.setUint32(byteOffset + 4, lo, littleEndian);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -70,28 +70,25 @@ const buffer = MyStruct.serialize({
|
|||
|
||||
## Compatibility
|
||||
|
||||
| | Chrome | Edge | Firefox | Internet Explorer | Safari | Node.js |
|
||||
| --------------------------------------------------------------- | ------ | ---- | ------- | ----------------- | ------------------ | -------------------- |
|
||||
| **Basic usage** | 32 | 12 | 29 | 10<sup>1</sup> | 8 | 0.12 |
|
||||
| [`Promise`][MDN_Promise] | 32 | 12 | 29 | No<sup>1</sup> | 8 | 0.12 |
|
||||
| [`ArrayBuffer`][MDN_ArrayBuffer] | 7 | 12 | 4 | 10 | 5.1 | 0.10 |
|
||||
| [`Uint8Array`][MDN_Uint8Array] | 7 | 12 | 4 | 10 | 5.1 | 0.10 |
|
||||
| [`DataView`][MDN_DataView] | 9 | 12 | 15 | 10 | 5.1 | 0.10 |
|
||||
| **Use [`int64`/`uint64`](#int64uint64) API** | 67 | 79 | 68 | No<sup>2</sup> | 14<sup>3</sup>, 15 | 10.4<sup>5</sup>, 11 |
|
||||
| [`BigInt`][MDN_BigInt] | 67 | 79 | 68 | No<sup>2</sup> | 14 | 10.4 |
|
||||
| [`DataView`][MDN_DataView] `BigInt` API | 67 | 79 | 68 | No | 15 | 10.4 |
|
||||
| **Use [`string`](#arraybufferuint8clampedarraystring) API** | 38 | 79 | 29 | 10<sup>4</sup> | 10.1 | 8.3<sup>5</sup>, 11 |
|
||||
| [`TextEncoder`][MDN_TextEncoder] | 38 | 79 | 19 | No | 10.1 | 11 |
|
||||
| | Chrome | Edge | Firefox | Internet Explorer | Safari | Node.js |
|
||||
| ------------------------------------------------------------ | ------ | ---- | ------- | ----------------- | ------ | ------------------- |
|
||||
| **Basic usage** | 32 | 12 | 29 | 10<sup>1</sup> | 8 | 0.12 |
|
||||
| [`Promise`][MDN_Promise] | 32 | 12 | 29 | No<sup>1</sup> | 8 | 0.12 |
|
||||
| [`ArrayBuffer`][MDN_ArrayBuffer] | 7 | 12 | 4 | 10 | 5.1 | 0.10 |
|
||||
| [`Uint8Array`][MDN_Uint8Array] | 7 | 12 | 4 | 10 | 5.1 | 0.10 |
|
||||
| [`DataView`][MDN_DataView] | 9 | 12 | 15 | 10 | 5.1 | 0.10 |
|
||||
| **Use [`int64`/`uint64`](#int64uint64) type** | 67 | 79 | 68 | No<sup>2</sup> | 14 | 10.4 |
|
||||
| [`BigInt`][MDN_BigInt] | 67 | 79 | 68 | No<sup>2</sup> | 14 | 10.4 |
|
||||
| **Use [`string`](#arraybufferuint8clampedarraystring) type** | 38 | 79 | 29 | 10<sup>3</sup> | 10.1 | 8.3<sup>4</sup>, 11 |
|
||||
| [`TextEncoder`][MDN_TextEncoder] | 38 | 79 | 19 | No | 10.1 | 11 |
|
||||
|
||||
<sup>1</sup> Requires a polyfill for Promise (e.g. [promise-polyfill](https://www.npmjs.com/package/promise-polyfill))
|
||||
|
||||
<sup>2</sup> `BigInt` can't be polyfilled
|
||||
|
||||
<sup>3</sup> `int64` requires a polyfill for `DataView#getBigUint64` and `DataView#setBigUint64`; `uint64` requires a polyfill for `DataView#getBigInt64` and `DataView#setBigInt64`
|
||||
<sup>3</sup> Requires a polyfill for `TextEncoder` and `TextDecoder` (e.g. [fast-text-encoding](https://www.npmjs.com/package/fast-text-encoding))
|
||||
|
||||
<sup>4</sup> Requires a polyfill for `TextEncoder` and `TextDecoder` (e.g. [fast-text-encoding](https://www.npmjs.com/package/fast-text-encoding))
|
||||
|
||||
<sup>5</sup> `TextEncoder` and `TextDecoder` are only available in `util` module. Must be assigned to global object.
|
||||
<sup>4</sup> `TextEncoder` and `TextDecoder` are only available in `util` module. Must be assigned to `globalThis`.
|
||||
|
||||
[MDN_Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
|
||||
[MDN_ArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"bluebird": "^3.7.2",
|
||||
"tslib": "^2.3.1"
|
||||
"tslib": "^2.3.1",
|
||||
"@yume-chan/dataview-bigint-polyfill": "0.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^26.6.3",
|
||||
|
|
|
@ -29,11 +29,11 @@ export class StructValue {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets a previously `StructFieldValue` for `key`
|
||||
* Gets the `StructFieldValue` for `key`
|
||||
*
|
||||
* @param key The field name
|
||||
*/
|
||||
public get(key: PropertyKey): StructFieldValue {
|
||||
return this.fieldValues[key];
|
||||
return this.fieldValues[key]!;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { StructAsyncDeserializeStream, StructDefaultOptions, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from './basic';
|
||||
import { Struct } from './struct';
|
||||
import { ArrayBufferFieldType, FixedLengthArrayBufferLikeFieldDefinition, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition } from './types';
|
||||
import { ArrayBufferFieldType, BigIntFieldDefinition, BigIntFieldType, FixedLengthArrayBufferLikeFieldDefinition, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition } from './types';
|
||||
import { ValueOrPromise } from './utils';
|
||||
|
||||
class MockDeserializationStream implements StructDeserializeStream {
|
||||
|
@ -86,7 +86,7 @@ describe('Struct', () => {
|
|||
struct.int8('foo');
|
||||
expect(struct).toHaveProperty('size', 1);
|
||||
|
||||
const definition = struct['_fields'][0][1] as NumberFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as NumberFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(NumberFieldDefinition);
|
||||
expect(definition.type).toBe(NumberFieldType.Int8);
|
||||
});
|
||||
|
@ -96,7 +96,7 @@ describe('Struct', () => {
|
|||
struct.uint8('foo');
|
||||
expect(struct).toHaveProperty('size', 1);
|
||||
|
||||
const definition = struct['_fields'][0][1] as NumberFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as NumberFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(NumberFieldDefinition);
|
||||
expect(definition.type).toBe(NumberFieldType.Uint8);
|
||||
});
|
||||
|
@ -106,7 +106,7 @@ describe('Struct', () => {
|
|||
struct.int16('foo');
|
||||
expect(struct).toHaveProperty('size', 2);
|
||||
|
||||
const definition = struct['_fields'][0][1] as NumberFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as NumberFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(NumberFieldDefinition);
|
||||
expect(definition.type).toBe(NumberFieldType.Int16);
|
||||
});
|
||||
|
@ -116,7 +116,7 @@ describe('Struct', () => {
|
|||
struct.uint16('foo');
|
||||
expect(struct).toHaveProperty('size', 2);
|
||||
|
||||
const definition = struct['_fields'][0][1] as NumberFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as NumberFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(NumberFieldDefinition);
|
||||
expect(definition.type).toBe(NumberFieldType.Uint16);
|
||||
});
|
||||
|
@ -126,7 +126,7 @@ describe('Struct', () => {
|
|||
struct.int32('foo');
|
||||
expect(struct).toHaveProperty('size', 4);
|
||||
|
||||
const definition = struct['_fields'][0][1] as NumberFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as NumberFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(NumberFieldDefinition);
|
||||
expect(definition.type).toBe(NumberFieldType.Int32);
|
||||
});
|
||||
|
@ -136,7 +136,7 @@ describe('Struct', () => {
|
|||
struct.uint32('foo');
|
||||
expect(struct).toHaveProperty('size', 4);
|
||||
|
||||
const definition = struct['_fields'][0][1] as NumberFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as NumberFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(NumberFieldDefinition);
|
||||
expect(definition.type).toBe(NumberFieldType.Uint32);
|
||||
});
|
||||
|
@ -146,9 +146,9 @@ describe('Struct', () => {
|
|||
struct.int64('foo');
|
||||
expect(struct).toHaveProperty('size', 8);
|
||||
|
||||
const definition = struct['_fields'][0][1] as NumberFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(NumberFieldDefinition);
|
||||
expect(definition.type).toBe(NumberFieldType.Int64);
|
||||
const definition = struct['_fields'][0]![1] as BigIntFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(BigIntFieldDefinition);
|
||||
expect(definition.type).toBe(BigIntFieldType.Int64);
|
||||
});
|
||||
|
||||
it('`uint64` should append an `uint64` field', () => {
|
||||
|
@ -156,9 +156,9 @@ describe('Struct', () => {
|
|||
struct.uint64('foo');
|
||||
expect(struct).toHaveProperty('size', 8);
|
||||
|
||||
const definition = struct['_fields'][0][1] as NumberFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(NumberFieldDefinition);
|
||||
expect(definition.type).toBe(NumberFieldType.Uint64);
|
||||
const definition = struct['_fields'][0]![1] as BigIntFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(BigIntFieldDefinition);
|
||||
expect(definition.type).toBe(BigIntFieldType.Uint64);
|
||||
});
|
||||
|
||||
describe('#arrayBufferLike', () => {
|
||||
|
@ -168,7 +168,7 @@ describe('Struct', () => {
|
|||
struct.arrayBuffer('foo', { length: 10 });
|
||||
expect(struct).toHaveProperty('size', 10);
|
||||
|
||||
const definition = struct['_fields'][0][1] as FixedLengthArrayBufferLikeFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as FixedLengthArrayBufferLikeFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(FixedLengthArrayBufferLikeFieldDefinition);
|
||||
expect(definition.type).toBeInstanceOf(ArrayBufferFieldType);
|
||||
expect(definition.options.length).toBe(10);
|
||||
|
@ -179,7 +179,7 @@ describe('Struct', () => {
|
|||
struct.uint8ClampedArray('foo', { length: 10 });
|
||||
expect(struct).toHaveProperty('size', 10);
|
||||
|
||||
const definition = struct['_fields'][0][1] as FixedLengthArrayBufferLikeFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as FixedLengthArrayBufferLikeFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(FixedLengthArrayBufferLikeFieldDefinition);
|
||||
expect(definition.type).toBeInstanceOf(Uint8ClampedArrayFieldType);
|
||||
expect(definition.options.length).toBe(10);
|
||||
|
@ -190,7 +190,7 @@ describe('Struct', () => {
|
|||
struct.string('foo', { length: 10 });
|
||||
expect(struct).toHaveProperty('size', 10);
|
||||
|
||||
const definition = struct['_fields'][0][1] as FixedLengthArrayBufferLikeFieldDefinition;
|
||||
const definition = struct['_fields'][0]![1] as FixedLengthArrayBufferLikeFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(FixedLengthArrayBufferLikeFieldDefinition);
|
||||
expect(definition.type).toBeInstanceOf(StringFieldType);
|
||||
expect(definition.options.length).toBe(10);
|
||||
|
@ -205,7 +205,7 @@ describe('Struct', () => {
|
|||
struct.arrayBuffer('bar', { lengthField: 'barLength' });
|
||||
expect(struct).toHaveProperty('size', 1);
|
||||
|
||||
const definition = struct['_fields'][1][1] as VariableLengthArrayBufferLikeFieldDefinition;
|
||||
const definition = struct['_fields'][1]![1] as VariableLengthArrayBufferLikeFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(VariableLengthArrayBufferLikeFieldDefinition);
|
||||
expect(definition.type).toBeInstanceOf(ArrayBufferFieldType);
|
||||
expect(definition.options.lengthField).toBe('barLength');
|
||||
|
@ -218,7 +218,7 @@ describe('Struct', () => {
|
|||
struct.uint8ClampedArray('bar', { lengthField: 'barLength' });
|
||||
expect(struct).toHaveProperty('size', 1);
|
||||
|
||||
const definition = struct['_fields'][1][1] as VariableLengthArrayBufferLikeFieldDefinition;
|
||||
const definition = struct['_fields'][1]![1] as VariableLengthArrayBufferLikeFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(VariableLengthArrayBufferLikeFieldDefinition);
|
||||
expect(definition.type).toBeInstanceOf(Uint8ClampedArrayFieldType);
|
||||
expect(definition.options.lengthField).toBe('barLength');
|
||||
|
@ -232,7 +232,7 @@ describe('Struct', () => {
|
|||
struct.string('bar', { lengthField: 'barLength' });
|
||||
expect(struct).toHaveProperty('size', 1);
|
||||
|
||||
const definition = struct['_fields'][1][1] as VariableLengthArrayBufferLikeFieldDefinition;
|
||||
const definition = struct['_fields'][1]![1] as VariableLengthArrayBufferLikeFieldDefinition;
|
||||
expect(definition).toBeInstanceOf(VariableLengthArrayBufferLikeFieldDefinition);
|
||||
expect(definition.type).toBeInstanceOf(StringFieldType);
|
||||
expect(definition.options.lengthField).toBe('barLength');
|
||||
|
@ -251,21 +251,21 @@ describe('Struct', () => {
|
|||
.fields(sub)
|
||||
.int64('int64');
|
||||
|
||||
const field0 = struct['_fields'][0];
|
||||
const field0 = struct['_fields'][0]!;
|
||||
expect(field0).toHaveProperty('0', 'int8');
|
||||
expect(field0[1]).toHaveProperty('type', NumberFieldType.Int8);
|
||||
|
||||
const field1 = struct['_fields'][1];
|
||||
const field1 = struct['_fields'][1]!;
|
||||
expect(field1).toHaveProperty('0', 'int16');
|
||||
expect(field1[1]).toHaveProperty('type', NumberFieldType.Int16);
|
||||
|
||||
const field2 = struct['_fields'][2];
|
||||
const field2 = struct['_fields'][2]!;
|
||||
expect(field2).toHaveProperty('0', 'int32');
|
||||
expect(field2[1]).toHaveProperty('type', NumberFieldType.Int32);
|
||||
|
||||
const field3 = struct['_fields'][3];
|
||||
const field3 = struct['_fields'][3]!;
|
||||
expect(field3).toHaveProperty('0', 'int64');
|
||||
expect(field3[1]).toHaveProperty('type', NumberFieldType.Int64);
|
||||
expect(field3[1]).toHaveProperty('type', BigIntFieldType.Int64);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -396,7 +396,6 @@ describe('Struct', () => {
|
|||
.int8('foo')
|
||||
.int16('bar');
|
||||
|
||||
const stream = new MockDeserializationStream();
|
||||
const result = new Uint8Array(struct.serialize({ foo: 0x42, bar: 0x1024 }));
|
||||
|
||||
expect(result).toEqual(new Uint8Array([0x42, 0x10, 0x24]));
|
||||
|
@ -407,7 +406,6 @@ describe('Struct', () => {
|
|||
.int8('fooLength')
|
||||
.arrayBuffer('foo', { lengthField: 'fooLength' });
|
||||
|
||||
const stream = new MockDeserializationStream();
|
||||
const result = new Uint8Array(struct.serialize({ foo: new Uint8Array([0x03, 0x04, 0x05]).buffer }));
|
||||
|
||||
expect(result).toEqual(new Uint8Array([0x03, 0x03, 0x04, 0x05]));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions } from './basic';
|
||||
import { StructDefaultOptions, StructValue } from './basic';
|
||||
import { Syncbird } from "./syncbird";
|
||||
import { ArrayBufferFieldType, ArrayBufferLikeFieldType, FixedLengthArrayBufferLikeFieldDefinition, FixedLengthArrayBufferLikeFieldOptions, LengthField, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldOptions } from './types';
|
||||
import { ArrayBufferFieldType, ArrayBufferLikeFieldType, BigIntFieldDefinition, BigIntFieldType, FixedLengthArrayBufferLikeFieldDefinition, FixedLengthArrayBufferLikeFieldOptions, LengthField, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldOptions } from './types';
|
||||
import { Evaluate, Identity, Overwrite, ValueOrPromise } from "./utils";
|
||||
|
||||
export interface StructLike<TValue> {
|
||||
|
@ -185,7 +185,7 @@ export class Struct<
|
|||
|
||||
private _extra: PropertyDescriptorMap = {};
|
||||
|
||||
private _postDeserialized?: StructPostDeserialized<any, any>;
|
||||
private _postDeserialized?: StructPostDeserialized<any, any> | undefined;
|
||||
|
||||
public constructor(options?: Partial<Readonly<StructOptions>>) {
|
||||
this.options = { ...StructDefaultOptions, ...options };
|
||||
|
@ -359,6 +359,21 @@ export class Struct<
|
|||
);
|
||||
}
|
||||
|
||||
private bigint<
|
||||
TName extends PropertyKey,
|
||||
TType extends BigIntFieldType = BigIntFieldType,
|
||||
TTypeScriptType = TType['TTypeScriptType']
|
||||
>(
|
||||
name: TName,
|
||||
type: TType,
|
||||
_typescriptType?: TTypeScriptType,
|
||||
) {
|
||||
return this.field(
|
||||
name,
|
||||
new BigIntFieldDefinition(type, _typescriptType),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an `int64` field to the `Struct`
|
||||
*
|
||||
|
@ -366,14 +381,14 @@ export class Struct<
|
|||
*/
|
||||
public int64<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = (typeof NumberFieldType)['Int64']['TTypeScriptType']
|
||||
TTypeScriptType = BigIntFieldType['TTypeScriptType']
|
||||
>(
|
||||
name: TName,
|
||||
_typescriptType?: TTypeScriptType,
|
||||
) {
|
||||
return this.number(
|
||||
return this.bigint(
|
||||
name,
|
||||
NumberFieldType.Int64,
|
||||
BigIntFieldType.Int64,
|
||||
_typescriptType
|
||||
);
|
||||
}
|
||||
|
@ -385,14 +400,14 @@ export class Struct<
|
|||
*/
|
||||
public uint64<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = (typeof NumberFieldType)['Uint64']['TTypeScriptType']
|
||||
TTypeScriptType = BigIntFieldType['TTypeScriptType']
|
||||
>(
|
||||
name: TName,
|
||||
_typescriptType?: TTypeScriptType,
|
||||
) {
|
||||
return this.number(
|
||||
return this.bigint(
|
||||
name,
|
||||
NumberFieldType.Uint64,
|
||||
BigIntFieldType.Int64,
|
||||
_typescriptType
|
||||
);
|
||||
}
|
||||
|
|
101
libraries/struct/src/types/bigint.ts
Normal file
101
libraries/struct/src/types/bigint.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
|
||||
import { getBigInt64, getBigUint64, setBigInt64, setBigUint64 } from '@yume-chan/dataview-bigint-polyfill/esm/fallback';
|
||||
import { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from "../basic";
|
||||
import { Syncbird } from "../syncbird";
|
||||
import { ValueOrPromise } from "../utils";
|
||||
|
||||
type DataViewBigInt64Getter = (dataView: DataView, byteOffset: number, littleEndian: boolean | undefined) => bigint;
|
||||
|
||||
type DataViewBigInt64Setter = (dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) => void;
|
||||
|
||||
export class BigIntFieldType {
|
||||
public readonly TTypeScriptType!: bigint;
|
||||
|
||||
public readonly size: number;
|
||||
|
||||
public readonly getter: DataViewBigInt64Getter;
|
||||
|
||||
public readonly setter: DataViewBigInt64Setter;
|
||||
|
||||
public constructor(
|
||||
size: number,
|
||||
getter: DataViewBigInt64Getter,
|
||||
setter: DataViewBigInt64Setter,
|
||||
) {
|
||||
this.size = size;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
public static readonly Int64 = new BigIntFieldType(8, getBigInt64, setBigInt64);
|
||||
|
||||
public static readonly Uint64 = new BigIntFieldType(8, getBigUint64, setBigUint64);
|
||||
}
|
||||
|
||||
export class BigIntFieldDefinition<
|
||||
TType extends BigIntFieldType = BigIntFieldType,
|
||||
TTypeScriptType = TType["TTypeScriptType"],
|
||||
> extends StructFieldDefinition<
|
||||
void,
|
||||
TTypeScriptType
|
||||
> {
|
||||
public readonly type: TType;
|
||||
|
||||
public constructor(type: TType, _typescriptType?: TTypeScriptType) {
|
||||
super();
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public getSize(): number {
|
||||
return this.type.size;
|
||||
}
|
||||
|
||||
public create(
|
||||
options: Readonly<StructOptions>,
|
||||
struct: StructValue,
|
||||
value: TTypeScriptType,
|
||||
): BigIntFieldValue<this> {
|
||||
return new BigIntFieldValue(this, options, struct, value);
|
||||
}
|
||||
|
||||
public override deserialize(
|
||||
options: Readonly<StructOptions>,
|
||||
stream: StructDeserializeStream,
|
||||
struct: StructValue,
|
||||
): BigIntFieldValue<this>;
|
||||
public override deserialize(
|
||||
options: Readonly<StructOptions>,
|
||||
stream: StructAsyncDeserializeStream,
|
||||
struct: StructValue,
|
||||
): Promise<BigIntFieldValue<this>>;
|
||||
public override deserialize(
|
||||
options: Readonly<StructOptions>,
|
||||
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
||||
struct: StructValue,
|
||||
): ValueOrPromise<BigIntFieldValue<this>> {
|
||||
return Syncbird.try(() => {
|
||||
return stream.read(this.getSize());
|
||||
}).then(buffer => {
|
||||
const view = new DataView(buffer);
|
||||
const value = this.type.getter(
|
||||
view,
|
||||
0,
|
||||
options.littleEndian
|
||||
);
|
||||
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 {
|
||||
this.definition.type.setter(
|
||||
dataView,
|
||||
offset,
|
||||
this.value!,
|
||||
this.options.littleEndian
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
export * from './array-buffer';
|
||||
export * from './bigint';
|
||||
export * from './fixed-length-array-buffer';
|
||||
export * from './number';
|
||||
export * from './variable-length-array-buffer';
|
||||
|
|
|
@ -8,8 +8,8 @@ export type DataViewGetters =
|
|||
export type DataViewSetters =
|
||||
{ [TKey in keyof DataView]: TKey extends `set${string}` ? TKey : never }[keyof DataView];
|
||||
|
||||
export class NumberFieldType<TTypeScriptType extends number | bigint = number | bigint> {
|
||||
public readonly TTypeScriptType!: TTypeScriptType;
|
||||
export class NumberFieldType {
|
||||
public readonly TTypeScriptType!: number;
|
||||
|
||||
public readonly size: number;
|
||||
|
||||
|
@ -27,26 +27,22 @@ export class NumberFieldType<TTypeScriptType extends number | bigint = number |
|
|||
this.dataViewSetter = dataViewSetter;
|
||||
}
|
||||
|
||||
public static readonly Int8 = new NumberFieldType<number>(1, 'getInt8', 'setInt8');
|
||||
public static readonly Int8 = new NumberFieldType(1, 'getInt8', 'setInt8');
|
||||
|
||||
public static readonly Uint8 = new NumberFieldType<number>(1, 'getUint8', 'setUint8');
|
||||
public static readonly Uint8 = new NumberFieldType(1, 'getUint8', 'setUint8');
|
||||
|
||||
public static readonly Int16 = new NumberFieldType<number>(2, 'getInt16', 'setInt16');
|
||||
public static readonly Int16 = new NumberFieldType(2, 'getInt16', 'setInt16');
|
||||
|
||||
public static readonly Uint16 = new NumberFieldType<number>(2, 'getUint16', 'setUint16');
|
||||
public static readonly Uint16 = new NumberFieldType(2, 'getUint16', 'setUint16');
|
||||
|
||||
public static readonly Int32 = new NumberFieldType<number>(4, 'getInt32', 'setInt32');
|
||||
public static readonly Int32 = new NumberFieldType(4, 'getInt32', 'setInt32');
|
||||
|
||||
public static readonly Uint32 = new NumberFieldType<number>(4, 'getUint32', 'setUint32');
|
||||
|
||||
public static readonly Int64 = new NumberFieldType<bigint>(8, 'getBigInt64', 'setBigInt64');
|
||||
|
||||
public static readonly Uint64 = new NumberFieldType<bigint>(8, 'getBigUint64', 'setBigUint64');
|
||||
public static readonly Uint32 = new NumberFieldType(4, 'getUint32', 'setUint32');
|
||||
}
|
||||
|
||||
export class NumberFieldDefinition<
|
||||
TType extends NumberFieldType = NumberFieldType,
|
||||
TTypeScriptType = TType['TTypeScriptType'],
|
||||
TTypeScriptType = TType["TTypeScriptType"],
|
||||
> extends StructFieldDefinition<
|
||||
void,
|
||||
TTypeScriptType
|
||||
|
@ -92,8 +88,8 @@ export class NumberFieldDefinition<
|
|||
const value = view[this.type.dataViewGetter](
|
||||
0,
|
||||
options.littleEndian
|
||||
) as any;
|
||||
return this.create(options, struct, value);
|
||||
);
|
||||
return this.create(options, struct, value as any);
|
||||
}).valueOrPromise();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -438,7 +438,9 @@
|
|||
},
|
||||
{
|
||||
"packageName": "@yume-chan/dataview-bigint-polyfill",
|
||||
"projectFolder": "libraries/dataview-bigint-polyfill"
|
||||
"projectFolder": "libraries/dataview-bigint-polyfill",
|
||||
"shouldPublish": true,
|
||||
"versionPolicyName": "adb"
|
||||
},
|
||||
{
|
||||
"packageName": "@yume-chan/event",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue