mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-03 17:59:50 +02:00
refactor(struct): remove context interfaces
This commit is contained in:
parent
949aaab818
commit
08767c7b71
20 changed files with 429 additions and 333 deletions
78
common/config/rush/pnpm-lock.yaml
generated
78
common/config/rush/pnpm-lock.yaml
generated
|
@ -23,12 +23,14 @@ specifiers:
|
||||||
'@rush-temp/ts-package-builder': file:./projects/ts-package-builder.tgz
|
'@rush-temp/ts-package-builder': file:./projects/ts-package-builder.tgz
|
||||||
'@rush-temp/unofficial-adb-book': file:./projects/unofficial-adb-book.tgz
|
'@rush-temp/unofficial-adb-book': file:./projects/unofficial-adb-book.tgz
|
||||||
'@svgr/webpack': ^5.5.0
|
'@svgr/webpack': ^5.5.0
|
||||||
|
'@types/bluebird': ^3.5.36
|
||||||
'@types/dom-webcodecs': ^0.1.3
|
'@types/dom-webcodecs': ^0.1.3
|
||||||
'@types/jest': ^26.0.23
|
'@types/jest': ^27.4.0
|
||||||
'@types/node': ^16.9.1
|
'@types/node': ^16.11.19
|
||||||
'@types/react': 17.0.27
|
'@types/react': 17.0.27
|
||||||
'@types/w3c-web-usb': ^1.0.4
|
'@types/w3c-web-usb': ^1.0.4
|
||||||
'@yume-chan/async': ^2.1.4
|
'@yume-chan/async': ^2.1.4
|
||||||
|
bluebird: ^3.7.2
|
||||||
clsx: ^1.1.1
|
clsx: ^1.1.1
|
||||||
eslint: 7.32.0
|
eslint: 7.32.0
|
||||||
eslint-config-next: ^12.0.7
|
eslint-config-next: ^12.0.7
|
||||||
|
@ -78,12 +80,14 @@ dependencies:
|
||||||
'@rush-temp/ts-package-builder': file:projects/ts-package-builder.tgz
|
'@rush-temp/ts-package-builder': file:projects/ts-package-builder.tgz
|
||||||
'@rush-temp/unofficial-adb-book': file:projects/unofficial-adb-book.tgz_fe5950f86df08edf541b29db75ee5ce3
|
'@rush-temp/unofficial-adb-book': file:projects/unofficial-adb-book.tgz_fe5950f86df08edf541b29db75ee5ce3
|
||||||
'@svgr/webpack': 5.5.0
|
'@svgr/webpack': 5.5.0
|
||||||
|
'@types/bluebird': 3.5.36
|
||||||
'@types/dom-webcodecs': 0.1.3
|
'@types/dom-webcodecs': 0.1.3
|
||||||
'@types/jest': 26.0.24
|
'@types/jest': 27.4.0
|
||||||
'@types/node': 16.11.17
|
'@types/node': 16.11.19
|
||||||
'@types/react': 17.0.27
|
'@types/react': 17.0.27
|
||||||
'@types/w3c-web-usb': 1.0.5
|
'@types/w3c-web-usb': 1.0.5
|
||||||
'@yume-chan/async': 2.1.4
|
'@yume-chan/async': 2.1.4
|
||||||
|
bluebird: 3.7.2
|
||||||
clsx: 1.1.1
|
clsx: 1.1.1
|
||||||
eslint: 7.32.0
|
eslint: 7.32.0
|
||||||
eslint-config-next: 12.0.7_eslint@7.32.0+next@12.0.7
|
eslint-config-next: 12.0.7_eslint@7.32.0+next@12.0.7
|
||||||
|
@ -3480,6 +3484,10 @@ packages:
|
||||||
'@babel/types': 7.16.0
|
'@babel/types': 7.16.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/bluebird/3.5.36:
|
||||||
|
resolution: {integrity: sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/body-parser/1.19.2:
|
/@types/body-parser/1.19.2:
|
||||||
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
|
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3603,11 +3611,11 @@ packages:
|
||||||
'@types/istanbul-lib-report': 3.0.0
|
'@types/istanbul-lib-report': 3.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/jest/26.0.24:
|
/@types/jest/27.4.0:
|
||||||
resolution: {integrity: sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==}
|
resolution: {integrity: sha512-gHl8XuC1RZ8H2j5sHv/JqsaxXkDDM9iDOgu0Wp8sjs4u/snb2PVehyWXJPr+ORA0RPpgw231mnutWI1+0hgjIQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
jest-diff: 26.6.2
|
jest-diff: 27.4.6
|
||||||
pretty-format: 26.6.2
|
pretty-format: 27.4.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/json-schema/7.0.9:
|
/@types/json-schema/7.0.9:
|
||||||
|
@ -3643,6 +3651,10 @@ packages:
|
||||||
resolution: {integrity: sha512-C1vTZME8cFo8uxY2ui41xcynEotVkczIVI5AjLmy5pkpBv/FtG+jhtOlfcPysI8VRVwoOMv6NJm44LGnoMSWkw==}
|
resolution: {integrity: sha512-C1vTZME8cFo8uxY2ui41xcynEotVkczIVI5AjLmy5pkpBv/FtG+jhtOlfcPysI8VRVwoOMv6NJm44LGnoMSWkw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/node/16.11.19:
|
||||||
|
resolution: {integrity: sha512-BPAcfDPoHlRQNKktbsbnpACGdypPFBuX4xQlsWDE7B8XXcfII+SpOLay3/qZmCLb39kV5S1RTYwXdkx2lwLYng==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/normalize-package-data/2.4.1:
|
/@types/normalize-package-data/2.4.1:
|
||||||
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
|
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -4204,6 +4216,11 @@ packages:
|
||||||
color-convert: 2.0.1
|
color-convert: 2.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/ansi-styles/5.2.0:
|
||||||
|
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/anymatch/2.0.0:
|
/anymatch/2.0.0:
|
||||||
resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==}
|
resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5981,6 +5998,11 @@ packages:
|
||||||
engines: {node: '>= 10.14.2'}
|
engines: {node: '>= 10.14.2'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/diff-sequences/27.4.0:
|
||||||
|
resolution: {integrity: sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==}
|
||||||
|
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/diffie-hellman/5.0.3:
|
/diffie-hellman/5.0.3:
|
||||||
resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
|
resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -8556,6 +8578,16 @@ packages:
|
||||||
pretty-format: 26.6.2
|
pretty-format: 26.6.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/jest-diff/27.4.6:
|
||||||
|
resolution: {integrity: sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==}
|
||||||
|
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
diff-sequences: 27.4.0
|
||||||
|
jest-get-type: 27.4.0
|
||||||
|
pretty-format: 27.4.6
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jest-docblock/26.0.0:
|
/jest-docblock/26.0.0:
|
||||||
resolution: {integrity: sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==}
|
resolution: {integrity: sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==}
|
||||||
engines: {node: '>= 10.14.2'}
|
engines: {node: '>= 10.14.2'}
|
||||||
|
@ -8609,6 +8641,11 @@ packages:
|
||||||
engines: {node: '>= 10.14.2'}
|
engines: {node: '>= 10.14.2'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/jest-get-type/27.4.0:
|
||||||
|
resolution: {integrity: sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==}
|
||||||
|
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jest-haste-map/26.6.2:
|
/jest-haste-map/26.6.2:
|
||||||
resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==}
|
resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==}
|
||||||
engines: {node: '>= 10.14.2'}
|
engines: {node: '>= 10.14.2'}
|
||||||
|
@ -10909,6 +10946,15 @@ packages:
|
||||||
react-is: 17.0.2
|
react-is: 17.0.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/pretty-format/27.4.6:
|
||||||
|
resolution: {integrity: sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==}
|
||||||
|
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 5.0.1
|
||||||
|
ansi-styles: 5.2.0
|
||||||
|
react-is: 17.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/pretty-time/1.1.0:
|
/pretty-time/1.1.0:
|
||||||
resolution: {integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==}
|
resolution: {integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
@ -13991,11 +14037,12 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/scrcpy.tgz:
|
file:projects/scrcpy.tgz:
|
||||||
resolution: {integrity: sha512-RhLyxPrPiFxJQ+Baq50eIebhA5txN5f+h4hIfFMcXQZzHFx7+HcdyZvUlDFS8Z5JngkwVGu5dD6lk59F78cCYA==, tarball: file:projects/scrcpy.tgz}
|
resolution: {integrity: sha512-ZoKLRmeOhzbvUl1tXGFjRmPKRFcGWQjkfocPpSRiZLvU0kdb62Plq65M2m0tnQuSsge+m1VcsbC7zf0fyMNmhQ==, tarball: file:projects/scrcpy.tgz}
|
||||||
name: '@rush-temp/scrcpy'
|
name: '@rush-temp/scrcpy'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/dom-webcodecs': 0.1.3
|
'@types/dom-webcodecs': 0.1.3
|
||||||
|
'@types/jest': 27.4.0
|
||||||
'@yume-chan/async': 2.1.4
|
'@yume-chan/async': 2.1.4
|
||||||
gh-release-fetch: 2.0.4
|
gh-release-fetch: 2.0.4
|
||||||
jest: 26.6.3
|
jest: 26.6.3
|
||||||
|
@ -14013,11 +14060,14 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/struct.tgz:
|
file:projects/struct.tgz:
|
||||||
resolution: {integrity: sha512-j1oCxwpkmde9LO5iyZHD3ndKGeD8ECVh7n8wpm4mqiQmHzay++2ky0djoqL5j3+sMycUmQfMKq7llNvNN0tDHg==, tarball: file:projects/struct.tgz}
|
resolution: {integrity: sha512-bQp42RvxKuE6ql0/uinMby4wEgAWnpgnWrtd2+Z3QD+SSTLIc3HpoXuuahoeWnx6rIWjS3zYgA5FKijJZflyGQ==, tarball: file:projects/struct.tgz}
|
||||||
name: '@rush-temp/struct'
|
name: '@rush-temp/struct'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 16.11.17
|
'@types/bluebird': 3.5.36
|
||||||
|
'@types/jest': 27.4.0
|
||||||
|
'@types/node': 16.11.19
|
||||||
|
bluebird: 3.7.2
|
||||||
jest: 26.6.3
|
jest: 26.6.3
|
||||||
tslib: 2.3.1
|
tslib: 2.3.1
|
||||||
typescript: 4.5.4
|
typescript: 4.5.4
|
||||||
|
@ -14030,12 +14080,12 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/ts-package-builder.tgz:
|
file:projects/ts-package-builder.tgz:
|
||||||
resolution: {integrity: sha512-M7CMjBTO+fqKzrmWZgx3veL5IqvlOvvjYRJsMe6l/ylsaApwrJthKN7QwHE1wcHm7M/cCbakhA8oucRMLSAqxw==, tarball: file:projects/ts-package-builder.tgz}
|
resolution: {integrity: sha512-EKE72WCmD/Gkx/d5IU0i1BApSo+DjvxTn9WyWeiq8VkVYw9MyXceD4Zio6Cntm7z2QHhaGkLkuCWhC0/h9XkQg==, tarball: file:projects/ts-package-builder.tgz}
|
||||||
name: '@rush-temp/ts-package-builder'
|
name: '@rush-temp/ts-package-builder'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/jest': 26.0.24
|
'@types/jest': 27.4.0
|
||||||
'@types/node': 16.11.17
|
'@types/node': 16.11.19
|
||||||
json5: 2.2.0
|
json5: 2.2.0
|
||||||
typescript: 4.5.4
|
typescript: 4.5.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
A C-style structure serializer and deserializer. Written in TypeScript and highly takes advantage of its type system.
|
A C-style structure serializer and deserializer. Written in TypeScript and highly takes advantage of its type system.
|
||||||
|
|
||||||
|
**WARNING:** The public API is UNSTABLE. If you have any questions, please open an issue.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
@ -214,9 +216,9 @@ So it's technically possible to pass in an incompatible type (e.g. `string`). Bu
|
||||||
const value = await struct.deserialize(stream);
|
const value = await struct.deserialize(stream);
|
||||||
value.foo; // number
|
value.foo; // number
|
||||||
|
|
||||||
struct.serialize({ }, context) // error: 'foo' is required
|
struct.serialize({ }) // error: 'foo' is required
|
||||||
struct.serialize({ foo: 'bar' }, context) // error: 'foo' must be a number
|
struct.serialize({ foo: 'bar' }) // error: 'foo' must be a number
|
||||||
struct.serialize({ foo: 42 }, context) // ok
|
struct.serialize({ foo: 42 }) // ok
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Set fields' type (can be used with [`placeholder` method](#placeholder))
|
2. Set fields' type (can be used with [`placeholder` method](#placeholder))
|
||||||
|
@ -235,9 +237,9 @@ So it's technically possible to pass in an incompatible type (e.g. `string`). Bu
|
||||||
value.foo; // MyEnum
|
value.foo; // MyEnum
|
||||||
value.bar; // MyEnum.a
|
value.bar; // MyEnum.a
|
||||||
|
|
||||||
struct.serialize({ foo: 42, bar: MyEnum.a }, context); // error: 'foo' must be of type `MyEnum`
|
struct.serialize({ foo: 42, bar: MyEnum.a }); // error: 'foo' must be of type `MyEnum`
|
||||||
struct.serialize({ foo: MyEnum.a, bar: MyEnum.b }, context); // error: 'bar' must be of type `MyEnum.a`
|
struct.serialize({ foo: MyEnum.a, bar: MyEnum.b }); // error: 'bar' must be of type `MyEnum.a`
|
||||||
struct.serialize({ foo: MyEnum.a, bar: MyEnum.b }, context); // ok
|
struct.serialize({ foo: MyEnum.a, bar: MyEnum.b }); // ok
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `int64`/`uint64`
|
#### `int64`/`uint64`
|
||||||
|
@ -334,7 +336,7 @@ Merges (flats) another `Struct`'s fields and extra fields into the current one.
|
||||||
.fields(MyStructV1)
|
.fields(MyStructV1)
|
||||||
.int32('field2');
|
.int32('field2');
|
||||||
|
|
||||||
const structV2 = await MyStructV2.deserialize(context);
|
const structV2 = await MyStructV2.deserialize(stream);
|
||||||
structV2.field1; // number
|
structV2.field1; // number
|
||||||
structV2.field2; // number
|
structV2.field2; // number
|
||||||
// Fields are flatten
|
// Fields are flatten
|
||||||
|
@ -352,7 +354,7 @@ Merges (flats) another `Struct`'s fields and extra fields into the current one.
|
||||||
.int32('field2')
|
.int32('field2')
|
||||||
.fields(MyStructV1);
|
.fields(MyStructV1);
|
||||||
|
|
||||||
const structV2 = await MyStructV2.deserialize(context);
|
const structV2 = await MyStructV2.deserialize(stream);
|
||||||
structV2.field1; // number
|
structV2.field1; // number
|
||||||
structV2.field2; // number
|
structV2.field2; // number
|
||||||
// Same result as above, but serialize/deserialize order is reversed
|
// Same result as above, but serialize/deserialize order is reversed
|
||||||
|
@ -409,8 +411,8 @@ Multiple calls merge all extra fields together.
|
||||||
value.foo; // number
|
value.foo; // number
|
||||||
value.bar; // 'hello'
|
value.bar; // 'hello'
|
||||||
|
|
||||||
struct.serialize({ foo: 42 }, context); // ok
|
struct.serialize({ foo: 42 }); // ok
|
||||||
struct.serialize({ foo: 42, bar: 'hello' }, context); // error: 'bar' is redundant
|
struct.serialize({ foo: 42, bar: 'hello' }); // error: 'bar' is redundant
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Add getters and methods. `this` in functions refers to the result object.
|
2. Add getters and methods. `this` in functions refers to the result object.
|
||||||
|
@ -520,37 +522,43 @@ A callback returning anything other than `undefined` will cause `deserialize` to
|
||||||
#### `deserialize`
|
#### `deserialize`
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
interface StructDeserializationContext {
|
interface StructDeserializeStream {
|
||||||
decodeUtf8(buffer: ArrayBuffer): string;
|
/**
|
||||||
|
* Read data from the underlying data source.
|
||||||
read(length: number): ArrayBuffer | Promise<ArrayBuffer>;
|
*
|
||||||
|
* Stream must return exactly `length` bytes or data. If that's not possible
|
||||||
|
* (due to end of file or other error condition), it must throw an error.
|
||||||
|
*/
|
||||||
|
read(length: number): ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(
|
deserialize(
|
||||||
context: StructDeserializationContext
|
stream: StructDeserializeStream,
|
||||||
|
): TPostDeserialized extends undefined
|
||||||
|
? Overwrite<TExtra, TValue>
|
||||||
|
: TPostDeserialized
|
||||||
|
>;
|
||||||
|
deserialize(
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
): Promise<
|
): Promise<
|
||||||
TPostDeserialized extends undefined
|
TPostDeserialized extends undefined
|
||||||
? Overwrite<TExtra, TValue>
|
? Overwrite<TExtra, TValue>
|
||||||
: TPostDeserialized
|
: TPostDeserialized
|
||||||
|
>
|
||||||
>;
|
>;
|
||||||
```
|
```
|
||||||
|
|
||||||
Deserialize a Struct value from `context`.
|
Deserialize a Struct value from `stream`.
|
||||||
|
|
||||||
As the signature shows, if the `postDeserialize` callback returns any value, `deserialize` will return that value instead.
|
As the signature shows, if the `postDeserialize` callback returns any value, `deserialize` will return that value instead.
|
||||||
|
|
||||||
The `read` method of `context`, when being called, should returns exactly `length` bytes of data (or throw an `Error` if it can't).
|
The `read` method of `stream`, when being called, should returns exactly `length` bytes of data (or throw an `Error` if it can't).
|
||||||
|
|
||||||
#### `serialize`
|
#### `serialize`
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
interface StructSerializationContext {
|
|
||||||
encodeUtf8(input: string): ArrayBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize(
|
serialize(
|
||||||
init: Omit<TFields, TOmitInitKey>,
|
init: Omit<TFields, TOmitInitKey>
|
||||||
context: StructSerializationContext
|
|
||||||
): ArrayBuffer;
|
): ArrayBuffer;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -634,7 +642,6 @@ Actual size should be returned from `StructFieldValue#getSize`
|
||||||
```ts
|
```ts
|
||||||
abstract create(
|
abstract create(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructSerializationContext,
|
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
value: TValue,
|
value: TValue,
|
||||||
): StructFieldValue<this>;
|
): StructFieldValue<this>;
|
||||||
|
@ -649,16 +656,21 @@ Derived classes must implement this method to create its own field value instanc
|
||||||
```ts
|
```ts
|
||||||
abstract deserialize(
|
abstract deserialize(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructDeserializationContext,
|
stream: StructDeserializeStream,
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
): ValueOrPromise<StructFieldValue<this>>;
|
): StructFieldValue<this>;
|
||||||
|
abstract deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): Promise<StructFieldValue<this>>;
|
||||||
```
|
```
|
||||||
|
|
||||||
Derived classes must implement this method to define how to deserialize a value from `context`. Can also return a `Promise`.
|
Derived classes must implement this method to define how to deserialize a value from `stream`. Can also return a `Promise`.
|
||||||
|
|
||||||
Usually implementations should be:
|
Usually implementations should be:
|
||||||
|
|
||||||
1. Somehow parse the value from `context`
|
1. Somehow parse the value from `stream`
|
||||||
2. Pass the value into its `create` method
|
2. Pass the value into its `create` method
|
||||||
|
|
||||||
Sometimes, some metadata is present when deserializing, but need to be calculated when serializing, for example a UTF-8 encoded string may have different length between itself (character count) and serialized form (byte length). So `deserialize` can save those metadata on the `StructFieldValue` instance for later use.
|
Sometimes, some metadata is present when deserializing, but need to be calculated when serializing, for example a UTF-8 encoded string may have different length between itself (character count) and serialized form (byte length). So `deserialize` can save those metadata on the `StructFieldValue` instance for later use.
|
||||||
|
@ -701,8 +713,7 @@ If one needs to manipulate other states when getting/setting values, they can ov
|
||||||
```ts
|
```ts
|
||||||
abstract serialize(
|
abstract serialize(
|
||||||
dataView: DataView,
|
dataView: DataView,
|
||||||
offset: number,
|
offset: number
|
||||||
context: StructSerializationContext
|
|
||||||
): void;
|
): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,15 @@
|
||||||
"prepublishOnly": "npm run build"
|
"prepublishOnly": "npm run build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bluebird": "^3.7.2",
|
||||||
"tslib": "^2.3.1"
|
"tslib": "^2.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^26.6.3",
|
"jest": "^26.6.3",
|
||||||
"typescript": "^4.4.3",
|
"typescript": "^4.4.3",
|
||||||
"@yume-chan/ts-package-builder": "^1.0.0",
|
"@yume-chan/ts-package-builder": "^1.0.0",
|
||||||
"@types/node": "^16.9.1"
|
"@types/jest": "^27.4.0",
|
||||||
|
"@types/node": "^16.11.19",
|
||||||
|
"@types/bluebird": "^3.5.36"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,14 @@
|
||||||
import type { ValueOrPromise } from '../utils';
|
export interface StructDeserializeStream {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context with enough methods to serialize a struct
|
* Read data from the underlying data source.
|
||||||
|
*
|
||||||
|
* Stream must return exactly `length` bytes or data. If that's not possible
|
||||||
|
* (due to end of file or other error condition), it must throw an error.
|
||||||
*/
|
*/
|
||||||
export interface StructSerializationContext {
|
|
||||||
/**
|
|
||||||
* Encode the specified string into an ArrayBuffer using UTF-8 encoding
|
|
||||||
*/
|
|
||||||
encodeUtf8(input: string): ArrayBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StructSyncDeserializationContext extends StructSerializationContext {
|
|
||||||
decodeUtf8(buffer: ArrayBuffer): string;
|
|
||||||
|
|
||||||
read(length: number): ArrayBuffer;
|
read(length: number): ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export interface StructAsyncDeserializeStream {
|
||||||
* Context with enough methods to deserialize a struct
|
|
||||||
*/
|
|
||||||
export interface StructDeserializationContext extends StructSerializationContext {
|
|
||||||
/**
|
|
||||||
* Decode the specified `ArrayBuffer` using UTF-8 encoding
|
|
||||||
*/
|
|
||||||
decodeUtf8(buffer: ArrayBuffer): string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read data from the underlying data source.
|
* Read data from the underlying data source.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { ValueOrPromise } from '../utils';
|
import type { ValueOrPromise } from '../utils';
|
||||||
import type { StructDeserializationContext, StructOptions, StructSerializationContext } from './context';
|
import type { StructAsyncDeserializeStream, StructDeserializeStream, StructOptions } from './context';
|
||||||
import { StructFieldDefinition } from './definition';
|
import { StructFieldDefinition } from './definition';
|
||||||
import type { StructFieldValue } from './field-value';
|
import type { StructFieldValue } from './field-value';
|
||||||
import type { StructValue } from './struct-value';
|
import type { StructValue } from './struct-value';
|
||||||
|
@ -14,10 +14,24 @@ describe('StructFieldDefinition', () => {
|
||||||
public getSize(): number {
|
public getSize(): number {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
public create(options: Readonly<StructOptions>, context: StructSerializationContext, struct: StructValue, value: unknown): StructFieldValue<this> {
|
public create(options: Readonly<StructOptions>, struct: StructValue, value: unknown): StructFieldValue<this> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
public deserialize(options: Readonly<StructOptions>, context: StructDeserializationContext, struct: StructValue): ValueOrPromise<StructFieldValue<this>> {
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): StructFieldValue<this>;
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): Promise<StructFieldValue<this>>;
|
||||||
|
public deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue
|
||||||
|
): ValueOrPromise<StructFieldValue<this>> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import type { ValueOrPromise } from '../utils';
|
import type { StructAsyncDeserializeStream, StructDeserializeStream, StructOptions } from './context';
|
||||||
import type { StructDeserializationContext, StructOptions, StructSerializationContext } from './context';
|
|
||||||
import type { StructFieldValue } from './field-value';
|
import type { StructFieldValue } from './field-value';
|
||||||
import type { StructValue } from './struct-value';
|
import type { StructValue } from './struct-value';
|
||||||
|
|
||||||
|
@ -53,7 +52,6 @@ export abstract class StructFieldDefinition<
|
||||||
*/
|
*/
|
||||||
public abstract create(
|
public abstract create(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructSerializationContext,
|
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
value: TValue,
|
value: TValue,
|
||||||
): StructFieldValue<this>;
|
): StructFieldValue<this>;
|
||||||
|
@ -63,7 +61,12 @@ export abstract class StructFieldDefinition<
|
||||||
*/
|
*/
|
||||||
public abstract deserialize(
|
public abstract deserialize(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructDeserializationContext,
|
stream: StructDeserializeStream,
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
): ValueOrPromise<StructFieldValue<this>>;
|
): StructFieldValue<this>;
|
||||||
|
public abstract deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): Promise<StructFieldValue<this>>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { ValueOrPromise } from '../utils';
|
import type { ValueOrPromise } from '../utils';
|
||||||
import type { StructDeserializationContext, StructOptions, StructSerializationContext } from './context';
|
import type { StructAsyncDeserializeStream, StructDeserializeStream, StructOptions } from './context';
|
||||||
import { StructFieldDefinition } from './definition';
|
import { StructFieldDefinition } from './definition';
|
||||||
import { StructFieldValue } from './field-value';
|
import { StructFieldValue } from './field-value';
|
||||||
import type { StructValue } from './struct-value';
|
import type { StructValue } from './struct-value';
|
||||||
|
@ -8,21 +8,19 @@ describe('StructFieldValue', () => {
|
||||||
describe('.constructor', () => {
|
describe('.constructor', () => {
|
||||||
it('should save parameters', () => {
|
it('should save parameters', () => {
|
||||||
class MockStructFieldValue extends StructFieldValue {
|
class MockStructFieldValue extends StructFieldValue {
|
||||||
public serialize(dataView: DataView, offset: number, context: StructSerializationContext): void {
|
public serialize(dataView: DataView, offset: number): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const definition = 1 as any;
|
const definition = {} as any;
|
||||||
const options = 2 as any;
|
const options = {} as any;
|
||||||
const context = 3 as any;
|
const struct = {} as any;
|
||||||
const struct = 4 as any;
|
const value = {} as any;
|
||||||
const value = 5 as any;
|
|
||||||
|
|
||||||
const fieldValue = new MockStructFieldValue(definition, options, context, struct, value);
|
const fieldValue = new MockStructFieldValue(definition, options, struct, value);
|
||||||
expect(fieldValue).toHaveProperty('definition', definition);
|
expect(fieldValue).toHaveProperty('definition', definition);
|
||||||
expect(fieldValue).toHaveProperty('options', options);
|
expect(fieldValue).toHaveProperty('options', options);
|
||||||
expect(fieldValue).toHaveProperty('context', context);
|
|
||||||
expect(fieldValue).toHaveProperty('struct', struct);
|
expect(fieldValue).toHaveProperty('struct', struct);
|
||||||
expect(fieldValue.get()).toBe(value);
|
expect(fieldValue.get()).toBe(value);
|
||||||
});
|
});
|
||||||
|
@ -34,22 +32,37 @@ describe('StructFieldValue', () => {
|
||||||
public getSize(): number {
|
public getSize(): number {
|
||||||
return 42;
|
return 42;
|
||||||
}
|
}
|
||||||
public create(options: Readonly<StructOptions>, context: StructSerializationContext, struct: StructValue, value: unknown): StructFieldValue<this> {
|
public create(options: Readonly<StructOptions>, struct: StructValue, value: unknown): StructFieldValue<this> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
public deserialize(options: Readonly<StructOptions>, context: StructDeserializationContext, struct: StructValue): ValueOrPromise<StructFieldValue<this>> {
|
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): StructFieldValue<this>;
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): Promise<StructFieldValue<this>>;
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): ValueOrPromise<StructFieldValue<this>> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockStructFieldValue extends StructFieldValue {
|
class MockStructFieldValue extends StructFieldValue {
|
||||||
public serialize(dataView: DataView, offset: number, context: StructSerializationContext): void {
|
public serialize(dataView: DataView, offset: number): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldDefinition = new MockFieldDefinition();
|
const fieldDefinition = new MockFieldDefinition();
|
||||||
const fieldValue = new MockStructFieldValue(fieldDefinition, undefined as any, undefined as any, undefined as any, undefined as any);
|
const fieldValue = new MockStructFieldValue(fieldDefinition, undefined as any, undefined as any, undefined as any);
|
||||||
expect(fieldValue.getSize()).toBe(42);
|
expect(fieldValue.getSize()).toBe(42);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -57,12 +70,12 @@ describe('StructFieldValue', () => {
|
||||||
describe('#set', () => {
|
describe('#set', () => {
|
||||||
it('should update its internal value', () => {
|
it('should update its internal value', () => {
|
||||||
class MockStructFieldValue extends StructFieldValue {
|
class MockStructFieldValue extends StructFieldValue {
|
||||||
public serialize(dataView: DataView, offset: number, context: StructSerializationContext): void {
|
public serialize(dataView: DataView, offset: number): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldValue = new MockStructFieldValue(undefined as any, undefined as any, undefined as any, undefined as any, undefined as any);
|
const fieldValue = new MockStructFieldValue(undefined as any, undefined as any, undefined as any, undefined as any);
|
||||||
fieldValue.set(1);
|
fieldValue.set(1);
|
||||||
expect(fieldValue.get()).toBe(1);
|
expect(fieldValue.get()).toBe(1);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { StructOptions, StructSerializationContext } from './context';
|
import type { StructOptions } from './context';
|
||||||
import type { StructFieldDefinition } from './definition';
|
import type { StructFieldDefinition } from './definition';
|
||||||
import type { StructValue } from './struct-value';
|
import type { StructValue } from './struct-value';
|
||||||
|
|
||||||
|
@ -17,9 +17,6 @@ export abstract class StructFieldValue<
|
||||||
/** Gets the options of the associated `Struct` */
|
/** Gets the options of the associated `Struct` */
|
||||||
public readonly options: Readonly<StructOptions>;
|
public readonly options: Readonly<StructOptions>;
|
||||||
|
|
||||||
/** Gets the serialization context of the associated `Struct` instance */
|
|
||||||
public readonly context: StructSerializationContext;
|
|
||||||
|
|
||||||
/** Gets the associated `Struct` instance */
|
/** Gets the associated `Struct` instance */
|
||||||
public readonly struct: StructValue;
|
public readonly struct: StructValue;
|
||||||
|
|
||||||
|
@ -28,13 +25,11 @@ export abstract class StructFieldValue<
|
||||||
public constructor(
|
public constructor(
|
||||||
definition: TDefinition,
|
definition: TDefinition,
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructSerializationContext,
|
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
value: TDefinition['TValue'],
|
value: TDefinition['TValue'],
|
||||||
) {
|
) {
|
||||||
this.definition = definition;
|
this.definition = definition;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.context = context;
|
|
||||||
this.struct = struct;
|
this.struct = struct;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +63,5 @@ export abstract class StructFieldValue<
|
||||||
public abstract serialize(
|
public abstract serialize(
|
||||||
dataView: DataView,
|
dataView: DataView,
|
||||||
offset: number,
|
offset: number,
|
||||||
context: StructSerializationContext
|
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
import { StructDefaultOptions, StructDeserializationContext, StructFieldDefinition, StructFieldValue, StructOptions, StructSerializationContext, StructValue } from './basic';
|
import { StructAsyncDeserializeStream, StructDefaultOptions, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from './basic';
|
||||||
import { Struct } from './struct';
|
import { Struct } from './struct';
|
||||||
import { ArrayBufferFieldType, FixedLengthArrayBufferLikeFieldDefinition, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition } from './types';
|
import { ArrayBufferFieldType, FixedLengthArrayBufferLikeFieldDefinition, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition } from './types';
|
||||||
import { ValueOrPromise } from './utils';
|
import { ValueOrPromise } from './utils';
|
||||||
|
|
||||||
class MockDeserializationContext implements StructDeserializationContext {
|
class MockDeserializationStream implements StructDeserializeStream {
|
||||||
public buffer = new ArrayBuffer(0);
|
public buffer = new ArrayBuffer(0);
|
||||||
|
|
||||||
public read = jest.fn((length: number) => this.buffer);
|
public read = jest.fn((length: number) => this.buffer);
|
||||||
|
|
||||||
public encodeUtf8 = jest.fn((input: string) => Buffer.from(input, 'utf-8'));
|
|
||||||
|
|
||||||
public decodeUtf8 = jest.fn((buffer: ArrayBuffer) => Buffer.from(buffer).toString('utf-8'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Struct', () => {
|
describe('Struct', () => {
|
||||||
|
@ -32,10 +28,24 @@ describe('Struct', () => {
|
||||||
return this.options;
|
return this.options;
|
||||||
});
|
});
|
||||||
|
|
||||||
public create(options: Readonly<StructOptions>, context: StructSerializationContext, struct: StructValue, value: unknown): StructFieldValue<this> {
|
public create(options: Readonly<StructOptions>, struct: StructValue, value: unknown): StructFieldValue<this> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
public deserialize(options: Readonly<StructOptions>, context: StructDeserializationContext, struct: StructValue): ValueOrPromise<StructFieldValue<this>> {
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): StructFieldValue<this>;
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): Promise<StructFieldValue<this>>;
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue
|
||||||
|
): ValueOrPromise<StructFieldValue<this>> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,17 +275,17 @@ describe('Struct', () => {
|
||||||
.int8('foo')
|
.int8('foo')
|
||||||
.int16('bar');
|
.int16('bar');
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
context.read
|
stream.read
|
||||||
.mockReturnValueOnce(new Uint8Array([2]).buffer)
|
.mockReturnValueOnce(new Uint8Array([2]).buffer)
|
||||||
.mockReturnValueOnce(new Uint8Array([0, 16]).buffer);
|
.mockReturnValueOnce(new Uint8Array([0, 16]).buffer);
|
||||||
|
|
||||||
const result = await struct.deserialize(context);
|
const result = await struct.deserialize(stream);
|
||||||
expect(result).toEqual({ foo: 2, bar: 16 });
|
expect(result).toEqual({ foo: 2, bar: 16 });
|
||||||
|
|
||||||
expect(context.read).toBeCalledTimes(2);
|
expect(stream.read).toBeCalledTimes(2);
|
||||||
expect(context.read).nthCalledWith(1, 1);
|
expect(stream.read).nthCalledWith(1, 1);
|
||||||
expect(context.read).nthCalledWith(2, 2);
|
expect(stream.read).nthCalledWith(2, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should deserialize with dynamic size fields', async () => {
|
it('should deserialize with dynamic size fields', async () => {
|
||||||
|
@ -283,16 +293,16 @@ describe('Struct', () => {
|
||||||
.int8('fooLength')
|
.int8('fooLength')
|
||||||
.uint8ClampedArray('foo', { lengthField: 'fooLength' });
|
.uint8ClampedArray('foo', { lengthField: 'fooLength' });
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
context.read
|
stream.read
|
||||||
.mockReturnValueOnce(new Uint8Array([2]).buffer)
|
.mockReturnValueOnce(new Uint8Array([2]).buffer)
|
||||||
.mockReturnValueOnce(new Uint8Array([3, 4]).buffer);
|
.mockReturnValueOnce(new Uint8Array([3, 4]).buffer);
|
||||||
|
|
||||||
const result = await struct.deserialize(context);
|
const result = await struct.deserialize(stream);
|
||||||
expect(result).toEqual({ fooLength: 2, foo: new Uint8ClampedArray([3, 4]) });
|
expect(result).toEqual({ fooLength: 2, foo: new Uint8ClampedArray([3, 4]) });
|
||||||
expect(context.read).toBeCalledTimes(2);
|
expect(stream.read).toBeCalledTimes(2);
|
||||||
expect(context.read).nthCalledWith(1, 1);
|
expect(stream.read).nthCalledWith(1, 1);
|
||||||
expect(context.read).nthCalledWith(2, 2);
|
expect(stream.read).nthCalledWith(2, 2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -301,8 +311,8 @@ describe('Struct', () => {
|
||||||
const struct = new Struct()
|
const struct = new Struct()
|
||||||
.extra({ foo: 42, bar: true });
|
.extra({ foo: 42, bar: true });
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
const result = await struct.deserialize(context);
|
const result = await struct.deserialize(stream);
|
||||||
|
|
||||||
expect(Object.entries(Object.getOwnPropertyDescriptors(result))).toEqual([
|
expect(Object.entries(Object.getOwnPropertyDescriptors(result))).toEqual([
|
||||||
['foo', { configurable: true, enumerable: true, writable: true, value: 42 }],
|
['foo', { configurable: true, enumerable: true, writable: true, value: 42 }],
|
||||||
|
@ -318,8 +328,8 @@ describe('Struct', () => {
|
||||||
set bar(value) { },
|
set bar(value) { },
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
const result = await struct.deserialize(context);
|
const result = await struct.deserialize(stream);
|
||||||
|
|
||||||
expect(Object.entries(Object.getOwnPropertyDescriptors(result))).toEqual([
|
expect(Object.entries(Object.getOwnPropertyDescriptors(result))).toEqual([
|
||||||
['foo', { configurable: true, enumerable: true, get: expect.any(Function) }],
|
['foo', { configurable: true, enumerable: true, get: expect.any(Function) }],
|
||||||
|
@ -334,8 +344,8 @@ describe('Struct', () => {
|
||||||
const callback = jest.fn(() => { throw new Error('mock'); });
|
const callback = jest.fn(() => { throw new Error('mock'); });
|
||||||
struct.postDeserialize(callback);
|
struct.postDeserialize(callback);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
expect(struct.deserialize(context)).rejects.toThrowError('mock');
|
expect(struct.deserialize(stream)).rejects.toThrowError('mock');
|
||||||
expect(callback).toBeCalledTimes(1);
|
expect(callback).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -344,8 +354,8 @@ describe('Struct', () => {
|
||||||
const callback = jest.fn(() => 'mock');
|
const callback = jest.fn(() => 'mock');
|
||||||
struct.postDeserialize(callback);
|
struct.postDeserialize(callback);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
expect(struct.deserialize(context)).resolves.toBe('mock');
|
expect(struct.deserialize(stream)).resolves.toBe('mock');
|
||||||
expect(callback).toBeCalledTimes(1);
|
expect(callback).toBeCalledTimes(1);
|
||||||
expect(callback).toBeCalledWith({});
|
expect(callback).toBeCalledWith({});
|
||||||
});
|
});
|
||||||
|
@ -355,8 +365,8 @@ describe('Struct', () => {
|
||||||
const callback = jest.fn();
|
const callback = jest.fn();
|
||||||
struct.postDeserialize(callback);
|
struct.postDeserialize(callback);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
const result = await struct.deserialize(context);
|
const result = await struct.deserialize(stream);
|
||||||
|
|
||||||
expect(callback).toBeCalledTimes(1);
|
expect(callback).toBeCalledTimes(1);
|
||||||
expect(callback).toBeCalledWith(result);
|
expect(callback).toBeCalledWith(result);
|
||||||
|
@ -371,8 +381,8 @@ describe('Struct', () => {
|
||||||
const callback2 = jest.fn();
|
const callback2 = jest.fn();
|
||||||
struct.postDeserialize(callback2);
|
struct.postDeserialize(callback2);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
await struct.deserialize(context);
|
await struct.deserialize(stream);
|
||||||
|
|
||||||
expect(callback1).toBeCalledTimes(0);
|
expect(callback1).toBeCalledTimes(0);
|
||||||
expect(callback2).toBeCalledTimes(1);
|
expect(callback2).toBeCalledTimes(1);
|
||||||
|
@ -386,8 +396,8 @@ describe('Struct', () => {
|
||||||
.int8('foo')
|
.int8('foo')
|
||||||
.int16('bar');
|
.int16('bar');
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
const result = new Uint8Array(struct.serialize({ foo: 0x42, bar: 0x1024 }, context));
|
const result = new Uint8Array(struct.serialize({ foo: 0x42, bar: 0x1024 }));
|
||||||
|
|
||||||
expect(result).toEqual(new Uint8Array([0x42, 0x10, 0x24]));
|
expect(result).toEqual(new Uint8Array([0x42, 0x10, 0x24]));
|
||||||
});
|
});
|
||||||
|
@ -397,8 +407,8 @@ describe('Struct', () => {
|
||||||
.int8('fooLength')
|
.int8('fooLength')
|
||||||
.arrayBuffer('foo', { lengthField: 'fooLength' });
|
.arrayBuffer('foo', { lengthField: 'fooLength' });
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const stream = new MockDeserializationStream();
|
||||||
const result = new Uint8Array(struct.serialize({ foo: new Uint8Array([0x03, 0x04, 0x05]).buffer }, context));
|
const result = new Uint8Array(struct.serialize({ foo: new Uint8Array([0x03, 0x04, 0x05]).buffer }));
|
||||||
|
|
||||||
expect(result).toEqual(new Uint8Array([0x03, 0x03, 0x04, 0x05]));
|
expect(result).toEqual(new Uint8Array([0x03, 0x03, 0x04, 0x05]));
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { StructDefaultOptions, StructDeserializationContext, StructFieldDefinition, StructFieldValue, StructOptions, StructSerializationContext, StructValue } from './basic';
|
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, FixedLengthArrayBufferLikeFieldDefinition, FixedLengthArrayBufferLikeFieldOptions, LengthField, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldOptions } from './types';
|
||||||
import { Awaited, Evaluate, Identity, Overwrite } from './utils';
|
import { Awaited, Evaluate, Identity, Overwrite, ValueOrPromise } from "./utils";
|
||||||
|
|
||||||
export interface StructLike<TValue> {
|
export interface StructLike<TValue> {
|
||||||
deserialize(context: StructDeserializationContext): Promise<TValue>;
|
deserialize(stream: StructDeserializeStream | StructAsyncDeserializeStream): Promise<TValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,7 +55,7 @@ interface ArrayBufferLikeFieldCreator<
|
||||||
*/
|
*/
|
||||||
<
|
<
|
||||||
TName extends PropertyKey,
|
TName extends PropertyKey,
|
||||||
TType extends ArrayBufferLikeFieldType,
|
TType extends ArrayBufferLikeFieldType<any, any>,
|
||||||
TTypeScriptType = TType['TTypeScriptType'],
|
TTypeScriptType = TType['TTypeScriptType'],
|
||||||
>(
|
>(
|
||||||
name: TName,
|
name: TName,
|
||||||
|
@ -77,7 +79,7 @@ interface ArrayBufferLikeFieldCreator<
|
||||||
*/
|
*/
|
||||||
<
|
<
|
||||||
TName extends PropertyKey,
|
TName extends PropertyKey,
|
||||||
TType extends ArrayBufferLikeFieldType,
|
TType extends ArrayBufferLikeFieldType<any, any>,
|
||||||
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TFields>,
|
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TFields>,
|
||||||
TTypeScriptType = TType['TTypeScriptType'],
|
TTypeScriptType = TType['TTypeScriptType'],
|
||||||
>(
|
>(
|
||||||
|
@ -106,7 +108,7 @@ interface BindedArrayBufferLikeFieldDefinitionCreator<
|
||||||
TOmitInitKey extends PropertyKey,
|
TOmitInitKey extends PropertyKey,
|
||||||
TExtra extends object,
|
TExtra extends object,
|
||||||
TPostDeserialized,
|
TPostDeserialized,
|
||||||
TType extends ArrayBufferLikeFieldType
|
TType extends ArrayBufferLikeFieldType<any, any>
|
||||||
> {
|
> {
|
||||||
<
|
<
|
||||||
TName extends PropertyKey,
|
TName extends PropertyKey,
|
||||||
|
@ -520,17 +522,36 @@ export class Struct<
|
||||||
return this as any;
|
return this as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deserialize(
|
public deserialize(
|
||||||
context: StructDeserializationContext
|
stream: StructDeserializeStream,
|
||||||
): Promise<StructDeserializedResult<TFields, TExtra, TPostDeserialized>> {
|
): StructDeserializedResult<TFields, TExtra, TPostDeserialized>;
|
||||||
|
public deserialize(
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
|
): Promise<StructDeserializedResult<TFields, TExtra, TPostDeserialized>>;
|
||||||
|
public deserialize(
|
||||||
|
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
||||||
|
): ValueOrPromise<StructDeserializedResult<TFields, TExtra, TPostDeserialized>> {
|
||||||
const value = new StructValue();
|
const value = new StructValue();
|
||||||
Object.defineProperties(value.value, this._extra);
|
Object.defineProperties(value.value, this._extra);
|
||||||
|
|
||||||
for (const [name, definition] of this._fields) {
|
return Syncbird.try(() => {
|
||||||
const fieldValue = await definition.deserialize(this.options, context, value);
|
const iterator = this._fields[Symbol.iterator]();
|
||||||
value.set(name, fieldValue);
|
const iterate: () => StructValue | Syncbird<StructValue> = () => {
|
||||||
|
const result = iterator.next();
|
||||||
|
if (result.done) {
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [name, definition] = result.value;
|
||||||
|
return Syncbird.resolve(
|
||||||
|
definition.deserialize(this.options, stream as any, value)
|
||||||
|
).then(fieldValue => {
|
||||||
|
value.set(name, fieldValue);
|
||||||
|
return iterate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return iterate();
|
||||||
|
}).then(value => {
|
||||||
if (this._postDeserialized) {
|
if (this._postDeserialized) {
|
||||||
const object = value.value as TFields;
|
const object = value.value as TFields;
|
||||||
const result = this._postDeserialized.call(object, object);
|
const result = this._postDeserialized.call(object, object);
|
||||||
|
@ -539,14 +560,15 @@ export class Struct<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.value as any;
|
return value.value;
|
||||||
|
}).valueOrPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
public serialize(init: Evaluate<Omit<TFields, TOmitInitKey>>, context: StructSerializationContext): ArrayBuffer {
|
public serialize(init: Evaluate<Omit<TFields, TOmitInitKey>>): ArrayBuffer {
|
||||||
const value = new StructValue();
|
const value = new StructValue();
|
||||||
|
|
||||||
for (const [name, definition] of this._fields) {
|
for (const [name, definition] of this._fields) {
|
||||||
const fieldValue = definition.create(this.options, context, value, (init as any)[name]);
|
const fieldValue = definition.create(this.options, value, (init as any)[name]);
|
||||||
value.set(name, fieldValue);
|
value.set(name, fieldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,7 +586,7 @@ export class Struct<
|
||||||
const dataView = new DataView(buffer);
|
const dataView = new DataView(buffer);
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
for (const { fieldValue, size } of fieldsInfo) {
|
for (const { fieldValue, size } of fieldsInfo) {
|
||||||
fieldValue.serialize(dataView, offset, context);
|
fieldValue.serialize(dataView, offset);
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
libraries/struct/src/syncbird.ts
Normal file
40
libraries/struct/src/syncbird.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import Bluebird from 'bluebird';
|
||||||
|
|
||||||
|
export type Resolvable<R> = R | PromiseLike<R>;
|
||||||
|
|
||||||
|
export class Syncbird<T> extends Bluebird<T> implements PromiseLike<T> {
|
||||||
|
public static resolve(): Syncbird<void>;
|
||||||
|
public static resolve<R>(value: Resolvable<R>): Syncbird<R>;
|
||||||
|
public static resolve<R>(value?: Resolvable<R>): Syncbird<R> {
|
||||||
|
return new Syncbird(
|
||||||
|
resolve => resolve(value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static try<R>(fn: () => Resolvable<R>): Syncbird<R> {
|
||||||
|
return Syncbird.resolve(fn());
|
||||||
|
}
|
||||||
|
|
||||||
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
|
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
||||||
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
|
||||||
|
): Syncbird<TResult1 | TResult2> {
|
||||||
|
if (this.isFulfilled()) {
|
||||||
|
if (!onfulfilled) {
|
||||||
|
return this as unknown as Syncbird<TResult1>;
|
||||||
|
} else {
|
||||||
|
return Syncbird.resolve(onfulfilled(this.value()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Syncbird.resolve(super.then(onfulfilled, onrejected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public valueOrPromise(): T | Promise<T> {
|
||||||
|
if (this.isFulfilled()) {
|
||||||
|
return this.value();
|
||||||
|
} else {
|
||||||
|
return this as Promise<T>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,10 @@
|
||||||
import { StructDefaultOptions, StructDeserializationContext, StructValue } from '../basic';
|
import { StructDefaultOptions, StructDeserializeStream, StructValue } from '../basic';
|
||||||
import { ArrayBufferFieldType, ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType, StringFieldType, Uint8ClampedArrayFieldType } from './array-buffer';
|
import { ArrayBufferFieldType, ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType, StringFieldType, Uint8ClampedArrayFieldType } from './array-buffer';
|
||||||
|
|
||||||
class MockDeserializationContext implements StructDeserializationContext {
|
class MockDeserializationStream implements StructDeserializeStream {
|
||||||
public buffer = new ArrayBuffer(0);
|
public buffer = new ArrayBuffer(0);
|
||||||
|
|
||||||
public read = jest.fn((length: number) => this.buffer);
|
public read = jest.fn((length: number) => this.buffer);
|
||||||
|
|
||||||
public encodeUtf8 = jest.fn((input: string) => Buffer.from(input, 'utf-8'));
|
|
||||||
|
|
||||||
public decodeUtf8 = jest.fn((buffer: ArrayBuffer) => Buffer.from(buffer).toString('utf-8'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Types', () => {
|
describe('Types', () => {
|
||||||
|
@ -67,27 +63,13 @@ describe('Types', () => {
|
||||||
it('`#toArrayBuffer` should return the decoded string', () => {
|
it('`#toArrayBuffer` should return the decoded string', () => {
|
||||||
const text = 'foo';
|
const text = 'foo';
|
||||||
const arrayBuffer = Buffer.from(text, 'utf-8');
|
const arrayBuffer = Buffer.from(text, 'utf-8');
|
||||||
const context = new MockDeserializationContext();
|
expect(StringFieldType.instance.toArrayBuffer(text)).toEqual(arrayBuffer);
|
||||||
expect(StringFieldType.instance.toArrayBuffer(text, context)).toEqual(arrayBuffer);
|
|
||||||
expect(context.encodeUtf8).toBeCalledTimes(1);
|
|
||||||
expect(context.encodeUtf8).toBeCalledWith(text);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('`#fromArrayBuffer` should return the encoded ArrayBuffer', () => {
|
it('`#fromArrayBuffer` should return the encoded ArrayBuffer', () => {
|
||||||
const text = 'foo';
|
const text = 'foo';
|
||||||
const arrayBuffer = Buffer.from(text, 'utf-8');
|
const arrayBuffer = Buffer.from(text, 'utf-8');
|
||||||
const context: StructDeserializationContext = {
|
expect(StringFieldType.instance.fromArrayBuffer(arrayBuffer)).toBe(text);
|
||||||
decodeUtf8(arrayBuffer: ArrayBuffer): string {
|
|
||||||
return Buffer.from(arrayBuffer).toString('utf-8');
|
|
||||||
},
|
|
||||||
encodeUtf8(input) {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
},
|
|
||||||
read(length) {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
},
|
|
||||||
};
|
|
||||||
expect(StringFieldType.instance.fromArrayBuffer(arrayBuffer, context)).toBe(text);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('`#getSize` should return -1', () => {
|
it('`#getSize` should return -1', () => {
|
||||||
|
@ -107,7 +89,7 @@ describe('Types', () => {
|
||||||
const size = 10;
|
const size = 10;
|
||||||
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const context = new MockDeserializationStream();
|
||||||
const buffer = new ArrayBuffer(size);
|
const buffer = new ArrayBuffer(size);
|
||||||
context.buffer = buffer;
|
context.buffer = buffer;
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
|
@ -124,7 +106,7 @@ describe('Types', () => {
|
||||||
const size = 10;
|
const size = 10;
|
||||||
const definition = new MockArrayBufferFieldDefinition(Uint8ClampedArrayFieldType.instance, size);
|
const definition = new MockArrayBufferFieldDefinition(Uint8ClampedArrayFieldType.instance, size);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const context = new MockDeserializationStream();
|
||||||
const buffer = new ArrayBuffer(size);
|
const buffer = new ArrayBuffer(size);
|
||||||
context.buffer = buffer;
|
context.buffer = buffer;
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
|
@ -143,7 +125,7 @@ describe('Types', () => {
|
||||||
const size = 0;
|
const size = 0;
|
||||||
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const context = new MockDeserializationStream();
|
||||||
const buffer = new ArrayBuffer(size);
|
const buffer = new ArrayBuffer(size);
|
||||||
context.buffer = buffer;
|
context.buffer = buffer;
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
|
@ -165,7 +147,7 @@ describe('Types', () => {
|
||||||
const size = 0;
|
const size = 0;
|
||||||
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const context = new MockDeserializationStream();
|
||||||
const buffer = new ArrayBuffer(size);
|
const buffer = new ArrayBuffer(size);
|
||||||
context.buffer = buffer;
|
context.buffer = buffer;
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
|
@ -184,7 +166,7 @@ describe('Types', () => {
|
||||||
const size = 0;
|
const size = 0;
|
||||||
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const context = new MockDeserializationStream();
|
||||||
const sourceArray = new Uint8Array(Array.from({ length: size }, (_, i) => i));
|
const sourceArray = new Uint8Array(Array.from({ length: size }, (_, i) => i));
|
||||||
const buffer = sourceArray.buffer;
|
const buffer = sourceArray.buffer;
|
||||||
context.buffer = buffer;
|
context.buffer = buffer;
|
||||||
|
@ -194,7 +176,7 @@ describe('Types', () => {
|
||||||
|
|
||||||
const targetArray = new Uint8Array(size);
|
const targetArray = new Uint8Array(size);
|
||||||
const targetView = new DataView(targetArray.buffer);
|
const targetView = new DataView(targetArray.buffer);
|
||||||
fieldValue.serialize(targetView, 0, context);
|
fieldValue.serialize(targetView, 0);
|
||||||
|
|
||||||
expect(targetArray).toEqual(sourceArray);
|
expect(targetArray).toEqual(sourceArray);
|
||||||
});
|
});
|
||||||
|
@ -203,7 +185,7 @@ describe('Types', () => {
|
||||||
const size = 0;
|
const size = 0;
|
||||||
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
const context = new MockDeserializationStream();
|
||||||
const sourceArray = new Uint8Array(Array.from({ length: size }, (_, i) => i));
|
const sourceArray = new Uint8Array(Array.from({ length: size }, (_, i) => i));
|
||||||
const buffer = sourceArray.buffer;
|
const buffer = sourceArray.buffer;
|
||||||
context.buffer = buffer;
|
context.buffer = buffer;
|
||||||
|
@ -215,7 +197,7 @@ describe('Types', () => {
|
||||||
|
|
||||||
const targetArray = new Uint8Array(size);
|
const targetArray = new Uint8Array(size);
|
||||||
const targetView = new DataView(targetArray.buffer);
|
const targetView = new DataView(targetArray.buffer);
|
||||||
fieldValue.serialize(targetView, 0, context);
|
fieldValue.serialize(targetView, 0);
|
||||||
|
|
||||||
expect(targetArray).toEqual(sourceArray);
|
expect(targetArray).toEqual(sourceArray);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { StructDeserializationContext, StructFieldDefinition, StructFieldValue, StructOptions, StructSerializationContext, StructValue } from '../basic';
|
import { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from '../basic';
|
||||||
|
import { Syncbird } from "../syncbird";
|
||||||
|
import { decodeUtf8, encodeUtf8, ValueOrPromise } from "../utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all types that
|
* Base class for all types that
|
||||||
|
@ -18,10 +20,10 @@ export abstract class ArrayBufferLikeFieldType<TValue = unknown, TTypeScriptType
|
||||||
* This function should be "pure", i.e.,
|
* This function should be "pure", i.e.,
|
||||||
* same `value` should always be converted to `ArrayBuffer`s that have same content.
|
* same `value` should always be converted to `ArrayBuffer`s that have same content.
|
||||||
*/
|
*/
|
||||||
public abstract toArrayBuffer(value: TValue, context: StructSerializationContext): ArrayBuffer;
|
public abstract toArrayBuffer(value: TValue): ArrayBuffer;
|
||||||
|
|
||||||
/** When implemented in derived classes, converts the `ArrayBuffer` to a type-specific value */
|
/** When implemented in derived classes, converts the `ArrayBuffer` to a type-specific value */
|
||||||
public abstract fromArrayBuffer(arrayBuffer: ArrayBuffer, context: StructDeserializationContext): TValue;
|
public abstract fromArrayBuffer(arrayBuffer: ArrayBuffer): TValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When implemented in derived classes, gets the size in byte of the type-specific `value`.
|
* When implemented in derived classes, gets the size in byte of the type-specific `value`.
|
||||||
|
@ -76,17 +78,17 @@ export class Uint8ClampedArrayFieldType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Am ArrayBufferLike type that converts between `ArrayBuffer` and `string` */
|
/** An ArrayBufferLike type that converts between `ArrayBuffer` and `string` */
|
||||||
export class StringFieldType<TTypeScriptType = string>
|
export class StringFieldType<TTypeScriptType = string>
|
||||||
extends ArrayBufferLikeFieldType<string, TTypeScriptType> {
|
extends ArrayBufferLikeFieldType<string, TTypeScriptType> {
|
||||||
public static readonly instance = new StringFieldType();
|
public static readonly instance = new StringFieldType();
|
||||||
|
|
||||||
public toArrayBuffer(value: string, context: StructSerializationContext): ArrayBuffer {
|
public toArrayBuffer(value: string): ArrayBuffer {
|
||||||
return context.encodeUtf8(value);
|
return encodeUtf8(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fromArrayBuffer(arrayBuffer: ArrayBuffer, context: StructDeserializationContext): string {
|
public fromArrayBuffer(arrayBuffer: ArrayBuffer): string {
|
||||||
return context.decodeUtf8(arrayBuffer);
|
return decodeUtf8(arrayBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSize(): number {
|
public getSize(): number {
|
||||||
|
@ -100,7 +102,7 @@ export class StringFieldType<TTypeScriptType = string>
|
||||||
const EmptyArrayBuffer = new ArrayBuffer(0);
|
const EmptyArrayBuffer = new ArrayBuffer(0);
|
||||||
|
|
||||||
export abstract class ArrayBufferLikeFieldDefinition<
|
export abstract class ArrayBufferLikeFieldDefinition<
|
||||||
TType extends ArrayBufferLikeFieldType = ArrayBufferLikeFieldType,
|
TType extends ArrayBufferLikeFieldType<any, any> = ArrayBufferLikeFieldType<unknown, unknown>,
|
||||||
TOptions = void,
|
TOptions = void,
|
||||||
TOmitInitKey extends PropertyKey = never,
|
TOmitInitKey extends PropertyKey = never,
|
||||||
> extends StructFieldDefinition<
|
> extends StructFieldDefinition<
|
||||||
|
@ -124,47 +126,55 @@ export abstract class ArrayBufferLikeFieldDefinition<
|
||||||
*/
|
*/
|
||||||
public create(
|
public create(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructSerializationContext,
|
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
value: TType['TTypeScriptType'],
|
value: TType['TTypeScriptType'],
|
||||||
arrayBuffer?: ArrayBuffer,
|
arrayBuffer?: ArrayBuffer,
|
||||||
): ArrayBufferLikeFieldValue<this> {
|
): ArrayBufferLikeFieldValue<this> {
|
||||||
return new ArrayBufferLikeFieldValue(this, options, context, struct, value, arrayBuffer);
|
return new ArrayBufferLikeFieldValue(this, options, struct, value, arrayBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deserialize(
|
public override deserialize(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructDeserializationContext,
|
stream: StructDeserializeStream,
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
): Promise<ArrayBufferLikeFieldValue<this>> {
|
): ArrayBufferLikeFieldValue<this>;
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): Promise<ArrayBufferLikeFieldValue<this>>;
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): ValueOrPromise<ArrayBufferLikeFieldValue<this>> {
|
||||||
|
return Syncbird.resolve().then(() => {
|
||||||
const size = this.getDeserializeSize(struct);
|
const size = this.getDeserializeSize(struct);
|
||||||
|
|
||||||
let arrayBuffer: ArrayBuffer;
|
|
||||||
if (size === 0) {
|
if (size === 0) {
|
||||||
arrayBuffer = EmptyArrayBuffer;
|
return EmptyArrayBuffer;
|
||||||
} else {
|
} else {
|
||||||
arrayBuffer = await context.read(size);
|
return stream.read(size);
|
||||||
}
|
}
|
||||||
|
}).then(arrayBuffer => {
|
||||||
const value = this.type.fromArrayBuffer(arrayBuffer, context);
|
const value = this.type.fromArrayBuffer(arrayBuffer);
|
||||||
return this.create(options, context, struct, value, arrayBuffer);
|
return this.create(options, struct, value, arrayBuffer);
|
||||||
|
}).valueOrPromise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ArrayBufferLikeFieldValue<
|
export class ArrayBufferLikeFieldValue<
|
||||||
TDefinition extends ArrayBufferLikeFieldDefinition<ArrayBufferLikeFieldType, any, any>,
|
TDefinition extends ArrayBufferLikeFieldDefinition<ArrayBufferLikeFieldType<unknown, unknown>, any, any>,
|
||||||
> extends StructFieldValue<TDefinition> {
|
> extends StructFieldValue<TDefinition> {
|
||||||
protected arrayBuffer: ArrayBuffer | undefined;
|
protected arrayBuffer: ArrayBuffer | undefined;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
definition: TDefinition,
|
definition: TDefinition,
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructSerializationContext,
|
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
value: TDefinition['TValue'],
|
value: TDefinition['TValue'],
|
||||||
arrayBuffer?: ArrayBuffer,
|
arrayBuffer?: ArrayBuffer,
|
||||||
) {
|
) {
|
||||||
super(definition, options, context, struct, value);
|
super(definition, options, struct, value);
|
||||||
this.arrayBuffer = arrayBuffer;
|
this.arrayBuffer = arrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,9 +183,9 @@ export class ArrayBufferLikeFieldValue<
|
||||||
this.arrayBuffer = undefined;
|
this.arrayBuffer = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public serialize(dataView: DataView, offset: number, context: StructSerializationContext): void {
|
public serialize(dataView: DataView, offset: number): void {
|
||||||
if (!this.arrayBuffer) {
|
if (!this.arrayBuffer) {
|
||||||
this.arrayBuffer = this.definition.type.toArrayBuffer(this.value, context);
|
this.arrayBuffer = this.definition.type.toArrayBuffer(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
new Uint8Array(dataView.buffer)
|
new Uint8Array(dataView.buffer)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { StructDefaultOptions, StructDeserializationContext, StructSerializationContext, StructValue } from '../basic';
|
import { StructDefaultOptions, StructDeserializeStream, StructValue } from '../basic';
|
||||||
import { NumberFieldDefinition, NumberFieldType } from './number';
|
import { NumberFieldDefinition, NumberFieldType } from './number';
|
||||||
|
|
||||||
describe('Types', () => {
|
describe('Types', () => {
|
||||||
|
@ -78,17 +78,13 @@ describe('Types', () => {
|
||||||
describe('#deserialize', () => {
|
describe('#deserialize', () => {
|
||||||
it('should deserialize Uint8', async () => {
|
it('should deserialize Uint8', async () => {
|
||||||
const read = jest.fn((length: number) => new Uint8Array([1, 2, 3, 4]).buffer);
|
const read = jest.fn((length: number) => new Uint8Array([1, 2, 3, 4]).buffer);
|
||||||
const context: StructDeserializationContext = {
|
const stream: StructDeserializeStream = { read };
|
||||||
read,
|
|
||||||
decodeUtf8(buffer) { throw new Error(''); },
|
|
||||||
encodeUtf8(input) { throw new Error(''); },
|
|
||||||
};
|
|
||||||
|
|
||||||
const definition = new NumberFieldDefinition(NumberFieldType.Uint8);
|
const definition = new NumberFieldDefinition(NumberFieldType.Uint8);
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
const value = await definition.deserialize(
|
const value = await definition.deserialize(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
stream,
|
||||||
struct,
|
struct,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -99,17 +95,13 @@ describe('Types', () => {
|
||||||
|
|
||||||
it('should deserialize Uint16', async () => {
|
it('should deserialize Uint16', async () => {
|
||||||
const read = jest.fn((length: number) => new Uint8Array([1, 2, 3, 4]).buffer);
|
const read = jest.fn((length: number) => new Uint8Array([1, 2, 3, 4]).buffer);
|
||||||
const context: StructDeserializationContext = {
|
const stream: StructDeserializeStream = { read };
|
||||||
read,
|
|
||||||
decodeUtf8(buffer) { throw new Error(''); },
|
|
||||||
encodeUtf8(input) { throw new Error(''); },
|
|
||||||
};
|
|
||||||
|
|
||||||
const definition = new NumberFieldDefinition(NumberFieldType.Uint16);
|
const definition = new NumberFieldDefinition(NumberFieldType.Uint16);
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
const value = await definition.deserialize(
|
const value = await definition.deserialize(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
stream,
|
||||||
struct,
|
struct,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -120,17 +112,13 @@ describe('Types', () => {
|
||||||
|
|
||||||
it('should deserialize Uint16LE', async () => {
|
it('should deserialize Uint16LE', async () => {
|
||||||
const read = jest.fn((length: number) => new Uint8Array([1, 2, 3, 4]).buffer);
|
const read = jest.fn((length: number) => new Uint8Array([1, 2, 3, 4]).buffer);
|
||||||
const context: StructDeserializationContext = {
|
const stream: StructDeserializeStream = { read };
|
||||||
read,
|
|
||||||
decodeUtf8(buffer) { throw new Error(''); },
|
|
||||||
encodeUtf8(input) { throw new Error(''); },
|
|
||||||
};
|
|
||||||
|
|
||||||
const definition = new NumberFieldDefinition(NumberFieldType.Uint16);
|
const definition = new NumberFieldDefinition(NumberFieldType.Uint16);
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
const value = await definition.deserialize(
|
const value = await definition.deserialize(
|
||||||
{ ...StructDefaultOptions, littleEndian: true },
|
{ ...StructDefaultOptions, littleEndian: true },
|
||||||
context,
|
stream,
|
||||||
struct,
|
struct,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -144,16 +132,12 @@ describe('Types', () => {
|
||||||
describe('NumberFieldValue', () => {
|
describe('NumberFieldValue', () => {
|
||||||
describe('#getSize', () => {
|
describe('#getSize', () => {
|
||||||
it('should return size of its definition', () => {
|
it('should return size of its definition', () => {
|
||||||
const context: StructSerializationContext = {
|
|
||||||
encodeUtf8(input) { throw new Error(''); },
|
|
||||||
};
|
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
new NumberFieldDefinition(NumberFieldType.Int8)
|
new NumberFieldDefinition(NumberFieldType.Int8)
|
||||||
.create(
|
.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
42,
|
42,
|
||||||
)
|
)
|
||||||
|
@ -164,7 +148,6 @@ describe('Types', () => {
|
||||||
new NumberFieldDefinition(NumberFieldType.Uint8)
|
new NumberFieldDefinition(NumberFieldType.Uint8)
|
||||||
.create(
|
.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
42,
|
42,
|
||||||
)
|
)
|
||||||
|
@ -175,7 +158,6 @@ describe('Types', () => {
|
||||||
new NumberFieldDefinition(NumberFieldType.Int16)
|
new NumberFieldDefinition(NumberFieldType.Int16)
|
||||||
.create(
|
.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
42,
|
42,
|
||||||
)
|
)
|
||||||
|
@ -186,7 +168,6 @@ describe('Types', () => {
|
||||||
new NumberFieldDefinition(NumberFieldType.Uint16)
|
new NumberFieldDefinition(NumberFieldType.Uint16)
|
||||||
.create(
|
.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
42,
|
42,
|
||||||
)
|
)
|
||||||
|
@ -197,7 +178,6 @@ describe('Types', () => {
|
||||||
new NumberFieldDefinition(NumberFieldType.Int32)
|
new NumberFieldDefinition(NumberFieldType.Int32)
|
||||||
.create(
|
.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
42,
|
42,
|
||||||
)
|
)
|
||||||
|
@ -208,7 +188,6 @@ describe('Types', () => {
|
||||||
new NumberFieldDefinition(NumberFieldType.Uint32)
|
new NumberFieldDefinition(NumberFieldType.Uint32)
|
||||||
.create(
|
.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
42,
|
42,
|
||||||
)
|
)
|
||||||
|
@ -219,7 +198,6 @@ describe('Types', () => {
|
||||||
new NumberFieldDefinition(NumberFieldType.Int64)
|
new NumberFieldDefinition(NumberFieldType.Int64)
|
||||||
.create(
|
.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
BigInt(100),
|
BigInt(100),
|
||||||
)
|
)
|
||||||
|
@ -230,7 +208,6 @@ describe('Types', () => {
|
||||||
new NumberFieldDefinition(NumberFieldType.Uint64)
|
new NumberFieldDefinition(NumberFieldType.Uint64)
|
||||||
.create(
|
.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
BigInt(100),
|
BigInt(100),
|
||||||
)
|
)
|
||||||
|
@ -241,14 +218,10 @@ describe('Types', () => {
|
||||||
|
|
||||||
describe('#serialize', () => {
|
describe('#serialize', () => {
|
||||||
it('should serialize uint8', () => {
|
it('should serialize uint8', () => {
|
||||||
const context: StructSerializationContext = {
|
|
||||||
encodeUtf8(input) { throw new Error(''); },
|
|
||||||
};
|
|
||||||
const definition = new NumberFieldDefinition(NumberFieldType.Int8);
|
const definition = new NumberFieldDefinition(NumberFieldType.Int8);
|
||||||
const struct = new StructValue();
|
const struct = new StructValue();
|
||||||
const value = definition.create(
|
const value = definition.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
42,
|
42,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { StructDeserializationContext, StructFieldDefinition, StructFieldValue, StructOptions, StructSerializationContext, StructValue } from '../basic';
|
import { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from '../basic';
|
||||||
|
import { Syncbird } from "../syncbird";
|
||||||
|
import { ValueOrPromise } from "../utils";
|
||||||
|
|
||||||
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];
|
||||||
|
@ -62,25 +64,37 @@ export class NumberFieldDefinition<
|
||||||
|
|
||||||
public create(
|
public create(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructSerializationContext,
|
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
value: TTypeScriptType,
|
value: TTypeScriptType,
|
||||||
): NumberFieldValue<this> {
|
): NumberFieldValue<this> {
|
||||||
return new NumberFieldValue(this, options, context, struct, value);
|
return new NumberFieldValue(this, options, struct, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deserialize(
|
public override deserialize(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructDeserializationContext,
|
stream: StructDeserializeStream,
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
): Promise<NumberFieldValue<this>> {
|
): NumberFieldValue<this>;
|
||||||
const buffer = await context.read(this.getSize());
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): Promise<NumberFieldValue<this>>;
|
||||||
|
public override deserialize(
|
||||||
|
options: Readonly<StructOptions>,
|
||||||
|
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
||||||
|
struct: StructValue,
|
||||||
|
): ValueOrPromise<NumberFieldValue<this>> {
|
||||||
|
return Syncbird.try(() => {
|
||||||
|
return stream.read(this.getSize());
|
||||||
|
}).then(buffer => {
|
||||||
const view = new DataView(buffer);
|
const view = new DataView(buffer);
|
||||||
const value = view[this.type.dataViewGetter](
|
const value = view[this.type.dataViewGetter](
|
||||||
0,
|
0,
|
||||||
options.littleEndian
|
options.littleEndian
|
||||||
) as any;
|
) as any;
|
||||||
return this.create(options, context, struct, value);
|
return this.create(options, struct, value);
|
||||||
|
}).valueOrPromise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,10 @@
|
||||||
import { StructDefaultOptions, StructDeserializationContext, StructFieldValue, StructSerializationContext, StructValue } from '../basic';
|
import { StructDefaultOptions, StructFieldValue, StructValue } from '../basic';
|
||||||
import { ArrayBufferFieldType, ArrayBufferLikeFieldType } from './array-buffer';
|
import { ArrayBufferFieldType, ArrayBufferLikeFieldType } from './array-buffer';
|
||||||
import { VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldLengthValue, VariableLengthArrayBufferLikeStructFieldValue } from './variable-length-array-buffer';
|
import { VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldLengthValue, VariableLengthArrayBufferLikeStructFieldValue } from './variable-length-array-buffer';
|
||||||
|
|
||||||
class MockDeserializationContext implements StructDeserializationContext {
|
|
||||||
public buffer = new ArrayBuffer(0);
|
|
||||||
|
|
||||||
public read = jest.fn((length: number) => this.buffer);
|
|
||||||
|
|
||||||
public encodeUtf8 = jest.fn((input: string) => Buffer.from(input, 'utf-8'));
|
|
||||||
|
|
||||||
public decodeUtf8 = jest.fn((buffer: ArrayBuffer) => Buffer.from(buffer).toString('utf-8'));
|
|
||||||
}
|
|
||||||
|
|
||||||
class MockOriginalFieldValue extends StructFieldValue {
|
class MockOriginalFieldValue extends StructFieldValue {
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super({} as any, {} as any, {} as any, {} as any, {});
|
super({} as any, {} as any, {} as any, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
public value: string | number = 0;
|
public value: string | number = 0;
|
||||||
|
@ -27,21 +17,21 @@ class MockOriginalFieldValue extends StructFieldValue {
|
||||||
|
|
||||||
public set = jest.fn((value: string | number) => { });
|
public set = jest.fn((value: string | number) => { });
|
||||||
|
|
||||||
public serialize = jest.fn((dataView: DataView, offset: number, context: StructSerializationContext): void => { });
|
public serialize = jest.fn((dataView: DataView, offset: number): void => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Types', () => {
|
describe('Types', () => {
|
||||||
describe('VariableLengthArrayBufferLikeFieldLengthValue', () => {
|
describe('VariableLengthArrayBufferLikeFieldLengthValue', () => {
|
||||||
class MockArrayBufferFieldValue extends StructFieldValue {
|
class MockArrayBufferFieldValue extends StructFieldValue {
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super({ options: {} } as any, {} as any, {} as any, {} as any, {});
|
super({ options: {} } as any, {} as any, {} as any, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
public size = 0;
|
public size = 0;
|
||||||
|
|
||||||
public getSize = jest.fn(() => this.size);
|
public getSize = jest.fn(() => this.size);
|
||||||
|
|
||||||
public serialize(dataView: DataView, offset: number, context: StructSerializationContext): void {
|
public serialize(dataView: DataView, offset: number): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,26 +131,25 @@ describe('Types', () => {
|
||||||
|
|
||||||
let dataView = 0 as any;
|
let dataView = 0 as any;
|
||||||
let offset = 1 as any;
|
let offset = 1 as any;
|
||||||
let context = 2 as any;
|
|
||||||
|
|
||||||
mockOriginalFieldValue.value = 10;
|
mockOriginalFieldValue.value = 10;
|
||||||
mockArrayBufferFieldValue.size = 0;
|
mockArrayBufferFieldValue.size = 0;
|
||||||
lengthFieldValue.serialize(dataView, offset, context);
|
lengthFieldValue.serialize(dataView, offset);
|
||||||
expect(mockOriginalFieldValue.get).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.get).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.get).toReturnWith(10);
|
expect(mockOriginalFieldValue.get).toReturnWith(10);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledWith(0);
|
expect(mockOriginalFieldValue.set).toBeCalledWith(0);
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset, context);
|
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset);
|
||||||
|
|
||||||
mockOriginalFieldValue.set.mockClear();
|
mockOriginalFieldValue.set.mockClear();
|
||||||
mockOriginalFieldValue.serialize.mockClear();
|
mockOriginalFieldValue.serialize.mockClear();
|
||||||
mockArrayBufferFieldValue.size = 100;
|
mockArrayBufferFieldValue.size = 100;
|
||||||
lengthFieldValue.serialize(dataView, offset, context);
|
lengthFieldValue.serialize(dataView, offset);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledWith(100);
|
expect(mockOriginalFieldValue.set).toBeCalledWith(100);
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset, context);
|
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stringify its length if `originalField` is a string', async () => {
|
it('should stringify its length if `originalField` is a string', async () => {
|
||||||
|
@ -173,26 +162,25 @@ describe('Types', () => {
|
||||||
|
|
||||||
let dataView = 0 as any;
|
let dataView = 0 as any;
|
||||||
let offset = 1 as any;
|
let offset = 1 as any;
|
||||||
let context = 2 as any;
|
|
||||||
|
|
||||||
mockOriginalFieldValue.value = '10';
|
mockOriginalFieldValue.value = '10';
|
||||||
mockArrayBufferFieldValue.size = 0;
|
mockArrayBufferFieldValue.size = 0;
|
||||||
lengthFieldValue.serialize(dataView, offset, context);
|
lengthFieldValue.serialize(dataView, offset);
|
||||||
expect(mockOriginalFieldValue.get).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.get).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.get).toReturnWith('10');
|
expect(mockOriginalFieldValue.get).toReturnWith('10');
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledWith('0');
|
expect(mockOriginalFieldValue.set).toBeCalledWith('0');
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset, context);
|
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset);
|
||||||
|
|
||||||
mockOriginalFieldValue.set.mockClear();
|
mockOriginalFieldValue.set.mockClear();
|
||||||
mockOriginalFieldValue.serialize.mockClear();
|
mockOriginalFieldValue.serialize.mockClear();
|
||||||
mockArrayBufferFieldValue.size = 100;
|
mockArrayBufferFieldValue.size = 100;
|
||||||
lengthFieldValue.serialize(dataView, offset, context);
|
lengthFieldValue.serialize(dataView, offset);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledWith('100');
|
expect(mockOriginalFieldValue.set).toBeCalledWith('100');
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset, context);
|
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stringify its length in specified base if `originalField` is a string', async () => {
|
it('should stringify its length in specified base if `originalField` is a string', async () => {
|
||||||
|
@ -208,26 +196,25 @@ describe('Types', () => {
|
||||||
|
|
||||||
let dataView = 0 as any;
|
let dataView = 0 as any;
|
||||||
let offset = 1 as any;
|
let offset = 1 as any;
|
||||||
let context = 2 as any;
|
|
||||||
|
|
||||||
mockOriginalFieldValue.value = '10';
|
mockOriginalFieldValue.value = '10';
|
||||||
mockArrayBufferFieldValue.size = 0;
|
mockArrayBufferFieldValue.size = 0;
|
||||||
lengthFieldValue.serialize(dataView, offset, context);
|
lengthFieldValue.serialize(dataView, offset);
|
||||||
expect(mockOriginalFieldValue.get).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.get).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.get).toReturnWith('10');
|
expect(mockOriginalFieldValue.get).toReturnWith('10');
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledWith('0');
|
expect(mockOriginalFieldValue.set).toBeCalledWith('0');
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset, context);
|
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset);
|
||||||
|
|
||||||
mockOriginalFieldValue.set.mockClear();
|
mockOriginalFieldValue.set.mockClear();
|
||||||
mockOriginalFieldValue.serialize.mockClear();
|
mockOriginalFieldValue.serialize.mockClear();
|
||||||
mockArrayBufferFieldValue.size = 100;
|
mockArrayBufferFieldValue.size = 100;
|
||||||
lengthFieldValue.serialize(dataView, offset, context);
|
lengthFieldValue.serialize(dataView, offset);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.set).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.set).toBeCalledWith((100).toString(base));
|
expect(mockOriginalFieldValue.set).toBeCalledWith((100).toString(base));
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
expect(mockOriginalFieldValue.serialize).toBeCalledTimes(1);
|
||||||
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset, context);
|
expect(mockOriginalFieldValue.serialize).toBeCalledWith(dataView, offset);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -246,20 +233,17 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(0);
|
const value = new ArrayBuffer(0);
|
||||||
|
|
||||||
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
arrayBufferFieldDefinition,
|
arrayBufferFieldDefinition,
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('definition', arrayBufferFieldDefinition);
|
expect(arrayBufferFieldValue).toHaveProperty('definition', arrayBufferFieldDefinition);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions);
|
expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('context', context);
|
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('struct', struct);
|
expect(arrayBufferFieldValue).toHaveProperty('struct', struct);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('value', value);
|
expect(arrayBufferFieldValue).toHaveProperty('value', value);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', undefined);
|
expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', undefined);
|
||||||
|
@ -278,13 +262,11 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(100);
|
const value = new ArrayBuffer(100);
|
||||||
|
|
||||||
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
arrayBufferFieldDefinition,
|
arrayBufferFieldDefinition,
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
value,
|
value,
|
||||||
|
@ -292,7 +274,6 @@ describe('Types', () => {
|
||||||
|
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('definition', arrayBufferFieldDefinition);
|
expect(arrayBufferFieldValue).toHaveProperty('definition', arrayBufferFieldDefinition);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions);
|
expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('context', context);
|
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('struct', struct);
|
expect(arrayBufferFieldValue).toHaveProperty('struct', struct);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('value', value);
|
expect(arrayBufferFieldValue).toHaveProperty('value', value);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', value);
|
expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', value);
|
||||||
|
@ -311,13 +292,11 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(0);
|
const value = new ArrayBuffer(0);
|
||||||
|
|
||||||
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
arrayBufferFieldDefinition,
|
arrayBufferFieldDefinition,
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
|
@ -329,11 +308,11 @@ describe('Types', () => {
|
||||||
|
|
||||||
describe('#getSize', () => {
|
describe('#getSize', () => {
|
||||||
class MockArrayBufferFieldType extends ArrayBufferLikeFieldType<ArrayBuffer> {
|
class MockArrayBufferFieldType extends ArrayBufferLikeFieldType<ArrayBuffer> {
|
||||||
public toArrayBuffer = jest.fn((value: ArrayBuffer, context: StructSerializationContext): ArrayBuffer => {
|
public toArrayBuffer = jest.fn((value: ArrayBuffer): ArrayBuffer => {
|
||||||
return value;
|
return value;
|
||||||
});
|
});
|
||||||
|
|
||||||
public fromArrayBuffer = jest.fn((arrayBuffer: ArrayBuffer, context: StructDeserializationContext): ArrayBuffer => {
|
public fromArrayBuffer = jest.fn((arrayBuffer: ArrayBuffer): ArrayBuffer => {
|
||||||
return arrayBuffer;
|
return arrayBuffer;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -357,13 +336,11 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(100);
|
const value = new ArrayBuffer(100);
|
||||||
|
|
||||||
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
arrayBufferFieldDefinition,
|
arrayBufferFieldDefinition,
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
value,
|
value,
|
||||||
|
@ -388,13 +365,11 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(100);
|
const value = new ArrayBuffer(100);
|
||||||
|
|
||||||
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
arrayBufferFieldDefinition,
|
arrayBufferFieldDefinition,
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
|
@ -421,13 +396,11 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(100);
|
const value = new ArrayBuffer(100);
|
||||||
|
|
||||||
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
arrayBufferFieldDefinition,
|
arrayBufferFieldDefinition,
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
|
@ -455,13 +428,11 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(100);
|
const value = new ArrayBuffer(100);
|
||||||
|
|
||||||
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
arrayBufferFieldDefinition,
|
arrayBufferFieldDefinition,
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
value,
|
value,
|
||||||
|
@ -485,13 +456,11 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(100);
|
const value = new ArrayBuffer(100);
|
||||||
|
|
||||||
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
arrayBufferFieldDefinition,
|
arrayBufferFieldDefinition,
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
value,
|
value,
|
||||||
|
@ -597,18 +566,15 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(100);
|
const value = new ArrayBuffer(100);
|
||||||
const arrayBufferFieldValue = definition.create(
|
const arrayBufferFieldValue = definition.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('definition', definition);
|
expect(arrayBufferFieldValue).toHaveProperty('definition', definition);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions);
|
expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('context', context);
|
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('struct', struct);
|
expect(arrayBufferFieldValue).toHaveProperty('struct', struct);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('value', value);
|
expect(arrayBufferFieldValue).toHaveProperty('value', value);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', undefined);
|
expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', undefined);
|
||||||
|
@ -627,11 +593,9 @@ describe('Types', () => {
|
||||||
{ lengthField },
|
{ lengthField },
|
||||||
);
|
);
|
||||||
|
|
||||||
const context = new MockDeserializationContext();
|
|
||||||
const value = new ArrayBuffer(100);
|
const value = new ArrayBuffer(100);
|
||||||
const arrayBufferFieldValue = definition.create(
|
const arrayBufferFieldValue = definition.create(
|
||||||
StructDefaultOptions,
|
StructDefaultOptions,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
value,
|
value,
|
||||||
|
@ -639,7 +603,6 @@ describe('Types', () => {
|
||||||
|
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('definition', definition);
|
expect(arrayBufferFieldValue).toHaveProperty('definition', definition);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions);
|
expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('context', context);
|
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('struct', struct);
|
expect(arrayBufferFieldValue).toHaveProperty('struct', struct);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('value', value);
|
expect(arrayBufferFieldValue).toHaveProperty('value', value);
|
||||||
expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', value);
|
expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', value);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { StructFieldDefinition, StructFieldValue, StructOptions, StructSerializationContext, StructValue } from '../basic';
|
import { StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from '../basic';
|
||||||
import type { KeysOfType } from '../utils';
|
import type { KeysOfType } from '../utils';
|
||||||
import { ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType, ArrayBufferLikeFieldValue } from './array-buffer';
|
import { ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType, ArrayBufferLikeFieldValue } from './array-buffer';
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ export class VariableLengthArrayBufferLikeFieldDefinition<
|
||||||
|
|
||||||
public create(
|
public create(
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructSerializationContext,
|
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
value: TType['TTypeScriptType'],
|
value: TType['TTypeScriptType'],
|
||||||
arrayBuffer?: ArrayBuffer
|
arrayBuffer?: ArrayBuffer
|
||||||
|
@ -43,7 +42,6 @@ export class VariableLengthArrayBufferLikeFieldDefinition<
|
||||||
return new VariableLengthArrayBufferLikeStructFieldValue(
|
return new VariableLengthArrayBufferLikeStructFieldValue(
|
||||||
this,
|
this,
|
||||||
options,
|
options,
|
||||||
context,
|
|
||||||
struct,
|
struct,
|
||||||
value,
|
value,
|
||||||
arrayBuffer,
|
arrayBuffer,
|
||||||
|
@ -61,12 +59,11 @@ export class VariableLengthArrayBufferLikeStructFieldValue<
|
||||||
public constructor(
|
public constructor(
|
||||||
definition: TDefinition,
|
definition: TDefinition,
|
||||||
options: Readonly<StructOptions>,
|
options: Readonly<StructOptions>,
|
||||||
context: StructSerializationContext,
|
|
||||||
struct: StructValue,
|
struct: StructValue,
|
||||||
value: TDefinition['TValue'],
|
value: TDefinition['TValue'],
|
||||||
arrayBuffer?: ArrayBuffer,
|
arrayBuffer?: ArrayBuffer,
|
||||||
) {
|
) {
|
||||||
super(definition, options, context, struct, value, arrayBuffer);
|
super(definition, options, struct, value, arrayBuffer);
|
||||||
|
|
||||||
if (arrayBuffer) {
|
if (arrayBuffer) {
|
||||||
this.length = arrayBuffer.byteLength;
|
this.length = arrayBuffer.byteLength;
|
||||||
|
@ -87,7 +84,7 @@ export class VariableLengthArrayBufferLikeStructFieldValue<
|
||||||
if (this.length === undefined) {
|
if (this.length === undefined) {
|
||||||
this.length = this.definition.type.getSize(this.value);
|
this.length = this.definition.type.getSize(this.value);
|
||||||
if (this.length === -1) {
|
if (this.length === -1) {
|
||||||
this.arrayBuffer = this.definition.type.toArrayBuffer(this.value, this.context);
|
this.arrayBuffer = this.definition.type.toArrayBuffer(this.value);
|
||||||
this.length = this.arrayBuffer.byteLength;
|
this.length = this.arrayBuffer.byteLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +113,7 @@ export class VariableLengthArrayBufferLikeFieldLengthValue
|
||||||
originalField: StructFieldValue,
|
originalField: StructFieldValue,
|
||||||
arrayBufferField: VariableLengthArrayBufferLikeFieldValueLike,
|
arrayBufferField: VariableLengthArrayBufferLikeFieldValueLike,
|
||||||
) {
|
) {
|
||||||
super(originalField.definition, originalField.options, originalField.context, originalField.struct, 0);
|
super(originalField.definition, originalField.options, originalField.struct, 0);
|
||||||
this.originalField = originalField;
|
this.originalField = originalField;
|
||||||
this.arrayBufferField = arrayBufferField;
|
this.arrayBufferField = arrayBufferField;
|
||||||
}
|
}
|
||||||
|
@ -138,8 +135,8 @@ export class VariableLengthArrayBufferLikeFieldLengthValue
|
||||||
|
|
||||||
set() { }
|
set() { }
|
||||||
|
|
||||||
serialize(dataView: DataView, offset: number, context: StructSerializationContext) {
|
serialize(dataView: DataView, offset: number) {
|
||||||
this.originalField.set(this.get());
|
this.originalField.set(this.get());
|
||||||
this.originalField.serialize(dataView, offset, context);
|
this.originalField.serialize(dataView, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,3 +49,16 @@ export type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T;
|
||||||
export function placeholder<T>(): T {
|
export function placeholder<T>(): T {
|
||||||
return undefined as unknown as T;
|
return undefined as unknown as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error @types/node missing `TextEncoder`
|
||||||
|
const Utf8Encoder = new TextEncoder();
|
||||||
|
// @ts-expect-error @types/node missing `TextDecoder`
|
||||||
|
const Utf8Decoder = new TextDecoder();
|
||||||
|
|
||||||
|
export function encodeUtf8(input: string): ArrayBuffer {
|
||||||
|
return Utf8Encoder.encode(input).buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeUtf8(buffer: ArrayBuffer): string {
|
||||||
|
return Utf8Decoder.decode(buffer);
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": [
|
"types": [
|
||||||
"node",
|
"node",
|
||||||
"jest"
|
"jest",
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"testTypes": [
|
"testTypes": [
|
||||||
"node",
|
"node",
|
||||||
|
|
|
@ -13,11 +13,11 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.9.1"
|
"@types/node": "^16.11.19"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"json5": "^2.2.0",
|
"json5": "^2.2.0",
|
||||||
"@types/jest": "^26.0.23"
|
"@types/jest": "^27.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^4.0.0"
|
"typescript": "^4.0.0"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue