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