@yume-chan/struct 0.0.24 → 2.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 +40 -1
- package/LICENSE +1 -1
- package/README.md +85 -714
- package/esm/bipedal.d.ts +6 -0
- package/esm/bipedal.d.ts.map +1 -0
- package/esm/bipedal.js +38 -0
- package/esm/bipedal.js.map +1 -0
- package/esm/buffer.d.ts +30 -0
- package/esm/buffer.d.ts.map +1 -0
- package/esm/buffer.js +170 -0
- package/esm/buffer.js.map +1 -0
- package/esm/concat.d.ts +13 -0
- package/esm/concat.d.ts.map +1 -0
- package/esm/concat.js +10 -0
- package/esm/concat.js.map +1 -0
- package/esm/extend.d.ts +6 -0
- package/esm/extend.d.ts.map +1 -0
- package/esm/extend.js +10 -0
- package/esm/extend.js.map +1 -0
- package/esm/field/factory.d.ts +13 -0
- package/esm/field/factory.d.ts.map +1 -0
- package/esm/field/factory.js +21 -0
- package/esm/field/factory.js.map +1 -0
- package/esm/field/index.d.ts +4 -0
- package/esm/field/index.d.ts.map +1 -0
- package/esm/field/index.js +4 -0
- package/esm/field/index.js.map +1 -0
- package/esm/field/serialize.d.ts +8 -0
- package/esm/field/serialize.d.ts.map +1 -0
- package/esm/field/serialize.js +33 -0
- package/esm/field/serialize.js.map +1 -0
- package/esm/field/types.d.ts +33 -0
- package/esm/field/types.d.ts.map +1 -0
- package/esm/field/types.js +2 -0
- package/esm/field/types.js.map +1 -0
- package/esm/index.d.ts +9 -4
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +9 -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 +57 -0
- package/esm/number.js.map +1 -0
- package/esm/{basic/stream.d.ts → readable.d.ts} +9 -3
- package/esm/readable.d.ts.map +1 -0
- package/esm/readable.js +26 -0
- 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 +13 -0
- package/esm/string.js.map +1 -0
- package/esm/struct.d.ts +22 -140
- package/esm/struct.d.ts.map +1 -1
- package/esm/struct.js +109 -227
- package/esm/struct.js.map +1 -1
- package/esm/types.d.ts +20 -0
- package/esm/types.d.ts.map +1 -0
- package/esm/types.js +2 -0
- package/esm/types.js.map +1 -0
- package/esm/utils.d.ts +10 -45
- package/esm/utils.d.ts.map +1 -1
- package/esm/utils.js +8 -9
- package/esm/utils.js.map +1 -1
- package/package.json +11 -12
- package/src/bipedal.ts +65 -0
- package/src/buffer.ts +334 -0
- package/src/concat.ts +78 -0
- package/src/extend.ts +37 -0
- package/src/field/factory.ts +64 -0
- package/src/field/index.ts +3 -0
- package/src/field/serialize.ts +58 -0
- package/src/field/types.ts +48 -0
- package/src/index.ts +9 -4
- package/src/number.ts +134 -0
- package/src/{basic/stream.ts → readable.ts} +31 -4
- package/src/string.ts +41 -0
- package/src/struct.ts +200 -670
- package/src/types.ts +37 -0
- package/src/utils.ts +14 -67
- package/tsconfig.build.json +6 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/CHANGELOG.json +0 -173
- 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 +0 -8
- package/esm/basic/stream.js.map +0 -1
- package/esm/basic/struct-value.d.ts +0 -30
- package/esm/basic/struct-value.d.ts.map +0 -1
- package/esm/basic/struct-value.js +0 -62
- 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 -25
- package/esm/types/bigint.d.ts.map +0 -1
- package/esm/types/bigint.js +0 -46
- package/esm/types/bigint.js.map +0 -1
- package/esm/types/buffer/base.d.ts +0 -69
- package/esm/types/buffer/base.d.ts.map +0 -1
- package/esm/types/buffer/base.js +0 -100
- 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 -46
- package/esm/types/buffer/variable-length.d.ts.map +0 -1
- package/esm/types/buffer/variable-length.js +0 -87
- 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 -99
- 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 -85
- package/src/sync-promise.ts +0 -134
- package/src/types/bigint.ts +0 -124
- package/src/types/buffer/base.ts +0 -198
- package/src/types/buffer/fixed-length.ts +0 -22
- package/src/types/buffer/index.ts +0 -3
- package/src/types/buffer/variable-length.ts +0 -198
- package/src/types/index.ts +0 -3
- package/src/types/number.ts +0 -166
package/src/struct.ts
CHANGED
|
@@ -1,202 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { bipedal } from "./bipedal.js";
|
|
3
2
|
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "./
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
STRUCT_VALUE_SYMBOL,
|
|
13
|
-
StructDefaultOptions,
|
|
14
|
-
StructValue,
|
|
15
|
-
isStructValueInit,
|
|
16
|
-
} from "./basic/index.js";
|
|
17
|
-
import { SyncPromise } from "./sync-promise.js";
|
|
3
|
+
Field,
|
|
4
|
+
FieldByobSerializeContext,
|
|
5
|
+
FieldDefaultSerializeContext,
|
|
6
|
+
FieldDeserializeContext,
|
|
7
|
+
FieldDeserializer,
|
|
8
|
+
} from "./field/index.js";
|
|
9
|
+
import type { AsyncExactReadable } from "./readable.js";
|
|
10
|
+
import { ExactReadableEndedError } from "./readable.js";
|
|
18
11
|
import type {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export type
|
|
44
|
-
|
|
12
|
+
StructDeserializer,
|
|
13
|
+
StructSerializeContext,
|
|
14
|
+
StructSerializer,
|
|
15
|
+
} from "./types.js";
|
|
16
|
+
|
|
17
|
+
export type StructField =
|
|
18
|
+
| Field<unknown, string, unknown, unknown>
|
|
19
|
+
| (StructSerializer<unknown> & StructDeserializer<unknown>);
|
|
20
|
+
|
|
21
|
+
export type StructFields = Record<string, StructField>;
|
|
22
|
+
|
|
23
|
+
export type FieldsValue<T extends StructFields> = {
|
|
24
|
+
[K in keyof T]: T[K] extends FieldDeserializer<infer U, unknown>
|
|
25
|
+
? U
|
|
26
|
+
: never;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type FieldOmitInit<T extends StructField> =
|
|
30
|
+
T extends Field<unknown, infer U, unknown, unknown>
|
|
31
|
+
? string extends U
|
|
32
|
+
? never
|
|
33
|
+
: U
|
|
34
|
+
: never;
|
|
35
|
+
|
|
36
|
+
export type FieldsOmitInits<T extends StructFields> = {
|
|
37
|
+
[K in keyof T]: FieldOmitInit<T[K]>;
|
|
38
|
+
}[keyof T];
|
|
39
|
+
|
|
40
|
+
export type FieldsInit<T extends StructFields> = Omit<
|
|
41
|
+
FieldsValue<T>,
|
|
42
|
+
FieldsOmitInits<T>
|
|
45
43
|
>;
|
|
46
44
|
|
|
47
|
-
/**
|
|
48
|
-
* Create a new `Struct` type with `TDefinition` appended
|
|
49
|
-
*/
|
|
50
|
-
type AddFieldDescriptor<
|
|
51
|
-
TFields extends object,
|
|
52
|
-
TOmitInitKey extends PropertyKey,
|
|
53
|
-
TExtra extends object,
|
|
54
|
-
TPostDeserialized,
|
|
55
|
-
TFieldName extends PropertyKey,
|
|
56
|
-
TDefinition extends StructFieldDefinition<unknown, unknown, PropertyKey>,
|
|
57
|
-
> = Identity<
|
|
58
|
-
Struct<
|
|
59
|
-
// Merge two types
|
|
60
|
-
// Evaluate immediately to optimize editor hover tooltip
|
|
61
|
-
Evaluate<TFields & Record<TFieldName, TDefinition["TValue"]>>,
|
|
62
|
-
// Merge two `TOmitInitKey`s
|
|
63
|
-
TOmitInitKey | TDefinition["TOmitInitKey"],
|
|
64
|
-
TExtra,
|
|
65
|
-
TPostDeserialized
|
|
66
|
-
>
|
|
67
|
-
>;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Overload methods to add an array buffer like field
|
|
71
|
-
*/
|
|
72
|
-
interface ArrayBufferLikeFieldCreator<
|
|
73
|
-
TFields extends object,
|
|
74
|
-
TOmitInitKey extends PropertyKey,
|
|
75
|
-
TExtra extends object,
|
|
76
|
-
TPostDeserialized,
|
|
77
|
-
> {
|
|
78
|
-
/**
|
|
79
|
-
* Append a fixed-length array buffer like field to the `Struct`
|
|
80
|
-
*
|
|
81
|
-
* @param name Name of the field
|
|
82
|
-
* @param type `Array.SubType.ArrayBuffer` or `Array.SubType.String`
|
|
83
|
-
* @param options Fixed-length array options
|
|
84
|
-
* @param typeScriptType Type of the field in TypeScript.
|
|
85
|
-
* For example, if this field is a string, you can declare it as a string enum or literal union.
|
|
86
|
-
*/
|
|
87
|
-
<
|
|
88
|
-
TName extends PropertyKey,
|
|
89
|
-
TType extends BufferFieldConverter<unknown, unknown>,
|
|
90
|
-
TTypeScriptType = TType["TTypeScriptType"],
|
|
91
|
-
>(
|
|
92
|
-
name: TName,
|
|
93
|
-
type: TType,
|
|
94
|
-
options: FixedLengthBufferLikeFieldOptions,
|
|
95
|
-
typeScriptType?: TTypeScriptType,
|
|
96
|
-
): AddFieldDescriptor<
|
|
97
|
-
TFields,
|
|
98
|
-
TOmitInitKey,
|
|
99
|
-
TExtra,
|
|
100
|
-
TPostDeserialized,
|
|
101
|
-
TName,
|
|
102
|
-
FixedLengthBufferLikeFieldDefinition<
|
|
103
|
-
TType,
|
|
104
|
-
FixedLengthBufferLikeFieldOptions
|
|
105
|
-
>
|
|
106
|
-
>;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Append a variable-length array buffer like field to the `Struct`
|
|
110
|
-
*/
|
|
111
|
-
<
|
|
112
|
-
TName extends PropertyKey,
|
|
113
|
-
TType extends BufferFieldConverter<unknown, unknown>,
|
|
114
|
-
TOptions extends VariableLengthBufferLikeFieldOptions<TFields>,
|
|
115
|
-
TTypeScriptType = TType["TTypeScriptType"],
|
|
116
|
-
>(
|
|
117
|
-
name: TName,
|
|
118
|
-
type: TType,
|
|
119
|
-
options: TOptions,
|
|
120
|
-
typeScriptType?: TTypeScriptType,
|
|
121
|
-
): AddFieldDescriptor<
|
|
122
|
-
TFields,
|
|
123
|
-
TOmitInitKey,
|
|
124
|
-
TExtra,
|
|
125
|
-
TPostDeserialized,
|
|
126
|
-
TName,
|
|
127
|
-
VariableLengthBufferLikeFieldDefinition<TType, TOptions>
|
|
128
|
-
>;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Similar to `ArrayBufferLikeFieldCreator`, but bind to `TType`
|
|
133
|
-
*/
|
|
134
|
-
interface BoundArrayBufferLikeFieldDefinitionCreator<
|
|
135
|
-
TFields extends object,
|
|
136
|
-
TOmitInitKey extends PropertyKey,
|
|
137
|
-
TExtra extends object,
|
|
138
|
-
TPostDeserialized,
|
|
139
|
-
TType extends BufferFieldConverter<unknown, unknown>,
|
|
140
|
-
> {
|
|
141
|
-
<TName extends PropertyKey, TTypeScriptType = TType["TTypeScriptType"]>(
|
|
142
|
-
name: TName,
|
|
143
|
-
options: FixedLengthBufferLikeFieldOptions,
|
|
144
|
-
typeScriptType?: TTypeScriptType,
|
|
145
|
-
): AddFieldDescriptor<
|
|
146
|
-
TFields,
|
|
147
|
-
TOmitInitKey,
|
|
148
|
-
TExtra,
|
|
149
|
-
TPostDeserialized,
|
|
150
|
-
TName,
|
|
151
|
-
FixedLengthBufferLikeFieldDefinition<
|
|
152
|
-
TType,
|
|
153
|
-
FixedLengthBufferLikeFieldOptions,
|
|
154
|
-
TTypeScriptType
|
|
155
|
-
>
|
|
156
|
-
>;
|
|
157
|
-
|
|
158
|
-
<
|
|
159
|
-
TName extends PropertyKey,
|
|
160
|
-
TOptions extends VariableLengthBufferLikeFieldOptions<
|
|
161
|
-
TFields,
|
|
162
|
-
LengthField<TFields>
|
|
163
|
-
>,
|
|
164
|
-
TTypeScriptType = TType["TTypeScriptType"],
|
|
165
|
-
>(
|
|
166
|
-
name: TName,
|
|
167
|
-
options: TOptions,
|
|
168
|
-
typeScriptType?: TTypeScriptType,
|
|
169
|
-
): AddFieldDescriptor<
|
|
170
|
-
TFields,
|
|
171
|
-
TOmitInitKey,
|
|
172
|
-
TExtra,
|
|
173
|
-
TPostDeserialized,
|
|
174
|
-
TName,
|
|
175
|
-
VariableLengthBufferLikeFieldDefinition<
|
|
176
|
-
TType,
|
|
177
|
-
TOptions,
|
|
178
|
-
TTypeScriptType
|
|
179
|
-
>
|
|
180
|
-
>;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export type StructPostDeserialized<TFields, TPostDeserialized> = (
|
|
184
|
-
this: TFields,
|
|
185
|
-
object: TFields,
|
|
186
|
-
) => TPostDeserialized;
|
|
187
|
-
|
|
188
|
-
export type StructDeserializedResult<
|
|
189
|
-
TFields extends object,
|
|
190
|
-
TExtra extends object,
|
|
191
|
-
TPostDeserialized,
|
|
192
|
-
> = TPostDeserialized extends undefined
|
|
193
|
-
? Overwrite<TExtra, TFields>
|
|
194
|
-
: TPostDeserialized;
|
|
195
|
-
|
|
196
45
|
export class StructDeserializeError extends Error {
|
|
197
46
|
constructor(message: string) {
|
|
198
47
|
super(message);
|
|
199
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
200
48
|
}
|
|
201
49
|
}
|
|
202
50
|
|
|
@@ -214,497 +62,179 @@ export class StructEmptyError extends StructDeserializeError {
|
|
|
214
62
|
}
|
|
215
63
|
}
|
|
216
64
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
65
|
+
export type ExtraToIntersection<
|
|
66
|
+
Extra extends Record<PropertyKey, unknown> | undefined,
|
|
67
|
+
> = Extra extends undefined ? unknown : Extra;
|
|
68
|
+
|
|
69
|
+
export interface Struct<
|
|
70
|
+
Fields extends StructFields,
|
|
71
|
+
Extra extends Record<PropertyKey, unknown> | undefined = undefined,
|
|
72
|
+
PostDeserialize = FieldsValue<Fields> & Extra,
|
|
73
|
+
> extends StructSerializer<FieldsInit<Fields>>,
|
|
74
|
+
StructDeserializer<PostDeserialize> {
|
|
75
|
+
littleEndian: boolean;
|
|
76
|
+
fields: Fields;
|
|
77
|
+
extra: Extra;
|
|
229
78
|
}
|
|
230
79
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
constructor(options?: Partial<Readonly<StructOptions>>) {
|
|
281
|
-
this.options = { ...StructDefaultOptions, ...options };
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Appends a `StructFieldDefinition` to the `Struct
|
|
286
|
-
*/
|
|
287
|
-
field<
|
|
288
|
-
TName extends PropertyKey,
|
|
289
|
-
TDefinition extends StructFieldDefinition<
|
|
290
|
-
unknown,
|
|
291
|
-
unknown,
|
|
292
|
-
PropertyKey
|
|
293
|
-
>,
|
|
294
|
-
>(
|
|
295
|
-
name: TName,
|
|
296
|
-
definition: TDefinition,
|
|
297
|
-
): AddFieldDescriptor<
|
|
298
|
-
TFields,
|
|
299
|
-
TOmitInitKey,
|
|
300
|
-
TExtra,
|
|
301
|
-
TPostDeserialized,
|
|
302
|
-
TName,
|
|
303
|
-
TDefinition
|
|
304
|
-
> {
|
|
305
|
-
for (const field of this.#fields) {
|
|
306
|
-
if (field[0] === name) {
|
|
307
|
-
// Convert Symbol to string
|
|
308
|
-
const nameString = String(name);
|
|
309
|
-
throw new Error(
|
|
310
|
-
`This struct already have a field with name '${nameString}'`,
|
|
311
|
-
);
|
|
80
|
+
/* #__NO_SIDE_EFFECTS__ */
|
|
81
|
+
export function struct<
|
|
82
|
+
Fields extends Record<
|
|
83
|
+
string,
|
|
84
|
+
| Field<unknown, string, Partial<FieldsValue<Fields>>, unknown>
|
|
85
|
+
| (StructSerializer<unknown> & StructDeserializer<unknown>)
|
|
86
|
+
>,
|
|
87
|
+
Extra extends Record<PropertyKey, unknown> | undefined = undefined,
|
|
88
|
+
PostDeserialize = FieldsValue<Fields> & ExtraToIntersection<Extra>,
|
|
89
|
+
>(
|
|
90
|
+
fields: Fields,
|
|
91
|
+
options: {
|
|
92
|
+
littleEndian: boolean;
|
|
93
|
+
extra?: (Extra & ThisType<FieldsValue<Fields>>) | undefined;
|
|
94
|
+
postDeserialize?:
|
|
95
|
+
| ((
|
|
96
|
+
this: FieldsValue<Fields> & ExtraToIntersection<Extra>,
|
|
97
|
+
value: FieldsValue<Fields> & ExtraToIntersection<Extra>,
|
|
98
|
+
) => PostDeserialize)
|
|
99
|
+
| undefined;
|
|
100
|
+
},
|
|
101
|
+
): Struct<Fields, Extra, PostDeserialize> {
|
|
102
|
+
const fieldList = Object.entries(fields);
|
|
103
|
+
const size = fieldList.reduce((sum, [, field]) => sum + field.size, 0);
|
|
104
|
+
|
|
105
|
+
const littleEndian = options.littleEndian;
|
|
106
|
+
const extra = options.extra
|
|
107
|
+
? Object.getOwnPropertyDescriptors(options.extra)
|
|
108
|
+
: undefined;
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
littleEndian,
|
|
112
|
+
type: "byob",
|
|
113
|
+
fields,
|
|
114
|
+
size,
|
|
115
|
+
extra: options.extra,
|
|
116
|
+
serialize(
|
|
117
|
+
source: FieldsInit<Fields>,
|
|
118
|
+
bufferOrContext?: Uint8Array | StructSerializeContext,
|
|
119
|
+
): Uint8Array | number {
|
|
120
|
+
const temp: Record<string, unknown> = { ...source };
|
|
121
|
+
|
|
122
|
+
for (const [key, field] of fieldList) {
|
|
123
|
+
if (key in temp && "init" in field) {
|
|
124
|
+
const result = field.init?.(temp[key], temp as never);
|
|
125
|
+
if (result !== undefined) {
|
|
126
|
+
temp[key] = result;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
312
129
|
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
this.#fields.push([name, definition]);
|
|
316
|
-
|
|
317
|
-
const size = definition.getSize();
|
|
318
|
-
this.#size += size;
|
|
319
|
-
|
|
320
|
-
// Force cast `this` to another type
|
|
321
|
-
return this as never;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Merges (flats) another `Struct`'s fields and extra fields into this one.
|
|
326
|
-
*
|
|
327
|
-
* `other`'s `postDeserialize` will be ignored.
|
|
328
|
-
*/
|
|
329
|
-
concat<TOther extends StructDefinition<object, PropertyKey, object>>(
|
|
330
|
-
other: TOther,
|
|
331
|
-
): Struct<
|
|
332
|
-
TFields & TOther["TFields"],
|
|
333
|
-
TOmitInitKey | TOther["TOmitInitKey"],
|
|
334
|
-
TExtra & TOther["TExtra"],
|
|
335
|
-
TPostDeserialized
|
|
336
|
-
> {
|
|
337
|
-
if (!(other instanceof Struct)) {
|
|
338
|
-
throw new TypeError("The other value must be a `Struct` instance");
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
for (const field of other.#fields) {
|
|
342
|
-
this.#fields.push(field);
|
|
343
|
-
}
|
|
344
|
-
this.#size += other.#size;
|
|
345
|
-
Object.defineProperties(
|
|
346
|
-
this.#extra,
|
|
347
|
-
Object.getOwnPropertyDescriptors(other.#extra),
|
|
348
|
-
);
|
|
349
|
-
return this as never;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
#number<
|
|
353
|
-
TName extends PropertyKey,
|
|
354
|
-
TType extends NumberFieldVariant = NumberFieldVariant,
|
|
355
|
-
TTypeScriptType = number,
|
|
356
|
-
>(name: TName, type: TType, typeScriptType?: TTypeScriptType) {
|
|
357
|
-
return this.field(
|
|
358
|
-
name,
|
|
359
|
-
new NumberFieldDefinition(type, typeScriptType),
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Appends an `int8` field to the `Struct`
|
|
365
|
-
*/
|
|
366
|
-
int8<TName extends PropertyKey, TTypeScriptType = number>(
|
|
367
|
-
name: TName,
|
|
368
|
-
typeScriptType?: TTypeScriptType,
|
|
369
|
-
) {
|
|
370
|
-
return this.#number(name, NumberFieldVariant.Int8, typeScriptType);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Appends an `uint8` field to the `Struct`
|
|
375
|
-
*/
|
|
376
|
-
uint8<TName extends PropertyKey, TTypeScriptType = number>(
|
|
377
|
-
name: TName,
|
|
378
|
-
typeScriptType?: TTypeScriptType,
|
|
379
|
-
) {
|
|
380
|
-
return this.#number(name, NumberFieldVariant.Uint8, typeScriptType);
|
|
381
|
-
}
|
|
382
130
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Appends an `uint16` field to the `Struct`
|
|
395
|
-
*/
|
|
396
|
-
uint16<TName extends PropertyKey, TTypeScriptType = number>(
|
|
397
|
-
name: TName,
|
|
398
|
-
typeScriptType?: TTypeScriptType,
|
|
399
|
-
) {
|
|
400
|
-
return this.#number(name, NumberFieldVariant.Uint16, typeScriptType);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Appends an `int32` field to the `Struct`
|
|
405
|
-
*/
|
|
406
|
-
int32<TName extends PropertyKey, TTypeScriptType = number>(
|
|
407
|
-
name: TName,
|
|
408
|
-
typeScriptType?: TTypeScriptType,
|
|
409
|
-
) {
|
|
410
|
-
return this.#number(name, NumberFieldVariant.Int32, typeScriptType);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Appends an `uint32` field to the `Struct`
|
|
415
|
-
*/
|
|
416
|
-
uint32<TName extends PropertyKey, TTypeScriptType = number>(
|
|
417
|
-
name: TName,
|
|
418
|
-
typeScriptType?: TTypeScriptType,
|
|
419
|
-
) {
|
|
420
|
-
return this.#number(name, NumberFieldVariant.Uint32, typeScriptType);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
#bigint<
|
|
424
|
-
TName extends PropertyKey,
|
|
425
|
-
TType extends BigIntFieldVariant = BigIntFieldVariant,
|
|
426
|
-
TTypeScriptType = TType["TTypeScriptType"],
|
|
427
|
-
>(name: TName, type: TType, typeScriptType?: TTypeScriptType) {
|
|
428
|
-
return this.field(
|
|
429
|
-
name,
|
|
430
|
-
new BigIntFieldDefinition(type, typeScriptType),
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Appends an `int64` field to the `Struct`
|
|
436
|
-
*
|
|
437
|
-
* Requires native `BigInt` support
|
|
438
|
-
*/
|
|
439
|
-
int64<
|
|
440
|
-
TName extends PropertyKey,
|
|
441
|
-
TTypeScriptType = BigIntFieldVariant["TTypeScriptType"],
|
|
442
|
-
>(name: TName, typeScriptType?: TTypeScriptType) {
|
|
443
|
-
return this.#bigint(name, BigIntFieldVariant.Int64, typeScriptType);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Appends an `uint64` field to the `Struct`
|
|
448
|
-
*
|
|
449
|
-
* Requires native `BigInt` support
|
|
450
|
-
*/
|
|
451
|
-
uint64<
|
|
452
|
-
TName extends PropertyKey,
|
|
453
|
-
TTypeScriptType = BigIntFieldVariant["TTypeScriptType"],
|
|
454
|
-
>(name: TName, typeScriptType?: TTypeScriptType) {
|
|
455
|
-
return this.#bigint(name, BigIntFieldVariant.Uint64, typeScriptType);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
#arrayBufferLike: ArrayBufferLikeFieldCreator<
|
|
459
|
-
TFields,
|
|
460
|
-
TOmitInitKey,
|
|
461
|
-
TExtra,
|
|
462
|
-
TPostDeserialized
|
|
463
|
-
> = (
|
|
464
|
-
name: PropertyKey,
|
|
465
|
-
type: BufferFieldConverter,
|
|
466
|
-
options:
|
|
467
|
-
| FixedLengthBufferLikeFieldOptions
|
|
468
|
-
| VariableLengthBufferLikeFieldOptions,
|
|
469
|
-
): never => {
|
|
470
|
-
if ("length" in options) {
|
|
471
|
-
return this.field(
|
|
472
|
-
name,
|
|
473
|
-
new FixedLengthBufferLikeFieldDefinition(type, options),
|
|
474
|
-
) as never;
|
|
475
|
-
} else {
|
|
476
|
-
return this.field(
|
|
477
|
-
name,
|
|
478
|
-
new VariableLengthBufferLikeFieldDefinition(type, options),
|
|
479
|
-
) as never;
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
uint8Array: BoundArrayBufferLikeFieldDefinitionCreator<
|
|
484
|
-
TFields,
|
|
485
|
-
TOmitInitKey,
|
|
486
|
-
TExtra,
|
|
487
|
-
TPostDeserialized,
|
|
488
|
-
Uint8ArrayBufferFieldConverter
|
|
489
|
-
> = (
|
|
490
|
-
name: PropertyKey,
|
|
491
|
-
options: unknown,
|
|
492
|
-
typeScriptType: unknown,
|
|
493
|
-
): never => {
|
|
494
|
-
return this.#arrayBufferLike(
|
|
495
|
-
name,
|
|
496
|
-
Uint8ArrayBufferFieldConverter.Instance,
|
|
497
|
-
options as never,
|
|
498
|
-
typeScriptType,
|
|
499
|
-
) as never;
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
string: BoundArrayBufferLikeFieldDefinitionCreator<
|
|
503
|
-
TFields,
|
|
504
|
-
TOmitInitKey,
|
|
505
|
-
TExtra,
|
|
506
|
-
TPostDeserialized,
|
|
507
|
-
StringBufferFieldConverter
|
|
508
|
-
> = (
|
|
509
|
-
name: PropertyKey,
|
|
510
|
-
options: unknown,
|
|
511
|
-
typeScriptType: unknown,
|
|
512
|
-
): never => {
|
|
513
|
-
return this.#arrayBufferLike(
|
|
514
|
-
name,
|
|
515
|
-
StringBufferFieldConverter.Instance,
|
|
516
|
-
options as never,
|
|
517
|
-
typeScriptType,
|
|
518
|
-
) as never;
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* Adds some extra properties into every `Struct` value.
|
|
523
|
-
*
|
|
524
|
-
* Extra properties will not affect serialize or deserialize process.
|
|
525
|
-
*
|
|
526
|
-
* Multiple calls to `extra` will merge all properties together.
|
|
527
|
-
*
|
|
528
|
-
* @param value
|
|
529
|
-
* An object containing properties to be added to the result value. Accessors and methods are also allowed.
|
|
530
|
-
*/
|
|
531
|
-
extra<
|
|
532
|
-
T extends Record<
|
|
533
|
-
// This trick disallows any keys that are already in `TValue`
|
|
534
|
-
Exclude<keyof T, Exclude<keyof T, keyof TFields>>,
|
|
535
|
-
never
|
|
536
|
-
>,
|
|
537
|
-
>(
|
|
538
|
-
value: T & ThisType<Overwrite<Overwrite<TExtra, T>, TFields>>,
|
|
539
|
-
): Struct<TFields, TOmitInitKey, Overwrite<TExtra, T>, TPostDeserialized> {
|
|
540
|
-
Object.defineProperties(
|
|
541
|
-
this.#extra,
|
|
542
|
-
Object.getOwnPropertyDescriptors(value),
|
|
543
|
-
);
|
|
544
|
-
return this as never;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Registers (or replaces) a custom callback to be run after deserialized.
|
|
549
|
-
*
|
|
550
|
-
* A callback returning `never` (always throw an error)
|
|
551
|
-
* will also change the return type of `deserialize` to `never`.
|
|
552
|
-
*/
|
|
553
|
-
postDeserialize(
|
|
554
|
-
callback: StructPostDeserialized<TFields, never>,
|
|
555
|
-
): Struct<TFields, TOmitInitKey, TExtra, never>;
|
|
556
|
-
/**
|
|
557
|
-
* Registers (or replaces) a custom callback to be run after deserialized.
|
|
558
|
-
*
|
|
559
|
-
* A callback returning `void` means it modify the result object in-place
|
|
560
|
-
* (or doesn't modify it at all), so `deserialize` will still return the result object.
|
|
561
|
-
*/
|
|
562
|
-
postDeserialize(
|
|
563
|
-
callback?: StructPostDeserialized<TFields, void>,
|
|
564
|
-
): Struct<TFields, TOmitInitKey, TExtra, undefined>;
|
|
565
|
-
/**
|
|
566
|
-
* Registers (or replaces) a custom callback to be run after deserialized.
|
|
567
|
-
*
|
|
568
|
-
* A callback returning anything other than `undefined`
|
|
569
|
-
* will `deserialize` to return that object instead.
|
|
570
|
-
*/
|
|
571
|
-
postDeserialize<TPostSerialize>(
|
|
572
|
-
callback?: StructPostDeserialized<TFields, TPostSerialize>,
|
|
573
|
-
): Struct<TFields, TOmitInitKey, TExtra, TPostSerialize>;
|
|
574
|
-
postDeserialize(callback?: StructPostDeserialized<TFields, unknown>) {
|
|
575
|
-
this.#postDeserialized = callback;
|
|
576
|
-
return this as never;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* Deserialize a struct value from `stream`.
|
|
581
|
-
*/
|
|
582
|
-
deserialize(
|
|
583
|
-
stream: ExactReadable,
|
|
584
|
-
): StructDeserializedResult<TFields, TExtra, TPostDeserialized>;
|
|
585
|
-
deserialize(
|
|
586
|
-
stream: AsyncExactReadable,
|
|
587
|
-
): Promise<StructDeserializedResult<TFields, TExtra, TPostDeserialized>>;
|
|
588
|
-
deserialize(
|
|
589
|
-
stream: ExactReadable | AsyncExactReadable,
|
|
590
|
-
): ValueOrPromise<
|
|
591
|
-
StructDeserializedResult<TFields, TExtra, TPostDeserialized>
|
|
592
|
-
> {
|
|
593
|
-
const structValue = new StructValue(this.#extra);
|
|
594
|
-
|
|
595
|
-
let promise = SyncPromise.resolve();
|
|
596
|
-
|
|
597
|
-
const startPosition = stream.position;
|
|
598
|
-
for (const [name, definition] of this.#fields) {
|
|
599
|
-
promise = promise
|
|
600
|
-
.then(() =>
|
|
601
|
-
definition.deserialize(this.options, stream, structValue),
|
|
602
|
-
)
|
|
603
|
-
.then(
|
|
604
|
-
(fieldValue) => {
|
|
605
|
-
structValue.set(name, fieldValue);
|
|
606
|
-
},
|
|
607
|
-
(e) => {
|
|
608
|
-
if (!(e instanceof ExactReadableEndedError)) {
|
|
609
|
-
throw e;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
if (stream.position === startPosition) {
|
|
613
|
-
throw new StructEmptyError();
|
|
614
|
-
} else {
|
|
615
|
-
throw new StructNotEnoughDataError();
|
|
616
|
-
}
|
|
617
|
-
},
|
|
618
|
-
);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
return promise
|
|
622
|
-
.then(() => {
|
|
623
|
-
const value = structValue.value;
|
|
624
|
-
|
|
625
|
-
// Run `postDeserialized`
|
|
626
|
-
if (this.#postDeserialized) {
|
|
627
|
-
const override = this.#postDeserialized.call(
|
|
628
|
-
value as never,
|
|
629
|
-
value as never,
|
|
630
|
-
);
|
|
631
|
-
// If it returns a new value, use that as result
|
|
632
|
-
// Otherwise it only inspects/mutates the object in place.
|
|
633
|
-
if (override !== undefined) {
|
|
634
|
-
return override as never;
|
|
131
|
+
const sizes = new Array<number>(fieldList.length);
|
|
132
|
+
const buffers = new Array<Uint8Array | undefined>(fieldList.length);
|
|
133
|
+
{
|
|
134
|
+
const context: FieldDefaultSerializeContext = { littleEndian };
|
|
135
|
+
for (const [index, [key, field]] of fieldList.entries()) {
|
|
136
|
+
if (field.type === "byob") {
|
|
137
|
+
sizes[index] = field.size;
|
|
138
|
+
} else {
|
|
139
|
+
buffers[index] = field.serialize(temp[key], context);
|
|
140
|
+
sizes[index] = buffers[index].length;
|
|
635
141
|
}
|
|
636
142
|
}
|
|
143
|
+
}
|
|
637
144
|
|
|
638
|
-
|
|
639
|
-
})
|
|
640
|
-
.valueOrPromise();
|
|
641
|
-
}
|
|
145
|
+
const size = sizes.reduce((sum, size) => sum + size, 0);
|
|
642
146
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
serialize(
|
|
650
|
-
init: Evaluate<Omit<TFields, TOmitInitKey>>,
|
|
651
|
-
output?: Uint8Array,
|
|
652
|
-
): Uint8Array {
|
|
653
|
-
let structValue: StructValue;
|
|
654
|
-
if (isStructValueInit(init)) {
|
|
655
|
-
structValue = init[STRUCT_VALUE_SYMBOL];
|
|
656
|
-
for (const [key, value] of Object.entries(init)) {
|
|
657
|
-
const fieldValue = structValue.get(key);
|
|
658
|
-
if (fieldValue) {
|
|
659
|
-
fieldValue.set(value);
|
|
147
|
+
let externalBuffer: boolean;
|
|
148
|
+
let buffer: Uint8Array;
|
|
149
|
+
let index: number;
|
|
150
|
+
if (bufferOrContext instanceof Uint8Array) {
|
|
151
|
+
if (bufferOrContext.length < size) {
|
|
152
|
+
throw new Error("Buffer too small");
|
|
660
153
|
}
|
|
154
|
+
externalBuffer = true;
|
|
155
|
+
buffer = bufferOrContext;
|
|
156
|
+
index = 0;
|
|
157
|
+
} else if (
|
|
158
|
+
typeof bufferOrContext === "object" &&
|
|
159
|
+
"buffer" in bufferOrContext
|
|
160
|
+
) {
|
|
161
|
+
externalBuffer = true;
|
|
162
|
+
buffer = bufferOrContext.buffer;
|
|
163
|
+
index = bufferOrContext.index ?? 0;
|
|
164
|
+
if (buffer.length - index < size) {
|
|
165
|
+
throw new Error("Buffer too small");
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
externalBuffer = false;
|
|
169
|
+
buffer = new Uint8Array(size);
|
|
170
|
+
index = 0;
|
|
661
171
|
}
|
|
662
|
-
} else {
|
|
663
|
-
structValue = new StructValue({});
|
|
664
|
-
for (const [name, definition] of this.#fields) {
|
|
665
|
-
const fieldValue = definition.create(
|
|
666
|
-
this.options,
|
|
667
|
-
structValue,
|
|
668
|
-
(init as Record<PropertyKey, unknown>)[name],
|
|
669
|
-
);
|
|
670
|
-
structValue.set(name, fieldValue);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
172
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
173
|
+
const context = {
|
|
174
|
+
buffer,
|
|
175
|
+
index,
|
|
176
|
+
littleEndian,
|
|
177
|
+
} satisfies FieldByobSerializeContext;
|
|
178
|
+
for (const [index, [key, field]] of fieldList.entries()) {
|
|
179
|
+
if (buffers[index]) {
|
|
180
|
+
buffer.set(buffers[index], context.index);
|
|
181
|
+
} else {
|
|
182
|
+
field.serialize(temp[key], context);
|
|
183
|
+
}
|
|
184
|
+
context.index += sizes[index]!;
|
|
185
|
+
}
|
|
679
186
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
187
|
+
if (externalBuffer) {
|
|
188
|
+
return size;
|
|
189
|
+
} else {
|
|
190
|
+
return buffer;
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
deserialize: bipedal(function* (
|
|
194
|
+
this: Struct<Fields, Extra, PostDeserialize>,
|
|
195
|
+
then,
|
|
196
|
+
reader: AsyncExactReadable,
|
|
197
|
+
) {
|
|
198
|
+
const startPosition = reader.position;
|
|
199
|
+
|
|
200
|
+
const result = {} as Record<string, unknown>;
|
|
201
|
+
const context: FieldDeserializeContext<
|
|
202
|
+
Partial<FieldsValue<Fields>>
|
|
203
|
+
> = {
|
|
204
|
+
dependencies: result as never,
|
|
205
|
+
littleEndian: littleEndian,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
for (const [key, field] of fieldList) {
|
|
210
|
+
result[key] = yield* then(
|
|
211
|
+
field.deserialize(reader, context),
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
} catch (e) {
|
|
215
|
+
if (!(e instanceof ExactReadableEndedError)) {
|
|
216
|
+
throw e;
|
|
217
|
+
}
|
|
686
218
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
219
|
+
if (reader.position === startPosition) {
|
|
220
|
+
throw new StructEmptyError();
|
|
221
|
+
} else {
|
|
222
|
+
throw new StructNotEnoughDataError();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
692
225
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
output.byteLength,
|
|
697
|
-
);
|
|
698
|
-
let offset = 0;
|
|
699
|
-
for (const { fieldValue, size } of fieldsInfo) {
|
|
700
|
-
fieldValue.serialize(dataView, output, offset);
|
|
701
|
-
offset += size;
|
|
702
|
-
}
|
|
226
|
+
if (extra) {
|
|
227
|
+
Object.defineProperties(result, extra);
|
|
228
|
+
}
|
|
703
229
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
230
|
+
if (options.postDeserialize) {
|
|
231
|
+
return options.postDeserialize.call(
|
|
232
|
+
result as never,
|
|
233
|
+
result as never,
|
|
234
|
+
);
|
|
235
|
+
} else {
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
} as never;
|
|
710
240
|
}
|