@yume-chan/struct 0.0.23 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -2
- package/LICENSE +1 -1
- package/README.md +85 -714
- package/esm/bipedal.d.ts +5 -0
- package/esm/bipedal.d.ts.map +1 -0
- package/esm/bipedal.js +31 -0
- package/esm/bipedal.js.map +1 -0
- package/esm/buffer.d.ts +21 -0
- package/esm/buffer.d.ts.map +1 -0
- package/esm/buffer.js +173 -0
- package/esm/buffer.js.map +1 -0
- package/esm/field.d.ts +21 -0
- package/esm/field.d.ts.map +1 -0
- package/esm/field.js +2 -0
- package/esm/field.js.map +1 -0
- package/esm/index.d.ts +6 -4
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +6 -4
- package/esm/index.js.map +1 -1
- package/esm/number.d.ts +13 -0
- package/esm/number.d.ts.map +1 -0
- package/esm/number.js +59 -0
- package/esm/number.js.map +1 -0
- package/esm/{basic/stream.d.ts → readable.d.ts} +3 -3
- package/esm/readable.d.ts.map +1 -0
- package/esm/{basic/stream.js → readable.js} +1 -2
- package/esm/readable.js.map +1 -0
- package/esm/string.d.ts +16 -0
- package/esm/string.d.ts.map +1 -0
- package/esm/string.js +14 -0
- package/esm/string.js.map +1 -0
- package/esm/struct.d.ts +24 -127
- package/esm/struct.d.ts.map +1 -1
- package/esm/struct.js +72 -214
- package/esm/struct.js.map +1 -1
- package/esm/utils.d.ts +10 -45
- package/esm/utils.d.ts.map +1 -1
- package/esm/utils.js +12 -11
- package/esm/utils.js.map +1 -1
- package/package.json +11 -14
- package/src/bipedal.ts +55 -0
- package/src/buffer.ts +260 -0
- package/src/field.ts +27 -0
- package/src/index.ts +6 -4
- package/src/number.ts +123 -0
- package/src/{basic/stream.ts → readable.ts} +3 -4
- package/src/string.ts +43 -0
- package/src/struct.ts +156 -628
- package/src/utils.ts +18 -69
- package/tsconfig.build.json +6 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/CHANGELOG.json +0 -161
- package/esm/basic/definition.d.ts +0 -44
- package/esm/basic/definition.d.ts.map +0 -1
- package/esm/basic/definition.js +0 -24
- package/esm/basic/definition.js.map +0 -1
- package/esm/basic/field-value.d.ts +0 -39
- package/esm/basic/field-value.d.ts.map +0 -1
- package/esm/basic/field-value.js +0 -46
- package/esm/basic/field-value.js.map +0 -1
- package/esm/basic/index.d.ts +0 -6
- package/esm/basic/index.d.ts.map +0 -1
- package/esm/basic/index.js +0 -6
- package/esm/basic/index.js.map +0 -1
- package/esm/basic/options.d.ts +0 -10
- package/esm/basic/options.d.ts.map +0 -1
- package/esm/basic/options.js +0 -4
- package/esm/basic/options.js.map +0 -1
- package/esm/basic/stream.d.ts.map +0 -1
- package/esm/basic/stream.js.map +0 -1
- package/esm/basic/struct-value.d.ts +0 -26
- package/esm/basic/struct-value.d.ts.map +0 -1
- package/esm/basic/struct-value.js +0 -57
- package/esm/basic/struct-value.js.map +0 -1
- package/esm/sync-promise.d.ts +0 -13
- package/esm/sync-promise.d.ts.map +0 -1
- package/esm/sync-promise.js +0 -71
- package/esm/sync-promise.js.map +0 -1
- package/esm/types/bigint.d.ts +0 -26
- package/esm/types/bigint.d.ts.map +0 -1
- package/esm/types/bigint.js +0 -47
- package/esm/types/bigint.js.map +0 -1
- package/esm/types/buffer/base.d.ts +0 -65
- package/esm/types/buffer/base.d.ts.map +0 -1
- package/esm/types/buffer/base.js +0 -101
- package/esm/types/buffer/base.js.map +0 -1
- package/esm/types/buffer/fixed-length.d.ts +0 -9
- package/esm/types/buffer/fixed-length.d.ts.map +0 -1
- package/esm/types/buffer/fixed-length.js +0 -7
- package/esm/types/buffer/fixed-length.js.map +0 -1
- package/esm/types/buffer/index.d.ts +0 -4
- package/esm/types/buffer/index.d.ts.map +0 -1
- package/esm/types/buffer/index.js +0 -4
- package/esm/types/buffer/index.js.map +0 -1
- package/esm/types/buffer/variable-length.d.ts +0 -45
- package/esm/types/buffer/variable-length.d.ts.map +0 -1
- package/esm/types/buffer/variable-length.js +0 -76
- package/esm/types/buffer/variable-length.js.map +0 -1
- package/esm/types/index.d.ts +0 -4
- package/esm/types/index.d.ts.map +0 -1
- package/esm/types/index.js +0 -4
- package/esm/types/index.js.map +0 -1
- package/esm/types/number.d.ts +0 -28
- package/esm/types/number.d.ts.map +0 -1
- package/esm/types/number.js +0 -111
- package/esm/types/number.js.map +0 -1
- package/src/basic/definition.ts +0 -68
- package/src/basic/field-value.ts +0 -75
- package/src/basic/index.ts +0 -5
- package/src/basic/options.ts +0 -19
- package/src/basic/struct-value.ts +0 -67
- package/src/sync-promise.ts +0 -134
- package/src/types/bigint.ts +0 -121
- package/src/types/buffer/base.ts +0 -197
- package/src/types/buffer/fixed-length.ts +0 -17
- package/src/types/buffer/index.ts +0 -3
- package/src/types/buffer/variable-length.ts +0 -174
- package/src/types/index.ts +0 -3
- package/src/types/number.ts +0 -173
package/esm/utils.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const Utf8Encoder = new TextEncoder();
|
|
9
|
-
const Utf8Decoder = new TextDecoder();
|
|
1
|
+
// This library can't use `@types/node` or `lib: dom`
|
|
2
|
+
// because they will pollute the global scope
|
|
3
|
+
// So `TextEncoder` and `TextDecoder` types are not available
|
|
4
|
+
export const { TextEncoder, TextDecoder } = globalThis;
|
|
5
|
+
const SharedEncoder = /* #__PURE__ */ new TextEncoder();
|
|
6
|
+
const SharedDecoder = /* #__PURE__ */ new TextDecoder();
|
|
7
|
+
/* #__NO_SIDE_EFFECTS__ */
|
|
10
8
|
export function encodeUtf8(input) {
|
|
11
|
-
return
|
|
9
|
+
return SharedEncoder.encode(input);
|
|
12
10
|
}
|
|
11
|
+
/* #__NO_SIDE_EFFECTS__ */
|
|
13
12
|
export function decodeUtf8(buffer) {
|
|
14
|
-
|
|
13
|
+
// `TextDecoder` has internal states in stream mode,
|
|
14
|
+
// but this method is not for stream mode, so the instance can be reused
|
|
15
|
+
return SharedDecoder.decode(buffer);
|
|
15
16
|
}
|
|
16
17
|
//# sourceMappingURL=utils.js.map
|
package/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,6CAA6C;AAC7C,6DAA6D;AAsB7D,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GACrC,UAAwC,CAAC;AAE7C,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,WAAW,EAAE,CAAC;AACxD,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,WAAW,EAAE,CAAC;AAExD,0BAA0B;AAC1B,MAAM,UAAU,UAAU,CAAC,KAAa;IACpC,OAAO,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,0BAA0B;AAC1B,MAAM,UAAU,UAAU,CAAC,MAAqC;IAC5D,oDAAoD;IACpD,wEAAwE;IACxE,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yume-chan/struct",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "C-style structure serializer and deserializer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"structure",
|
|
@@ -26,25 +26,22 @@
|
|
|
26
26
|
"type": "module",
|
|
27
27
|
"main": "esm/index.js",
|
|
28
28
|
"types": "esm/index.d.ts",
|
|
29
|
+
"sideEffects": false,
|
|
29
30
|
"dependencies": {
|
|
30
|
-
"@yume-chan/
|
|
31
|
-
"
|
|
31
|
+
"@yume-chan/async": "^4.0.2",
|
|
32
|
+
"@yume-chan/no-data-view": "^1.0.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@
|
|
35
|
-
"
|
|
35
|
+
"@types/node": "^22.10.0",
|
|
36
|
+
"prettier": "^3.4.1",
|
|
37
|
+
"typescript": "^5.7.2",
|
|
36
38
|
"@yume-chan/tsconfig": "^1.0.0",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"jest": "^30.0.0-alpha.3",
|
|
40
|
-
"prettier": "^3.2.5",
|
|
41
|
-
"ts-jest": "^29.1.2",
|
|
42
|
-
"typescript": "^5.4.2"
|
|
39
|
+
"@yume-chan/eslint-config": "^1.0.0",
|
|
40
|
+
"@yume-chan/test-runner": "^1.0.0"
|
|
43
41
|
},
|
|
44
42
|
"scripts": {
|
|
45
43
|
"build": "tsc -b tsconfig.build.json",
|
|
46
|
-
"
|
|
47
|
-
"test": "
|
|
48
|
-
"lint": "eslint src/**/*.ts --fix && prettier src/**/*.ts --write --tab-width 4"
|
|
44
|
+
"lint": "run-eslint && prettier src/**/*.ts --write --tab-width 4",
|
|
45
|
+
"test": "run-test"
|
|
49
46
|
}
|
|
50
47
|
}
|
package/src/bipedal.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { MaybePromiseLike } from "@yume-chan/async";
|
|
2
|
+
import { isPromiseLike } from "@yume-chan/async";
|
|
3
|
+
|
|
4
|
+
function advance<T>(
|
|
5
|
+
iterator: Iterator<unknown, T, unknown>,
|
|
6
|
+
next: unknown,
|
|
7
|
+
): MaybePromiseLike<T> {
|
|
8
|
+
while (true) {
|
|
9
|
+
const { done, value } = iterator.next(next);
|
|
10
|
+
if (done) {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
if (isPromiseLike(value)) {
|
|
14
|
+
return value.then(
|
|
15
|
+
(value) => advance(iterator, { resolved: value }),
|
|
16
|
+
(error: unknown) => advance(iterator, { error }),
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
next = value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function bipedal<This, T, A extends unknown[]>(
|
|
24
|
+
fn: (
|
|
25
|
+
this: This,
|
|
26
|
+
then: <U>(value: U | PromiseLike<U>) => Iterable<unknown, U, unknown>,
|
|
27
|
+
...args: A
|
|
28
|
+
) => Generator<unknown, T, unknown>,
|
|
29
|
+
): { (this: This, ...args: A): MaybePromiseLike<T> } {
|
|
30
|
+
return function (this: This, ...args: A) {
|
|
31
|
+
const iterator = fn.call(
|
|
32
|
+
this,
|
|
33
|
+
function* <U>(
|
|
34
|
+
value: U | PromiseLike<U>,
|
|
35
|
+
): Generator<
|
|
36
|
+
PromiseLike<U>,
|
|
37
|
+
U,
|
|
38
|
+
{ resolved: U } | { error: unknown }
|
|
39
|
+
> {
|
|
40
|
+
if (isPromiseLike(value)) {
|
|
41
|
+
const result = yield value;
|
|
42
|
+
if ("resolved" in result) {
|
|
43
|
+
return result.resolved;
|
|
44
|
+
} else {
|
|
45
|
+
throw result.error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return value;
|
|
50
|
+
},
|
|
51
|
+
...args,
|
|
52
|
+
) as never;
|
|
53
|
+
return advance(iterator, undefined);
|
|
54
|
+
};
|
|
55
|
+
}
|
package/src/buffer.ts
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { bipedal } from "./bipedal.js";
|
|
2
|
+
import type { Field } from "./field.js";
|
|
3
|
+
|
|
4
|
+
export interface Converter<From, To> {
|
|
5
|
+
convert: (value: From) => To;
|
|
6
|
+
back: (value: To) => From;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface BufferLengthConverter<K, KT> extends Converter<KT, number> {
|
|
10
|
+
field: K;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface BufferLike {
|
|
14
|
+
(length: number): Field<Uint8Array, never, never>;
|
|
15
|
+
<U>(
|
|
16
|
+
length: number,
|
|
17
|
+
converter: Converter<Uint8Array, U>,
|
|
18
|
+
): Field<U, never, never>;
|
|
19
|
+
|
|
20
|
+
<K extends string>(lengthField: K): Field<Uint8Array, K, Record<K, number>>;
|
|
21
|
+
<K extends string, U>(
|
|
22
|
+
lengthField: K,
|
|
23
|
+
converter: Converter<Uint8Array, U>,
|
|
24
|
+
): Field<U, K, Record<K, number>>;
|
|
25
|
+
|
|
26
|
+
<K extends string, KT>(
|
|
27
|
+
length: BufferLengthConverter<K, KT>,
|
|
28
|
+
): Field<Uint8Array, K, Record<K, KT>>;
|
|
29
|
+
<K extends string, KT, U>(
|
|
30
|
+
length: BufferLengthConverter<K, KT>,
|
|
31
|
+
converter: Converter<Uint8Array, U>,
|
|
32
|
+
): Field<U, K, Record<K, KT>>;
|
|
33
|
+
|
|
34
|
+
<KOmitInit extends string, KS>(
|
|
35
|
+
length: Field<number, KOmitInit, KS>,
|
|
36
|
+
): Field<Uint8Array, KOmitInit, KS>;
|
|
37
|
+
<KOmitInit extends string, KS, U>(
|
|
38
|
+
length: Field<number, KOmitInit, KS>,
|
|
39
|
+
converter: Converter<Uint8Array, U>,
|
|
40
|
+
): Field<U, KOmitInit, KS>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const EmptyUint8Array = new Uint8Array(0);
|
|
44
|
+
|
|
45
|
+
export const buffer: BufferLike = function (
|
|
46
|
+
lengthOrField:
|
|
47
|
+
| string
|
|
48
|
+
| number
|
|
49
|
+
| Field<number, never, unknown>
|
|
50
|
+
| BufferLengthConverter<string, unknown>,
|
|
51
|
+
converter?: Converter<Uint8Array, unknown>,
|
|
52
|
+
): Field<unknown, string, Record<string, unknown>> {
|
|
53
|
+
if (typeof lengthOrField === "number") {
|
|
54
|
+
if (converter) {
|
|
55
|
+
if (lengthOrField === 0) {
|
|
56
|
+
return {
|
|
57
|
+
size: 0,
|
|
58
|
+
serialize: () => {},
|
|
59
|
+
deserialize: () => converter.convert(EmptyUint8Array),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
size: lengthOrField,
|
|
65
|
+
serialize: (value, { buffer, index }) => {
|
|
66
|
+
buffer.set(
|
|
67
|
+
converter.back(value).slice(0, lengthOrField),
|
|
68
|
+
index,
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
deserialize: bipedal(function* (then, { reader }) {
|
|
72
|
+
const array = yield* then(
|
|
73
|
+
reader.readExactly(lengthOrField),
|
|
74
|
+
);
|
|
75
|
+
return converter.convert(array);
|
|
76
|
+
}),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (lengthOrField === 0) {
|
|
81
|
+
return {
|
|
82
|
+
size: 0,
|
|
83
|
+
serialize: () => {},
|
|
84
|
+
deserialize: () => EmptyUint8Array,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
size: lengthOrField,
|
|
90
|
+
serialize: (value, { buffer, index }) => {
|
|
91
|
+
buffer.set(
|
|
92
|
+
(value as Uint8Array).slice(0, lengthOrField),
|
|
93
|
+
index,
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
deserialize: ({ reader }) => reader.readExactly(lengthOrField),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Some Field type might be `function`s
|
|
101
|
+
if (
|
|
102
|
+
(typeof lengthOrField === "object" ||
|
|
103
|
+
typeof lengthOrField === "function") &&
|
|
104
|
+
"serialize" in lengthOrField
|
|
105
|
+
) {
|
|
106
|
+
if (converter) {
|
|
107
|
+
return {
|
|
108
|
+
size: 0,
|
|
109
|
+
dynamicSize(value) {
|
|
110
|
+
const array = converter.back(value);
|
|
111
|
+
const lengthFieldSize =
|
|
112
|
+
lengthOrField.dynamicSize?.(array.length) ??
|
|
113
|
+
lengthOrField.size;
|
|
114
|
+
return lengthFieldSize + array.length;
|
|
115
|
+
},
|
|
116
|
+
serialize(value, context) {
|
|
117
|
+
const array = converter.back(value);
|
|
118
|
+
const lengthFieldSize =
|
|
119
|
+
lengthOrField.dynamicSize?.(array.length) ??
|
|
120
|
+
lengthOrField.size;
|
|
121
|
+
lengthOrField.serialize(array.length, context);
|
|
122
|
+
context.buffer.set(array, context.index + lengthFieldSize);
|
|
123
|
+
},
|
|
124
|
+
deserialize: bipedal(function* (then, context) {
|
|
125
|
+
const length = yield* then(
|
|
126
|
+
lengthOrField.deserialize(context),
|
|
127
|
+
);
|
|
128
|
+
const array = yield* then(
|
|
129
|
+
context.reader.readExactly(length),
|
|
130
|
+
);
|
|
131
|
+
return converter.convert(array);
|
|
132
|
+
}),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
size: 0,
|
|
138
|
+
dynamicSize(value) {
|
|
139
|
+
const lengthFieldSize =
|
|
140
|
+
lengthOrField.dynamicSize?.((value as Uint8Array).length) ??
|
|
141
|
+
lengthOrField.size;
|
|
142
|
+
return lengthFieldSize + (value as Uint8Array).length;
|
|
143
|
+
},
|
|
144
|
+
serialize(value, context) {
|
|
145
|
+
const lengthFieldSize =
|
|
146
|
+
lengthOrField.dynamicSize?.((value as Uint8Array).length) ??
|
|
147
|
+
lengthOrField.size;
|
|
148
|
+
lengthOrField.serialize((value as Uint8Array).length, context);
|
|
149
|
+
context.buffer.set(
|
|
150
|
+
value as Uint8Array,
|
|
151
|
+
context.index + lengthFieldSize,
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
deserialize: bipedal(function* (then, context) {
|
|
155
|
+
const length = yield* then(lengthOrField.deserialize(context));
|
|
156
|
+
return context.reader.readExactly(length);
|
|
157
|
+
}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (typeof lengthOrField === "string") {
|
|
162
|
+
if (converter) {
|
|
163
|
+
return {
|
|
164
|
+
size: 0,
|
|
165
|
+
preSerialize: (value, runtimeStruct) => {
|
|
166
|
+
runtimeStruct[lengthOrField] = converter.back(value).length;
|
|
167
|
+
},
|
|
168
|
+
dynamicSize: (value) => {
|
|
169
|
+
return converter.back(value).length;
|
|
170
|
+
},
|
|
171
|
+
serialize: (value, { buffer, index }) => {
|
|
172
|
+
buffer.set(converter.back(value), index);
|
|
173
|
+
},
|
|
174
|
+
deserialize: bipedal(function* (
|
|
175
|
+
then,
|
|
176
|
+
{ reader, runtimeStruct },
|
|
177
|
+
) {
|
|
178
|
+
const length = runtimeStruct[lengthOrField] as number;
|
|
179
|
+
if (length === 0) {
|
|
180
|
+
return converter.convert(EmptyUint8Array);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const value = yield* then(reader.readExactly(length));
|
|
184
|
+
return converter.convert(value);
|
|
185
|
+
}),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
size: 0,
|
|
191
|
+
preSerialize: (value, runtimeStruct) => {
|
|
192
|
+
runtimeStruct[lengthOrField] = (value as Uint8Array).length;
|
|
193
|
+
},
|
|
194
|
+
dynamicSize: (value) => {
|
|
195
|
+
return (value as Uint8Array).length;
|
|
196
|
+
},
|
|
197
|
+
serialize: (value, { buffer, index }) => {
|
|
198
|
+
buffer.set(value as Uint8Array, index);
|
|
199
|
+
},
|
|
200
|
+
deserialize: ({ reader, runtimeStruct }) => {
|
|
201
|
+
const length = runtimeStruct[lengthOrField] as number;
|
|
202
|
+
if (length === 0) {
|
|
203
|
+
return EmptyUint8Array;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return reader.readExactly(length);
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (converter) {
|
|
212
|
+
return {
|
|
213
|
+
size: 0,
|
|
214
|
+
preSerialize: (value, runtimeStruct) => {
|
|
215
|
+
const length = converter.back(value).length;
|
|
216
|
+
runtimeStruct[lengthOrField.field] = lengthOrField.back(length);
|
|
217
|
+
},
|
|
218
|
+
dynamicSize: (value) => {
|
|
219
|
+
return converter.back(value).length;
|
|
220
|
+
},
|
|
221
|
+
serialize: (value, { buffer, index }) => {
|
|
222
|
+
buffer.set(converter.back(value), index);
|
|
223
|
+
},
|
|
224
|
+
deserialize: bipedal(function* (then, { reader, runtimeStruct }) {
|
|
225
|
+
const rawLength = runtimeStruct[lengthOrField.field];
|
|
226
|
+
const length = lengthOrField.convert(rawLength);
|
|
227
|
+
if (length === 0) {
|
|
228
|
+
return converter.convert(EmptyUint8Array);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const value = yield* then(reader.readExactly(length));
|
|
232
|
+
return converter.convert(value);
|
|
233
|
+
}),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
size: 0,
|
|
239
|
+
preSerialize: (value, runtimeStruct) => {
|
|
240
|
+
runtimeStruct[lengthOrField.field] = lengthOrField.back(
|
|
241
|
+
(value as Uint8Array).length,
|
|
242
|
+
);
|
|
243
|
+
},
|
|
244
|
+
dynamicSize: (value) => {
|
|
245
|
+
return (value as Uint8Array).length;
|
|
246
|
+
},
|
|
247
|
+
serialize: (value, { buffer, index }) => {
|
|
248
|
+
buffer.set(value as Uint8Array, index);
|
|
249
|
+
},
|
|
250
|
+
deserialize: ({ reader, runtimeStruct }) => {
|
|
251
|
+
const rawLength = runtimeStruct[lengthOrField.field];
|
|
252
|
+
const length = lengthOrField.convert(rawLength);
|
|
253
|
+
if (length === 0) {
|
|
254
|
+
return EmptyUint8Array;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return reader.readExactly(length);
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
} as never;
|
package/src/field.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MaybePromiseLike } from "@yume-chan/async";
|
|
2
|
+
|
|
3
|
+
import type { AsyncExactReadable } from "./readable.js";
|
|
4
|
+
|
|
5
|
+
export interface SerializeContext {
|
|
6
|
+
buffer: Uint8Array;
|
|
7
|
+
index: number;
|
|
8
|
+
littleEndian: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface DeserializeContext<S> {
|
|
12
|
+
reader: AsyncExactReadable;
|
|
13
|
+
littleEndian: boolean;
|
|
14
|
+
runtimeStruct: S;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Field<T, OmitInit extends string, S> {
|
|
18
|
+
__invariant?: OmitInit;
|
|
19
|
+
|
|
20
|
+
size: number;
|
|
21
|
+
|
|
22
|
+
dynamicSize?(value: T): number;
|
|
23
|
+
preSerialize?(value: T, runtimeStruct: S): void;
|
|
24
|
+
serialize(value: T, context: SerializeContext): void;
|
|
25
|
+
|
|
26
|
+
deserialize(context: DeserializeContext<S>): MaybePromiseLike<T>;
|
|
27
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,9 +10,11 @@ declare global {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export * from "./
|
|
13
|
+
export * from "./bipedal.js";
|
|
14
|
+
export * from "./buffer.js";
|
|
15
|
+
export * from "./field.js";
|
|
16
|
+
export * from "./number.js";
|
|
17
|
+
export * from "./readable.js";
|
|
18
|
+
export * from "./string.js";
|
|
14
19
|
export * from "./struct.js";
|
|
15
|
-
export { Struct as default } from "./struct.js";
|
|
16
|
-
export * from "./sync-promise.js";
|
|
17
|
-
export * from "./types/index.js";
|
|
18
20
|
export * from "./utils.js";
|
package/src/number.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getInt16,
|
|
3
|
+
getInt32,
|
|
4
|
+
getInt64,
|
|
5
|
+
getInt8,
|
|
6
|
+
getUint16,
|
|
7
|
+
getUint32,
|
|
8
|
+
getUint64,
|
|
9
|
+
setInt16,
|
|
10
|
+
setInt32,
|
|
11
|
+
setInt64,
|
|
12
|
+
setUint16,
|
|
13
|
+
setUint32,
|
|
14
|
+
setUint64,
|
|
15
|
+
} from "@yume-chan/no-data-view";
|
|
16
|
+
|
|
17
|
+
import { bipedal } from "./bipedal.js";
|
|
18
|
+
import type { Field } from "./field.js";
|
|
19
|
+
|
|
20
|
+
export interface NumberField<T> extends Field<T, never, never> {
|
|
21
|
+
<const U>(infer?: U): Field<U, never, never>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* #__NO_SIDE_EFFECTS__ */
|
|
25
|
+
function factory<T>(
|
|
26
|
+
size: number,
|
|
27
|
+
serialize: Field<T, never, never>["serialize"],
|
|
28
|
+
deserialize: Field<T, never, never>["deserialize"],
|
|
29
|
+
): NumberField<T> {
|
|
30
|
+
const result = () => result;
|
|
31
|
+
result.size = size;
|
|
32
|
+
result.serialize = serialize;
|
|
33
|
+
result.deserialize = deserialize;
|
|
34
|
+
return result as never;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const u8 = factory<number>(
|
|
38
|
+
1,
|
|
39
|
+
(value, { buffer, index }) => {
|
|
40
|
+
buffer[index] = value;
|
|
41
|
+
},
|
|
42
|
+
bipedal(function* (then, { reader }) {
|
|
43
|
+
const data = yield* then(reader.readExactly(1));
|
|
44
|
+
return data[0]!;
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
export const s8 = factory<number>(
|
|
49
|
+
1,
|
|
50
|
+
(value, { buffer, index }) => {
|
|
51
|
+
buffer[index] = value;
|
|
52
|
+
},
|
|
53
|
+
bipedal(function* (then, { reader }) {
|
|
54
|
+
const data = yield* then(reader.readExactly(1));
|
|
55
|
+
return getInt8(data, 0);
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
export const u16 = factory<number>(
|
|
60
|
+
2,
|
|
61
|
+
(value, { buffer, index, littleEndian }) => {
|
|
62
|
+
setUint16(buffer, index, value, littleEndian);
|
|
63
|
+
},
|
|
64
|
+
bipedal(function* (then, { reader, littleEndian }) {
|
|
65
|
+
const data = yield* then(reader.readExactly(2));
|
|
66
|
+
return getUint16(data, 0, littleEndian);
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
export const s16 = factory<number>(
|
|
71
|
+
2,
|
|
72
|
+
(value, { buffer, index, littleEndian }) => {
|
|
73
|
+
setInt16(buffer, index, value, littleEndian);
|
|
74
|
+
},
|
|
75
|
+
bipedal(function* (then, { reader, littleEndian }) {
|
|
76
|
+
const data = yield* then(reader.readExactly(2));
|
|
77
|
+
return getInt16(data, 0, littleEndian);
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
export const u32 = factory<number>(
|
|
82
|
+
4,
|
|
83
|
+
(value, { buffer, index, littleEndian }) => {
|
|
84
|
+
setUint32(buffer, index, value, littleEndian);
|
|
85
|
+
},
|
|
86
|
+
bipedal(function* (then, { reader, littleEndian }) {
|
|
87
|
+
const data = yield* then(reader.readExactly(4));
|
|
88
|
+
return getUint32(data, 0, littleEndian);
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
export const s32 = factory<number>(
|
|
93
|
+
4,
|
|
94
|
+
(value, { buffer, index, littleEndian }) => {
|
|
95
|
+
setInt32(buffer, index, value, littleEndian);
|
|
96
|
+
},
|
|
97
|
+
bipedal(function* (then, { reader, littleEndian }) {
|
|
98
|
+
const data = yield* then(reader.readExactly(4));
|
|
99
|
+
return getInt32(data, 0, littleEndian);
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
export const u64 = factory<bigint>(
|
|
104
|
+
8,
|
|
105
|
+
(value, { buffer, index, littleEndian }) => {
|
|
106
|
+
setUint64(buffer, index, value, littleEndian);
|
|
107
|
+
},
|
|
108
|
+
bipedal(function* (then, { reader, littleEndian }) {
|
|
109
|
+
const data = yield* then(reader.readExactly(8));
|
|
110
|
+
return getUint64(data, 0, littleEndian);
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
export const s64 = factory<bigint>(
|
|
115
|
+
8,
|
|
116
|
+
(value, { buffer, index, littleEndian }) => {
|
|
117
|
+
setInt64(buffer, index, value, littleEndian);
|
|
118
|
+
},
|
|
119
|
+
bipedal(function* (then, { reader, littleEndian }) {
|
|
120
|
+
const data = yield* then(reader.readExactly(8));
|
|
121
|
+
return getInt64(data, 0, littleEndian);
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import type { ValueOrPromise } from "../utils.js";
|
|
2
|
-
|
|
3
1
|
// TODO: allow over reading (returning a `Uint8Array`, an `offset` and a `length`) to avoid copying
|
|
4
2
|
|
|
3
|
+
import type { MaybePromiseLike } from "@yume-chan/async";
|
|
4
|
+
|
|
5
5
|
export class ExactReadableEndedError extends Error {
|
|
6
6
|
constructor() {
|
|
7
7
|
super("ExactReadable ended");
|
|
8
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
9
8
|
}
|
|
10
9
|
}
|
|
11
10
|
|
|
@@ -30,5 +29,5 @@ export interface AsyncExactReadable {
|
|
|
30
29
|
* The stream must return exactly `length` bytes or data. If that's not possible
|
|
31
30
|
* (due to end of file or other error condition), it must throw an {@link ExactReadableEndedError}.
|
|
32
31
|
*/
|
|
33
|
-
readExactly(length: number):
|
|
32
|
+
readExactly(length: number): MaybePromiseLike<Uint8Array>;
|
|
34
33
|
}
|
package/src/string.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { BufferLengthConverter } from "./buffer.js";
|
|
2
|
+
import { buffer } from "./buffer.js";
|
|
3
|
+
import type { Field } from "./field.js";
|
|
4
|
+
import { decodeUtf8, encodeUtf8 } from "./utils.js";
|
|
5
|
+
|
|
6
|
+
export interface String {
|
|
7
|
+
(length: number): Field<string, never, never> & {
|
|
8
|
+
as: <T>(infer: T) => Field<T, never, never>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
<K extends string>(
|
|
12
|
+
lengthField: K,
|
|
13
|
+
): Field<string, K, Record<K, number>> & {
|
|
14
|
+
as: <T>(infer: T) => Field<T, K, Record<K, number>>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
<const K extends string, KT>(
|
|
18
|
+
length: BufferLengthConverter<K, KT>,
|
|
19
|
+
): Field<string, K, Record<K, KT>> & {
|
|
20
|
+
as: <T>(infer: T) => Field<T, K, Record<K, KT>>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
<KOmitInit extends string, KS>(
|
|
24
|
+
length: Field<number, KOmitInit, KS>,
|
|
25
|
+
): Field<string, KOmitInit, KS>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Rollup doesn't support `/* #__NO_SIDE_EFFECTS__ */ export const a = () => {}
|
|
29
|
+
/* #__NO_SIDE_EFFECTS__ */
|
|
30
|
+
function _string(
|
|
31
|
+
lengthOrField: string | number | BufferLengthConverter<string, unknown>,
|
|
32
|
+
): Field<string, string, Record<string, unknown>> & {
|
|
33
|
+
as: <T>(infer: T) => Field<T, string, Record<string, unknown>>;
|
|
34
|
+
} {
|
|
35
|
+
const field = buffer(lengthOrField as never, {
|
|
36
|
+
convert: decodeUtf8,
|
|
37
|
+
back: encodeUtf8,
|
|
38
|
+
});
|
|
39
|
+
(field as never as { as: unknown }).as = () => field;
|
|
40
|
+
return field as never;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const string: String = _string as never;
|