@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.
Files changed (150) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/LICENSE +1 -1
  3. package/README.md +85 -714
  4. package/esm/bipedal.d.ts +6 -0
  5. package/esm/bipedal.d.ts.map +1 -0
  6. package/esm/bipedal.js +38 -0
  7. package/esm/bipedal.js.map +1 -0
  8. package/esm/buffer.d.ts +30 -0
  9. package/esm/buffer.d.ts.map +1 -0
  10. package/esm/buffer.js +170 -0
  11. package/esm/buffer.js.map +1 -0
  12. package/esm/concat.d.ts +13 -0
  13. package/esm/concat.d.ts.map +1 -0
  14. package/esm/concat.js +10 -0
  15. package/esm/concat.js.map +1 -0
  16. package/esm/extend.d.ts +6 -0
  17. package/esm/extend.d.ts.map +1 -0
  18. package/esm/extend.js +10 -0
  19. package/esm/extend.js.map +1 -0
  20. package/esm/field/factory.d.ts +13 -0
  21. package/esm/field/factory.d.ts.map +1 -0
  22. package/esm/field/factory.js +21 -0
  23. package/esm/field/factory.js.map +1 -0
  24. package/esm/field/index.d.ts +4 -0
  25. package/esm/field/index.d.ts.map +1 -0
  26. package/esm/field/index.js +4 -0
  27. package/esm/field/index.js.map +1 -0
  28. package/esm/field/serialize.d.ts +8 -0
  29. package/esm/field/serialize.d.ts.map +1 -0
  30. package/esm/field/serialize.js +33 -0
  31. package/esm/field/serialize.js.map +1 -0
  32. package/esm/field/types.d.ts +33 -0
  33. package/esm/field/types.d.ts.map +1 -0
  34. package/esm/field/types.js +2 -0
  35. package/esm/field/types.js.map +1 -0
  36. package/esm/index.d.ts +9 -4
  37. package/esm/index.d.ts.map +1 -1
  38. package/esm/index.js +9 -4
  39. package/esm/index.js.map +1 -1
  40. package/esm/number.d.ts +13 -0
  41. package/esm/number.d.ts.map +1 -0
  42. package/esm/number.js +57 -0
  43. package/esm/number.js.map +1 -0
  44. package/esm/{basic/stream.d.ts → readable.d.ts} +9 -3
  45. package/esm/readable.d.ts.map +1 -0
  46. package/esm/readable.js +26 -0
  47. package/esm/readable.js.map +1 -0
  48. package/esm/string.d.ts +16 -0
  49. package/esm/string.d.ts.map +1 -0
  50. package/esm/string.js +13 -0
  51. package/esm/string.js.map +1 -0
  52. package/esm/struct.d.ts +22 -140
  53. package/esm/struct.d.ts.map +1 -1
  54. package/esm/struct.js +109 -227
  55. package/esm/struct.js.map +1 -1
  56. package/esm/types.d.ts +20 -0
  57. package/esm/types.d.ts.map +1 -0
  58. package/esm/types.js +2 -0
  59. package/esm/types.js.map +1 -0
  60. package/esm/utils.d.ts +10 -45
  61. package/esm/utils.d.ts.map +1 -1
  62. package/esm/utils.js +8 -9
  63. package/esm/utils.js.map +1 -1
  64. package/package.json +11 -12
  65. package/src/bipedal.ts +65 -0
  66. package/src/buffer.ts +334 -0
  67. package/src/concat.ts +78 -0
  68. package/src/extend.ts +37 -0
  69. package/src/field/factory.ts +64 -0
  70. package/src/field/index.ts +3 -0
  71. package/src/field/serialize.ts +58 -0
  72. package/src/field/types.ts +48 -0
  73. package/src/index.ts +9 -4
  74. package/src/number.ts +134 -0
  75. package/src/{basic/stream.ts → readable.ts} +31 -4
  76. package/src/string.ts +41 -0
  77. package/src/struct.ts +200 -670
  78. package/src/types.ts +37 -0
  79. package/src/utils.ts +14 -67
  80. package/tsconfig.build.json +6 -1
  81. package/tsconfig.build.tsbuildinfo +1 -1
  82. package/CHANGELOG.json +0 -173
  83. package/esm/basic/definition.d.ts +0 -44
  84. package/esm/basic/definition.d.ts.map +0 -1
  85. package/esm/basic/definition.js +0 -24
  86. package/esm/basic/definition.js.map +0 -1
  87. package/esm/basic/field-value.d.ts +0 -39
  88. package/esm/basic/field-value.d.ts.map +0 -1
  89. package/esm/basic/field-value.js +0 -46
  90. package/esm/basic/field-value.js.map +0 -1
  91. package/esm/basic/index.d.ts +0 -6
  92. package/esm/basic/index.d.ts.map +0 -1
  93. package/esm/basic/index.js +0 -6
  94. package/esm/basic/index.js.map +0 -1
  95. package/esm/basic/options.d.ts +0 -10
  96. package/esm/basic/options.d.ts.map +0 -1
  97. package/esm/basic/options.js +0 -4
  98. package/esm/basic/options.js.map +0 -1
  99. package/esm/basic/stream.d.ts.map +0 -1
  100. package/esm/basic/stream.js +0 -8
  101. package/esm/basic/stream.js.map +0 -1
  102. package/esm/basic/struct-value.d.ts +0 -30
  103. package/esm/basic/struct-value.d.ts.map +0 -1
  104. package/esm/basic/struct-value.js +0 -62
  105. package/esm/basic/struct-value.js.map +0 -1
  106. package/esm/sync-promise.d.ts +0 -13
  107. package/esm/sync-promise.d.ts.map +0 -1
  108. package/esm/sync-promise.js +0 -71
  109. package/esm/sync-promise.js.map +0 -1
  110. package/esm/types/bigint.d.ts +0 -25
  111. package/esm/types/bigint.d.ts.map +0 -1
  112. package/esm/types/bigint.js +0 -46
  113. package/esm/types/bigint.js.map +0 -1
  114. package/esm/types/buffer/base.d.ts +0 -69
  115. package/esm/types/buffer/base.d.ts.map +0 -1
  116. package/esm/types/buffer/base.js +0 -100
  117. package/esm/types/buffer/base.js.map +0 -1
  118. package/esm/types/buffer/fixed-length.d.ts +0 -9
  119. package/esm/types/buffer/fixed-length.d.ts.map +0 -1
  120. package/esm/types/buffer/fixed-length.js +0 -7
  121. package/esm/types/buffer/fixed-length.js.map +0 -1
  122. package/esm/types/buffer/index.d.ts +0 -4
  123. package/esm/types/buffer/index.d.ts.map +0 -1
  124. package/esm/types/buffer/index.js +0 -4
  125. package/esm/types/buffer/index.js.map +0 -1
  126. package/esm/types/buffer/variable-length.d.ts +0 -46
  127. package/esm/types/buffer/variable-length.d.ts.map +0 -1
  128. package/esm/types/buffer/variable-length.js +0 -87
  129. package/esm/types/buffer/variable-length.js.map +0 -1
  130. package/esm/types/index.d.ts +0 -4
  131. package/esm/types/index.d.ts.map +0 -1
  132. package/esm/types/index.js +0 -4
  133. package/esm/types/index.js.map +0 -1
  134. package/esm/types/number.d.ts +0 -28
  135. package/esm/types/number.d.ts.map +0 -1
  136. package/esm/types/number.js +0 -99
  137. package/esm/types/number.js.map +0 -1
  138. package/src/basic/definition.ts +0 -68
  139. package/src/basic/field-value.ts +0 -75
  140. package/src/basic/index.ts +0 -5
  141. package/src/basic/options.ts +0 -19
  142. package/src/basic/struct-value.ts +0 -85
  143. package/src/sync-promise.ts +0 -134
  144. package/src/types/bigint.ts +0 -124
  145. package/src/types/buffer/base.ts +0 -198
  146. package/src/types/buffer/fixed-length.ts +0 -22
  147. package/src/types/buffer/index.ts +0 -3
  148. package/src/types/buffer/variable-length.ts +0 -198
  149. package/src/types/index.ts +0 -3
  150. package/src/types/number.ts +0 -166
package/src/bipedal.ts ADDED
@@ -0,0 +1,65 @@
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 type BipedalGenerator<This, T, A extends unknown[]> = (
24
+ this: This,
25
+ then: <U>(value: MaybePromiseLike<U>) => Iterable<unknown, U, unknown>,
26
+ ...args: A
27
+ ) => Generator<unknown, T, unknown>;
28
+
29
+ /* #__NO_SIDE_EFFECTS__ */
30
+ export function bipedal<This, T, A extends unknown[]>(
31
+ fn: BipedalGenerator<This, T, A>,
32
+ bindThis?: This,
33
+ ): { (this: This, ...args: A): MaybePromiseLike<T> } {
34
+ function result(this: This, ...args: A): MaybePromiseLike<T> {
35
+ const iterator = fn.call(
36
+ this,
37
+ function* <U>(
38
+ value: MaybePromiseLike<U>,
39
+ ): Generator<
40
+ PromiseLike<U>,
41
+ U,
42
+ { resolved: U } | { error: unknown }
43
+ > {
44
+ if (isPromiseLike(value)) {
45
+ const result = yield value;
46
+ if ("resolved" in result) {
47
+ return result.resolved;
48
+ } else {
49
+ throw result.error;
50
+ }
51
+ }
52
+
53
+ return value;
54
+ },
55
+ ...args,
56
+ ) as never;
57
+ return advance(iterator, undefined);
58
+ }
59
+
60
+ if (bindThis) {
61
+ return result.bind(bindThis);
62
+ } else {
63
+ return result;
64
+ }
65
+ }
package/src/buffer.ts ADDED
@@ -0,0 +1,334 @@
1
+ import type { Field } from "./field/index.js";
2
+ import { field } from "./field/index.js";
3
+
4
+ export const EmptyUint8Array = new Uint8Array(0);
5
+
6
+ export interface Converter<From, To> {
7
+ convert: (value: From) => To;
8
+ back: (value: To) => From;
9
+ }
10
+
11
+ export interface BufferLengthConverter<K, KT> extends Converter<KT, number> {
12
+ field: K;
13
+ }
14
+
15
+ export interface BufferLike {
16
+ (length: number): Field<Uint8Array, never, never, Uint8Array>;
17
+ <U>(
18
+ length: number,
19
+ converter: Converter<Uint8Array, U>,
20
+ ): Field<U, never, never, Uint8Array>;
21
+
22
+ <K extends string>(
23
+ lengthField: K,
24
+ ): Field<Uint8Array, K, Record<K, number>, Uint8Array>;
25
+ <K extends string, U>(
26
+ lengthField: K,
27
+ converter: Converter<Uint8Array, U>,
28
+ ): Field<U, K, Record<K, number>, Uint8Array>;
29
+
30
+ <K extends string, KT>(
31
+ length: BufferLengthConverter<K, KT>,
32
+ ): Field<Uint8Array, K, Record<K, KT>, Uint8Array>;
33
+ <K extends string, KT, U>(
34
+ length: BufferLengthConverter<K, KT>,
35
+ converter: Converter<Uint8Array, U>,
36
+ ): Field<U, K, Record<K, KT>, Uint8Array>;
37
+
38
+ <LengthOmitInit extends string, LengthDependencies>(
39
+ length: Field<number, LengthOmitInit, LengthDependencies, number>,
40
+ ): Field<Uint8Array, LengthOmitInit, LengthDependencies, Uint8Array>;
41
+ <LengthOmitInit extends string, LengthDependencies, U>(
42
+ length: Field<number, LengthOmitInit, LengthDependencies, number>,
43
+ converter: Converter<Uint8Array, U>,
44
+ ): Field<U, LengthOmitInit, LengthDependencies, Uint8Array>;
45
+ }
46
+
47
+ function _buffer(length: number): Field<Uint8Array, never, never, Uint8Array>;
48
+ function _buffer<U>(
49
+ length: number,
50
+ converter: Converter<Uint8Array, U>,
51
+ ): Field<U, never, never, Uint8Array>;
52
+
53
+ function _buffer<K extends string>(
54
+ lengthField: K,
55
+ ): Field<Uint8Array, K, Record<K, number>, Uint8Array>;
56
+ function _buffer<K extends string, U>(
57
+ lengthField: K,
58
+ converter: Converter<Uint8Array, U>,
59
+ ): Field<U, K, Record<K, number>, Uint8Array>;
60
+
61
+ function _buffer<K extends string, KT>(
62
+ length: BufferLengthConverter<K, KT>,
63
+ ): Field<Uint8Array, K, Record<K, KT>, Uint8Array>;
64
+ function _buffer<K extends string, KT, U>(
65
+ length: BufferLengthConverter<K, KT>,
66
+ converter: Converter<Uint8Array, U>,
67
+ ): Field<U, K, Record<K, KT>, Uint8Array>;
68
+
69
+ function _buffer<LengthOmitInit extends string, LengthDependencies>(
70
+ length: Field<number, LengthOmitInit, LengthDependencies, number>,
71
+ ): Field<Uint8Array, LengthOmitInit, LengthDependencies, Uint8Array>;
72
+ function _buffer<LengthOmitInit extends string, LengthDependencies, U>(
73
+ length: Field<number, LengthOmitInit, LengthDependencies, number>,
74
+ converter: Converter<Uint8Array, U>,
75
+ ): Field<U, LengthOmitInit, LengthDependencies, Uint8Array>;
76
+
77
+ /* #__NO_SIDE_EFFECTS__ */
78
+ function _buffer(
79
+ lengthOrField:
80
+ | string
81
+ | number
82
+ | Field<number, string, unknown, number>
83
+ | BufferLengthConverter<string, unknown>,
84
+ converter?: Converter<Uint8Array, unknown>,
85
+ ): Field<unknown, string, Record<string, unknown>, Uint8Array> {
86
+ // Fixed length
87
+ if (typeof lengthOrField === "number") {
88
+ if (converter) {
89
+ if (lengthOrField === 0) {
90
+ return field(
91
+ 0,
92
+ "byob",
93
+ () => {},
94
+ // eslint-disable-next-line require-yield
95
+ function* () {
96
+ return converter.convert(EmptyUint8Array);
97
+ },
98
+ );
99
+ }
100
+
101
+ return field(
102
+ 0,
103
+ "byob",
104
+ (value, { buffer, index }) => {
105
+ buffer.set(value.slice(0, lengthOrField), index);
106
+ },
107
+ function* (then, reader) {
108
+ const array = yield* then(
109
+ reader.readExactly(lengthOrField),
110
+ );
111
+ return converter.convert(array);
112
+ },
113
+ {
114
+ init(value) {
115
+ return converter.back(value);
116
+ },
117
+ },
118
+ );
119
+ }
120
+
121
+ if (lengthOrField === 0) {
122
+ return field(
123
+ 0,
124
+ "byob",
125
+ () => {},
126
+ // eslint-disable-next-line require-yield
127
+ function* () {
128
+ return EmptyUint8Array;
129
+ },
130
+ );
131
+ }
132
+
133
+ return field(
134
+ 0,
135
+ "byob",
136
+ (value, { buffer, index }) => {
137
+ buffer.set(value.slice(0, lengthOrField), index);
138
+ },
139
+ // eslint-disable-next-line require-yield
140
+ function* (_then, reader) {
141
+ return reader.readExactly(lengthOrField);
142
+ },
143
+ );
144
+ }
145
+
146
+ // Declare length field
147
+ // Some field types are `function`s
148
+ if (
149
+ (typeof lengthOrField === "object" ||
150
+ typeof lengthOrField === "function") &&
151
+ "serialize" in lengthOrField
152
+ ) {
153
+ if (converter) {
154
+ return field(
155
+ lengthOrField.size,
156
+ "default",
157
+ (value, { littleEndian }) => {
158
+ if (lengthOrField.type === "default") {
159
+ const lengthBuffer = lengthOrField.serialize(
160
+ value.length,
161
+ { littleEndian },
162
+ );
163
+ const result = new Uint8Array(
164
+ lengthBuffer.length + value.length,
165
+ );
166
+ result.set(lengthBuffer, 0);
167
+ result.set(value, lengthBuffer.length);
168
+ return result;
169
+ } else {
170
+ const result = new Uint8Array(
171
+ lengthOrField.size + value.length,
172
+ );
173
+ lengthOrField.serialize(value.length, {
174
+ buffer: result,
175
+ index: 0,
176
+ littleEndian,
177
+ });
178
+ result.set(value, lengthOrField.size);
179
+ return result;
180
+ }
181
+ },
182
+ function* (then, reader, context) {
183
+ const length = yield* then(
184
+ lengthOrField.deserialize(reader, context),
185
+ );
186
+ const array = yield* then(reader.readExactly(length));
187
+ return converter.convert(array);
188
+ },
189
+ {
190
+ init(value) {
191
+ return converter.back(value);
192
+ },
193
+ },
194
+ );
195
+ }
196
+
197
+ return field(
198
+ lengthOrField.size,
199
+ "default",
200
+ (value, { littleEndian }) => {
201
+ if (lengthOrField.type === "default") {
202
+ const lengthBuffer = lengthOrField.serialize(value.length, {
203
+ littleEndian,
204
+ });
205
+ const result = new Uint8Array(
206
+ lengthBuffer.length + value.length,
207
+ );
208
+ result.set(lengthBuffer, 0);
209
+ result.set(value, lengthBuffer.length);
210
+ return result;
211
+ } else {
212
+ const result = new Uint8Array(
213
+ lengthOrField.size + value.length,
214
+ );
215
+ lengthOrField.serialize(value.length, {
216
+ buffer: result,
217
+ index: 0,
218
+ littleEndian,
219
+ });
220
+ result.set(value, lengthOrField.size);
221
+ return result;
222
+ }
223
+ },
224
+ function* (then, reader, context) {
225
+ const length = yield* then(
226
+ lengthOrField.deserialize(reader, context),
227
+ );
228
+ return yield* then(reader.readExactly(length));
229
+ },
230
+ );
231
+ }
232
+
233
+ // Reference exiting length field
234
+ if (typeof lengthOrField === "string") {
235
+ if (converter) {
236
+ return field(
237
+ 0,
238
+ "default",
239
+ (source) => source,
240
+ // eslint-disable-next-line require-yield
241
+ function* (_then, reader, { dependencies }) {
242
+ const length = dependencies[lengthOrField] as number;
243
+ if (length === 0) {
244
+ return EmptyUint8Array;
245
+ }
246
+
247
+ return reader.readExactly(length);
248
+ },
249
+ {
250
+ init(value, dependencies) {
251
+ const array = converter.back(value);
252
+ dependencies[lengthOrField] = array.length;
253
+ return array;
254
+ },
255
+ },
256
+ );
257
+ }
258
+
259
+ return field(
260
+ 0,
261
+ "default",
262
+ (source) => source,
263
+ // eslint-disable-next-line require-yield
264
+ function* (_then, reader, { dependencies }) {
265
+ const length = dependencies[lengthOrField] as number;
266
+ if (length === 0) {
267
+ return EmptyUint8Array;
268
+ }
269
+
270
+ return reader.readExactly(length);
271
+ },
272
+ {
273
+ init(value, dependencies) {
274
+ dependencies[lengthOrField] = (value as Uint8Array).length;
275
+ return undefined;
276
+ },
277
+ },
278
+ );
279
+ }
280
+
281
+ // Reference existing length field + converter
282
+ if (converter) {
283
+ return field(
284
+ 0,
285
+ "default",
286
+ (source) => source,
287
+ // eslint-disable-next-line require-yield
288
+ function* (_then, reader, { dependencies }) {
289
+ const rawLength = dependencies[lengthOrField.field];
290
+ const length = lengthOrField.convert(rawLength);
291
+ if (length === 0) {
292
+ return EmptyUint8Array;
293
+ }
294
+
295
+ return reader.readExactly(length);
296
+ },
297
+ {
298
+ init(value, dependencies) {
299
+ const array = converter.back(value);
300
+ dependencies[lengthOrField.field] = lengthOrField.back(
301
+ array.length,
302
+ );
303
+ return array;
304
+ },
305
+ },
306
+ );
307
+ }
308
+
309
+ return field(
310
+ 0,
311
+ "default",
312
+ (source) => source,
313
+ // eslint-disable-next-line require-yield
314
+ function* (_then, reader, { dependencies }) {
315
+ const rawLength = dependencies[lengthOrField.field];
316
+ const length = lengthOrField.convert(rawLength);
317
+ if (length === 0) {
318
+ return EmptyUint8Array;
319
+ }
320
+
321
+ return reader.readExactly(length);
322
+ },
323
+ {
324
+ init(value, dependencies) {
325
+ dependencies[lengthOrField.field] = lengthOrField.back(
326
+ (value as Uint8Array).length,
327
+ );
328
+ return undefined;
329
+ },
330
+ },
331
+ );
332
+ }
333
+
334
+ export const buffer = _buffer;
package/src/concat.ts ADDED
@@ -0,0 +1,78 @@
1
+ import type { FieldsValue, Struct, StructFields } from "./struct.js";
2
+ import { struct } from "./struct.js";
3
+
4
+ type UnionToIntersection<U> = (
5
+ U extends unknown ? (x: U) => void : never
6
+ ) extends (x: infer I) => void
7
+ ? I
8
+ : never;
9
+
10
+ type As<T, U> = T extends infer V extends U ? V : never;
11
+
12
+ export type ConcatFields<
13
+ T extends Struct<
14
+ StructFields,
15
+ Record<PropertyKey, unknown> | undefined,
16
+ unknown
17
+ >[],
18
+ > = As<UnionToIntersection<T[number]["fields"]>, StructFields>;
19
+
20
+ type ConcatFieldValues<
21
+ T extends Struct<
22
+ StructFields,
23
+ Record<PropertyKey, unknown> | undefined,
24
+ unknown
25
+ >[],
26
+ > = FieldsValue<ConcatFields<T>>;
27
+
28
+ type ExtraToUnion<Extra extends Record<PropertyKey, unknown> | undefined> =
29
+ Extra extends undefined ? never : Extra;
30
+
31
+ export type ConcatExtras<
32
+ T extends Struct<
33
+ StructFields,
34
+ Record<PropertyKey, unknown> | undefined,
35
+ unknown
36
+ >[],
37
+ > = As<
38
+ UnionToIntersection<ExtraToUnion<T[number]["extra"]>>,
39
+ Record<PropertyKey, unknown>
40
+ >;
41
+
42
+ /* #__NO_SIDE_EFFECTS__ */
43
+ export function concat<
44
+ T extends Struct<
45
+ StructFields,
46
+ Record<PropertyKey, unknown> | undefined,
47
+ unknown
48
+ >[],
49
+ PostDeserialize = ConcatFieldValues<T> & ConcatExtras<T>,
50
+ >(
51
+ options: {
52
+ littleEndian: boolean;
53
+ postDeserialize?: (
54
+ this: ConcatFieldValues<T> & ConcatExtras<T>,
55
+ value: ConcatFieldValues<T> & ConcatExtras<T>,
56
+ ) => PostDeserialize;
57
+ },
58
+ ...structs: T
59
+ ): Struct<ConcatFields<T>, ConcatExtras<T>, PostDeserialize> {
60
+ return struct(
61
+ structs.reduce(
62
+ (fields, struct) => Object.assign(fields, struct.fields),
63
+ {},
64
+ ) as never,
65
+ {
66
+ littleEndian: options.littleEndian,
67
+ postDeserialize: options.postDeserialize,
68
+ extra: structs.reduce(
69
+ (extras, struct) =>
70
+ Object.defineProperties(
71
+ extras,
72
+ Object.getOwnPropertyDescriptors(struct.extra),
73
+ ),
74
+ {},
75
+ ) as never,
76
+ },
77
+ ) as never;
78
+ }
package/src/extend.ts ADDED
@@ -0,0 +1,37 @@
1
+ import type {
2
+ ExtraToIntersection,
3
+ FieldsValue,
4
+ Struct,
5
+ StructFields,
6
+ } from "./struct.js";
7
+ import { struct } from "./struct.js";
8
+
9
+ /* #__NO_SIDE_EFFECTS__ */
10
+ export function extend<
11
+ Base extends Struct<
12
+ StructFields,
13
+ Record<PropertyKey, unknown> | undefined,
14
+ unknown
15
+ >,
16
+ Fields extends StructFields,
17
+ PostDeserialize = FieldsValue<Base["fields"] & Fields> &
18
+ ExtraToIntersection<Base["extra"]>,
19
+ >(
20
+ base: Base,
21
+ fields: Fields,
22
+ options?: {
23
+ littleEndian?: boolean | undefined;
24
+ postDeserialize?: (
25
+ this: FieldsValue<Base["fields"] & Fields> &
26
+ ExtraToIntersection<Base["extra"]>,
27
+ value: FieldsValue<Base["fields"] & Fields> &
28
+ ExtraToIntersection<Base["extra"]>,
29
+ ) => PostDeserialize;
30
+ },
31
+ ): Struct<Base["fields"] & Fields, Base["extra"], PostDeserialize> {
32
+ return struct(Object.assign({}, base.fields, fields), {
33
+ littleEndian: options?.littleEndian ?? base.littleEndian,
34
+ extra: base.extra as never,
35
+ postDeserialize: options?.postDeserialize,
36
+ }) as never;
37
+ }
@@ -0,0 +1,64 @@
1
+ import type { BipedalGenerator } from "../bipedal.js";
2
+ import { bipedal } from "../bipedal.js";
3
+ import type { AsyncExactReadable } from "../readable.js";
4
+
5
+ import type {
6
+ ByobFieldSerializer,
7
+ DefaultFieldSerializer,
8
+ } from "./serialize.js";
9
+ import { byobFieldSerializer, defaultFieldSerializer } from "./serialize.js";
10
+ import type { Field, FieldDeserializeContext, FieldOptions } from "./types.js";
11
+
12
+ export type BipedalFieldDeserializer<T, D> = BipedalGenerator<
13
+ undefined,
14
+ T,
15
+ [reader: AsyncExactReadable, context: FieldDeserializeContext<D>]
16
+ >;
17
+
18
+ // eslint-disable-next-line @typescript-eslint/max-params
19
+ function _field<T, OmitInit extends string, D, Raw = T>(
20
+ size: number,
21
+ type: "default",
22
+ serialize: DefaultFieldSerializer<Raw>,
23
+ deserialize: BipedalFieldDeserializer<T, D>,
24
+ options?: FieldOptions<T, OmitInit, D, Raw>,
25
+ ): Field<T, OmitInit, D, Raw>;
26
+ // eslint-disable-next-line @typescript-eslint/max-params
27
+ function _field<T, OmitInit extends string, D, Raw = T>(
28
+ size: number,
29
+ type: "byob",
30
+ serialize: ByobFieldSerializer<Raw>,
31
+ deserialize: BipedalFieldDeserializer<T, D>,
32
+ options?: FieldOptions<T, OmitInit, D, Raw>,
33
+ ): Field<T, OmitInit, D, Raw>;
34
+ /* #__NO_SIDE_EFFECTS__ */
35
+ // eslint-disable-next-line @typescript-eslint/max-params
36
+ function _field<T, OmitInit extends string, D, Raw = T>(
37
+ size: number,
38
+ type: "default" | "byob",
39
+ serialize: DefaultFieldSerializer<Raw> | ByobFieldSerializer<Raw>,
40
+ deserialize: BipedalFieldDeserializer<T, D>,
41
+ options?: FieldOptions<T, OmitInit, D, Raw>,
42
+ ): Field<T, OmitInit, D, Raw> {
43
+ const field: Field<T, OmitInit, D, Raw> = {
44
+ size,
45
+ type: type,
46
+ serialize:
47
+ type === "default"
48
+ ? defaultFieldSerializer(
49
+ serialize as DefaultFieldSerializer<Raw>,
50
+ )
51
+ : byobFieldSerializer(
52
+ size,
53
+ serialize as ByobFieldSerializer<Raw>,
54
+ ),
55
+ deserialize: bipedal(deserialize) as never,
56
+ omitInit: options?.omitInit,
57
+ };
58
+ if (options?.init) {
59
+ field.init = options.init;
60
+ }
61
+ return field;
62
+ }
63
+
64
+ export const field = _field;
@@ -0,0 +1,3 @@
1
+ export * from "./factory.js";
2
+ export * from "./serialize.js";
3
+ export * from "./types.js";
@@ -0,0 +1,58 @@
1
+ import type {
2
+ FieldByobSerializeContext,
3
+ FieldDefaultSerializeContext,
4
+ FieldSerializer,
5
+ } from "./types.js";
6
+
7
+ export type DefaultFieldSerializer<T> = (
8
+ source: T,
9
+ context: FieldDefaultSerializeContext,
10
+ ) => Uint8Array;
11
+
12
+ /* Adapt default field serializer to universal field serializer */
13
+ export function defaultFieldSerializer<T>(
14
+ serializer: DefaultFieldSerializer<T>,
15
+ ): FieldSerializer<T>["serialize"] {
16
+ return (
17
+ source,
18
+ context: FieldDefaultSerializeContext | FieldByobSerializeContext,
19
+ ): never => {
20
+ if ("buffer" in context) {
21
+ const buffer = serializer(source, context);
22
+ context.buffer.set(buffer, context.index);
23
+ return buffer.length as never;
24
+ } else {
25
+ return serializer(source, context) as never;
26
+ }
27
+ };
28
+ }
29
+
30
+ export type ByobFieldSerializer<T> = (
31
+ source: T,
32
+ context: FieldByobSerializeContext & { index: number },
33
+ ) => void;
34
+
35
+ /* Adapt byob field serializer to universal field serializer */
36
+ export function byobFieldSerializer<T>(
37
+ size: number,
38
+ serializer: ByobFieldSerializer<T>,
39
+ ): FieldSerializer<T>["serialize"] {
40
+ return (
41
+ source,
42
+ context: FieldDefaultSerializeContext | FieldByobSerializeContext,
43
+ ): never => {
44
+ if ("buffer" in context) {
45
+ context.index ??= 0;
46
+ serializer(source, context as never);
47
+ return size as never;
48
+ } else {
49
+ const buffer = new Uint8Array(size);
50
+ serializer(source, {
51
+ buffer,
52
+ index: 0,
53
+ littleEndian: context.littleEndian,
54
+ });
55
+ return buffer as never;
56
+ }
57
+ };
58
+ }
@@ -0,0 +1,48 @@
1
+ import type { MaybePromiseLike } from "@yume-chan/async";
2
+
3
+ import type { AsyncExactReadable, ExactReadable } from "../readable.js";
4
+
5
+ export interface FieldDefaultSerializeContext {
6
+ littleEndian: boolean;
7
+ }
8
+
9
+ export interface FieldByobSerializeContext
10
+ extends FieldDefaultSerializeContext {
11
+ buffer: Uint8Array;
12
+ index?: number;
13
+ }
14
+
15
+ export interface FieldSerializer<T> {
16
+ type: "default" | "byob";
17
+ size: number;
18
+
19
+ serialize(source: T, context: FieldDefaultSerializeContext): Uint8Array;
20
+ serialize(source: T, context: FieldByobSerializeContext): number;
21
+ }
22
+
23
+ export interface Field<T, OmitInit extends string, D, Raw = T>
24
+ extends FieldSerializer<Raw>,
25
+ FieldDeserializer<T, D> {
26
+ omitInit: OmitInit | undefined;
27
+
28
+ init?(value: T, dependencies: D): Raw | undefined;
29
+ }
30
+
31
+ export interface FieldDeserializeContext<D> {
32
+ littleEndian: boolean;
33
+ dependencies: D;
34
+ }
35
+
36
+ export interface FieldDeserializer<T, D> {
37
+ deserialize(reader: ExactReadable, context: FieldDeserializeContext<D>): T;
38
+ deserialize(
39
+ reader: AsyncExactReadable,
40
+ context: FieldDeserializeContext<D>,
41
+ ): MaybePromiseLike<T>;
42
+ }
43
+
44
+ export interface FieldOptions<T, OmitInit extends string, D, Raw = T> {
45
+ omitInit?: OmitInit;
46
+ dependencies?: D;
47
+ init?: (value: T, dependencies: D) => Raw | undefined;
48
+ }