@yume-chan/struct 0.0.9 → 0.0.13

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 (151) hide show
  1. package/CHANGELOG.json +65 -0
  2. package/CHANGELOG.md +39 -0
  3. package/LICENSE +21 -21
  4. package/README.md +769 -709
  5. package/esm/basic/definition.d.ts +44 -0
  6. package/esm/basic/definition.d.ts.map +1 -0
  7. package/esm/basic/definition.js +13 -9
  8. package/esm/basic/definition.js.map +1 -1
  9. package/esm/basic/field-value.d.ts +38 -0
  10. package/esm/basic/field-value.d.ts.map +1 -0
  11. package/esm/basic/field-value.js +13 -7
  12. package/esm/basic/field-value.js.map +1 -1
  13. package/esm/basic/index.d.ts +6 -0
  14. package/esm/basic/index.d.ts.map +1 -0
  15. package/esm/basic/index.js +5 -4
  16. package/esm/basic/index.js.map +1 -1
  17. package/esm/basic/options.d.ts +10 -0
  18. package/esm/basic/options.d.ts.map +1 -0
  19. package/esm/basic/{context.js → options.js} +1 -1
  20. package/esm/basic/options.js.map +1 -0
  21. package/esm/basic/stream.d.ts +19 -0
  22. package/esm/basic/stream.d.ts.map +1 -0
  23. package/esm/basic/stream.js +2 -0
  24. package/esm/basic/stream.js.map +1 -0
  25. package/{dts → esm}/basic/struct-value.d.ts +3 -3
  26. package/esm/basic/struct-value.d.ts.map +1 -0
  27. package/esm/basic/struct-value.js +7 -15
  28. package/esm/basic/struct-value.js.map +1 -1
  29. package/esm/index.d.ts +14 -0
  30. package/esm/index.d.ts.map +1 -0
  31. package/esm/index.js +5 -5
  32. package/esm/index.js.map +1 -1
  33. package/{dts → esm}/struct.d.ts +25 -22
  34. package/esm/struct.d.ts.map +1 -0
  35. package/esm/struct.js +75 -52
  36. package/esm/struct.js.map +1 -1
  37. package/esm/syncbird.d.ts +65 -0
  38. package/esm/syncbird.d.ts.map +1 -0
  39. package/esm/syncbird.js +38 -0
  40. package/esm/syncbird.js.map +1 -0
  41. package/esm/types/bigint.d.ts +25 -0
  42. package/esm/types/bigint.d.ts.map +1 -0
  43. package/esm/types/bigint.js +45 -0
  44. package/esm/types/bigint.js.map +1 -0
  45. package/esm/types/buffer/base.d.ts +63 -0
  46. package/esm/types/buffer/base.d.ts.map +1 -0
  47. package/esm/types/buffer/base.js +100 -0
  48. package/esm/types/buffer/base.js.map +1 -0
  49. package/esm/types/buffer/fixed-length.d.ts +8 -0
  50. package/esm/types/buffer/fixed-length.d.ts.map +1 -0
  51. package/esm/types/buffer/fixed-length.js +8 -0
  52. package/esm/types/buffer/fixed-length.js.map +1 -0
  53. package/esm/types/buffer/index.d.ts +4 -0
  54. package/esm/types/buffer/index.d.ts.map +1 -0
  55. package/esm/types/buffer/index.js +4 -0
  56. package/esm/types/buffer/index.js.map +1 -0
  57. package/esm/types/buffer/variable-length.d.ts +43 -0
  58. package/esm/types/buffer/variable-length.d.ts.map +1 -0
  59. package/esm/types/buffer/variable-length.js +76 -0
  60. package/esm/types/buffer/variable-length.js.map +1 -0
  61. package/esm/types/index.d.ts +4 -0
  62. package/esm/types/index.d.ts.map +1 -0
  63. package/esm/types/index.js +3 -4
  64. package/esm/types/index.js.map +1 -1
  65. package/esm/types/number.d.ts +32 -0
  66. package/esm/types/number.d.ts.map +1 -0
  67. package/esm/types/number.js +26 -18
  68. package/esm/types/number.js.map +1 -1
  69. package/{dts → esm}/utils.d.ts +3 -2
  70. package/{dts → esm}/utils.d.ts.map +1 -1
  71. package/esm/utils.js +16 -0
  72. package/esm/utils.js.map +1 -1
  73. package/package.json +46 -49
  74. package/src/basic/definition.ts +70 -69
  75. package/src/basic/field-value.ts +67 -73
  76. package/src/basic/index.ts +5 -4
  77. package/src/basic/options.ts +19 -0
  78. package/src/basic/stream.ts +19 -0
  79. package/src/basic/struct-value.ts +39 -45
  80. package/src/index.ts +17 -5
  81. package/src/struct.ts +609 -575
  82. package/src/syncbird.ts +131 -0
  83. package/src/types/bigint.ts +102 -0
  84. package/src/types/buffer/base.ts +176 -0
  85. package/src/types/buffer/fixed-length.ts +17 -0
  86. package/src/types/buffer/index.ts +3 -0
  87. package/src/types/buffer/variable-length.ts +156 -0
  88. package/src/types/index.ts +3 -4
  89. package/src/types/number.ts +115 -100
  90. package/src/utils.ts +70 -51
  91. package/cjs/basic/context.js +0 -7
  92. package/cjs/basic/context.js.map +0 -1
  93. package/cjs/basic/definition.js +0 -25
  94. package/cjs/basic/definition.js.map +0 -1
  95. package/cjs/basic/field-value.js +0 -40
  96. package/cjs/basic/field-value.js.map +0 -1
  97. package/cjs/basic/index.js +0 -8
  98. package/cjs/basic/index.js.map +0 -1
  99. package/cjs/basic/struct-value.js +0 -46
  100. package/cjs/basic/struct-value.js.map +0 -1
  101. package/cjs/index.js +0 -11
  102. package/cjs/index.js.map +0 -1
  103. package/cjs/struct.js +0 -177
  104. package/cjs/struct.js.map +0 -1
  105. package/cjs/types/array-buffer.js +0 -118
  106. package/cjs/types/array-buffer.js.map +0 -1
  107. package/cjs/types/fixed-length-array-buffer.js +0 -12
  108. package/cjs/types/fixed-length-array-buffer.js.map +0 -1
  109. package/cjs/types/index.js +0 -8
  110. package/cjs/types/index.js.map +0 -1
  111. package/cjs/types/number.js +0 -52
  112. package/cjs/types/number.js.map +0 -1
  113. package/cjs/types/variable-length-array-buffer.js +0 -77
  114. package/cjs/types/variable-length-array-buffer.js.map +0 -1
  115. package/cjs/utils.js +0 -11
  116. package/cjs/utils.js.map +0 -1
  117. package/dts/basic/context.d.ts +0 -36
  118. package/dts/basic/context.d.ts.map +0 -1
  119. package/dts/basic/definition.d.ts +0 -48
  120. package/dts/basic/definition.d.ts.map +0 -1
  121. package/dts/basic/field-value.d.ts +0 -40
  122. package/dts/basic/field-value.d.ts.map +0 -1
  123. package/dts/basic/index.d.ts +0 -5
  124. package/dts/basic/index.d.ts.map +0 -1
  125. package/dts/basic/struct-value.d.ts.map +0 -1
  126. package/dts/index.d.ts +0 -6
  127. package/dts/index.d.ts.map +0 -1
  128. package/dts/struct.d.ts.map +0 -1
  129. package/dts/types/array-buffer.d.ts +0 -70
  130. package/dts/types/array-buffer.d.ts.map +0 -1
  131. package/dts/types/fixed-length-array-buffer.d.ts +0 -8
  132. package/dts/types/fixed-length-array-buffer.d.ts.map +0 -1
  133. package/dts/types/index.d.ts +0 -5
  134. package/dts/types/index.d.ts.map +0 -1
  135. package/dts/types/number.d.ts +0 -33
  136. package/dts/types/number.d.ts.map +0 -1
  137. package/dts/types/variable-length-array-buffer.d.ts +0 -32
  138. package/dts/types/variable-length-array-buffer.d.ts.map +0 -1
  139. package/esm/basic/context.js.map +0 -1
  140. package/esm/types/array-buffer.js +0 -109
  141. package/esm/types/array-buffer.js.map +0 -1
  142. package/esm/types/fixed-length-array-buffer.js +0 -8
  143. package/esm/types/fixed-length-array-buffer.js.map +0 -1
  144. package/esm/types/variable-length-array-buffer.js +0 -71
  145. package/esm/types/variable-length-array-buffer.js.map +0 -1
  146. package/src/basic/context.ts +0 -42
  147. package/src/types/array-buffer.ts +0 -184
  148. package/src/types/fixed-length-array-buffer.ts +0 -17
  149. package/src/types/variable-length-array-buffer.ts +0 -145
  150. package/tsconfig.cjs.json +0 -11
  151. package/tsconfig.esm.json +0 -15
@@ -0,0 +1,131 @@
1
+ // cspell: ignore syncbird
2
+
3
+ import Bluebird from 'bluebird';
4
+
5
+ export type Resolvable<R> = R | PromiseLike<R>;
6
+ export type IterateFunction<T, R> = (item: T, index: number, arrayLength: number) => Resolvable<R>;
7
+
8
+ export interface Syncbird<R> extends Bluebird<R> {
9
+ valueOrPromise(): R | PromiseLike<R>;
10
+
11
+ then<U>(onFulfill?: (value: R) => Resolvable<U>, onReject?: (error: any) => Resolvable<U>): Syncbird<U>; // For simpler signature help.
12
+ then<TResult1 = R, TResult2 = never>(
13
+ onfulfilled?: ((value: R) => Resolvable<TResult1>) | null,
14
+ onrejected?: ((reason: any) => Resolvable<TResult2>) | null
15
+ ): Syncbird<TResult1 | TResult2>;
16
+ }
17
+
18
+ interface SyncbirdStatic {
19
+ /**
20
+ * Iterate over an array, or a promise of an array,
21
+ * which contains promises (or a mix of promises and values) with the given iterator function with the signature `(item, index, value)`
22
+ * where item is the resolved value of a respective promise in the input array.
23
+ * Iteration happens serially. If any promise in the input array is rejected the returned promise is rejected as well.
24
+ *
25
+ * Resolves to the original array unmodified, this method is meant to be used for side effects.
26
+ * If the iterator function returns a promise or a thenable, the result for the promise is awaited for before continuing with next iteration.
27
+ */
28
+ each<R>(
29
+ values: Resolvable<Iterable<Resolvable<R>>>,
30
+ iterator: IterateFunction<R, any>
31
+ ): Syncbird<R[]>;
32
+
33
+ /**
34
+ * Reduce an array, or a promise of an array,
35
+ * which contains a promises (or a mix of promises and values) with the given `reducer` function with the signature `(total, current, index, arrayLength)`
36
+ * where `item` is the resolved value of a respective promise in the input array.
37
+ * If any promise in the input array is rejected the returned promise is rejected as well.
38
+ *
39
+ * If the reducer function returns a promise or a thenable, the result for the promise is awaited for before continuing with next iteration.
40
+ *
41
+ * *The original array is not modified. If no `initialValue` is given and the array doesn't contain at least 2 items,
42
+ * the callback will not be called and `undefined` is returned.
43
+ *
44
+ * If `initialValue` is given and the array doesn't have at least 1 item, `initialValue` is returned.*
45
+ */
46
+ reduce<R, U>(
47
+ values: Resolvable<Iterable<Resolvable<R>>>,
48
+ reducer: (total: U, current: R, index: number, arrayLength: number) => Resolvable<U>,
49
+ initialValue?: U
50
+ ): Syncbird<U>;
51
+
52
+ /**
53
+ * Create a promise that is resolved with the given `value`. If `value` is a thenable or promise, the returned promise will assume its state.
54
+ */
55
+ resolve(): Syncbird<void>;
56
+ resolve<R>(value: Resolvable<R>): Syncbird<R>;
57
+
58
+ try<R>(fn: () => Resolvable<R>): Syncbird<R>;
59
+ attempt<R>(fn: () => Resolvable<R>): Syncbird<R>;
60
+
61
+ /**
62
+ * Configure long stack traces, warnings, monitoring and cancellation.
63
+ * Note that even though false is the default here, a development environment might be detected which automatically
64
+ * enables long stack traces and warnings.
65
+ */
66
+ config(options: {
67
+ /** Enable warnings */
68
+ warnings?: boolean | {
69
+ /** Enables all warnings except forgotten return statements. */
70
+ wForgottenReturn: boolean;
71
+ } | undefined;
72
+ /** Enable long stack traces */
73
+ longStackTraces?: boolean | undefined;
74
+ /** Enable cancellation */
75
+ cancellation?: boolean | undefined;
76
+ /** Enable monitoring */
77
+ monitoring?: boolean | undefined;
78
+ /** Enable async hooks */
79
+ asyncHooks?: boolean | undefined;
80
+ }): void;
81
+
82
+ new <R>(callback: (resolve: (thenableOrResult?: Resolvable<R>) => void, reject: (error?: any) => void, onCancel?: (callback: () => void) => void) => void): Syncbird<R>;
83
+ }
84
+
85
+ export const Syncbird: SyncbirdStatic = Bluebird.getNewLibraryCopy() as any;
86
+
87
+ Syncbird.config({
88
+ asyncHooks: false,
89
+ cancellation: false,
90
+ longStackTraces: false,
91
+ monitoring: false,
92
+ warnings: false,
93
+ });
94
+
95
+ // Bluebird uses `_then` internally.
96
+ const _then = (Syncbird.prototype as any)._then;
97
+ Syncbird.prototype._then = function <T, TResult1 = T, TResult2 = never>(
98
+ this: Syncbird<T>,
99
+ onfulfilled: ((value: T, internalData?: unknown) => unknown) | undefined | null,
100
+ onrejected: ((reason: any) => unknown) | undefined | null,
101
+ _: never,
102
+ receiver: unknown,
103
+ internalData: unknown,
104
+ ): Syncbird<unknown> {
105
+ if (this.isFulfilled()) {
106
+ if (!onfulfilled) {
107
+ return this;
108
+ } else {
109
+ // Synchronously call `onfulfilled`, and wrap the result in a new `Syncbird` object.
110
+ return Syncbird.resolve(
111
+ onfulfilled.call(
112
+ receiver,
113
+ this.value(),
114
+ // Some Bluebird internal methods (for example `reduce`) need this `internalData`
115
+ internalData
116
+ )
117
+ );
118
+ }
119
+ } else {
120
+ // Forward to Bluebird's `_then` method.
121
+ return _then.call(this, onfulfilled, onrejected, _, receiver, internalData);
122
+ }
123
+ };
124
+
125
+ (Syncbird.prototype as any).valueOrPromise = function <T>(this: Bluebird<T>): T | PromiseLike<T> {
126
+ if (this.isFulfilled()) {
127
+ return this.value();
128
+ } else {
129
+ return this as Promise<T>;
130
+ }
131
+ };
@@ -0,0 +1,102 @@
1
+ // cspell: ignore syncbird
2
+
3
+ import { getBigInt64, getBigUint64, setBigInt64, setBigUint64 } from '@yume-chan/dataview-bigint-polyfill/esm/fallback.js';
4
+ import { StructFieldDefinition, StructFieldValue, StructValue, type StructAsyncDeserializeStream, type StructDeserializeStream, type StructOptions } from "../basic/index.js";
5
+ import { Syncbird } from "../syncbird.js";
6
+ import type { ValueOrPromise } from "../utils.js";
7
+
8
+ type DataViewBigInt64Getter = (dataView: DataView, byteOffset: number, littleEndian: boolean | undefined) => bigint;
9
+
10
+ type DataViewBigInt64Setter = (dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) => void;
11
+
12
+ export class BigIntFieldType {
13
+ public readonly TTypeScriptType!: bigint;
14
+
15
+ public readonly size: number;
16
+
17
+ public readonly getter: DataViewBigInt64Getter;
18
+
19
+ public readonly setter: DataViewBigInt64Setter;
20
+
21
+ public constructor(
22
+ size: number,
23
+ getter: DataViewBigInt64Getter,
24
+ setter: DataViewBigInt64Setter,
25
+ ) {
26
+ this.size = size;
27
+ this.getter = getter;
28
+ this.setter = setter;
29
+ }
30
+
31
+ public static readonly Int64 = new BigIntFieldType(8, getBigInt64, setBigInt64);
32
+
33
+ public static readonly Uint64 = new BigIntFieldType(8, getBigUint64, setBigUint64);
34
+ }
35
+
36
+ export class BigIntFieldDefinition<
37
+ TType extends BigIntFieldType = BigIntFieldType,
38
+ TTypeScriptType = TType["TTypeScriptType"],
39
+ > extends StructFieldDefinition<
40
+ void,
41
+ TTypeScriptType
42
+ > {
43
+ public readonly type: TType;
44
+
45
+ public constructor(type: TType, _typescriptType?: TTypeScriptType) {
46
+ super();
47
+ this.type = type;
48
+ }
49
+
50
+ public getSize(): number {
51
+ return this.type.size;
52
+ }
53
+
54
+ public create(
55
+ options: Readonly<StructOptions>,
56
+ struct: StructValue,
57
+ value: TTypeScriptType,
58
+ ): BigIntFieldValue<this> {
59
+ return new BigIntFieldValue(this, options, struct, value);
60
+ }
61
+
62
+ public override deserialize(
63
+ options: Readonly<StructOptions>,
64
+ stream: StructDeserializeStream,
65
+ struct: StructValue,
66
+ ): BigIntFieldValue<this>;
67
+ public override deserialize(
68
+ options: Readonly<StructOptions>,
69
+ stream: StructAsyncDeserializeStream,
70
+ struct: StructValue,
71
+ ): Promise<BigIntFieldValue<this>>;
72
+ public override deserialize(
73
+ options: Readonly<StructOptions>,
74
+ stream: StructDeserializeStream | StructAsyncDeserializeStream,
75
+ struct: StructValue,
76
+ ): ValueOrPromise<BigIntFieldValue<this>> {
77
+ return Syncbird.try(() => {
78
+ return stream.read(this.getSize());
79
+ }).then(array => {
80
+ const view = new DataView(array.buffer, array.byteOffset, array.byteLength);
81
+ const value = this.type.getter(
82
+ view,
83
+ 0,
84
+ options.littleEndian
85
+ );
86
+ return this.create(options, struct, value as any);
87
+ }).valueOrPromise();
88
+ }
89
+ }
90
+
91
+ export class BigIntFieldValue<
92
+ TDefinition extends BigIntFieldDefinition<BigIntFieldType, any>,
93
+ > extends StructFieldValue<TDefinition> {
94
+ public serialize(dataView: DataView, offset: number): void {
95
+ this.definition.type.setter(
96
+ dataView,
97
+ offset,
98
+ this.value!,
99
+ this.options.littleEndian
100
+ );
101
+ }
102
+ }
@@ -0,0 +1,176 @@
1
+ // cspell: ignore syncbird
2
+
3
+ import { StructFieldDefinition, StructFieldValue, StructValue, type StructAsyncDeserializeStream, type StructDeserializeStream, type StructOptions } from '../../basic/index.js';
4
+ import { Syncbird } from "../../syncbird.js";
5
+ import { decodeUtf8, encodeUtf8, type ValueOrPromise } from "../../utils.js";
6
+
7
+ /**
8
+ * Base class for all types that
9
+ * can be converted from an `Uint8Array` when deserialized,
10
+ * and need to be converted to an `Uint8Array` when serializing
11
+ *
12
+ * @template TValue The actual TypeScript type of this type
13
+ * @template TTypeScriptType Optional another type (should be compatible with `TType`)
14
+ * specified by user when creating field definitions.
15
+ */
16
+ export abstract class BufferFieldSubType<TValue = unknown, TTypeScriptType = TValue> {
17
+ public readonly TTypeScriptType!: TTypeScriptType;
18
+
19
+ /**
20
+ * When implemented in derived classes, converts the type-specific `value` to an `Uint8Array`
21
+ *
22
+ * This function should be "pure", i.e.,
23
+ * same `value` should always be converted to `Uint8Array`s that have same content.
24
+ */
25
+ public abstract toBuffer(value: TValue): Uint8Array;
26
+
27
+ /** When implemented in derived classes, converts the `Uint8Array` to a type-specific value */
28
+ public abstract toValue(array: Uint8Array): TValue;
29
+
30
+ /**
31
+ * When implemented in derived classes, gets the size in byte of the type-specific `value`.
32
+ *
33
+ * If the size can't be calculated without first converting the `value` back to an `Uint8Array`,
34
+ * implementer can returns `-1`, so the caller will get its size by first converting it to
35
+ * an `Uint8Array` (and cache the result).
36
+ */
37
+ public abstract getSize(value: TValue): number;
38
+ }
39
+
40
+ /** An `BufferFieldSubType` that's actually an `Uint8Array` */
41
+ export class Uint8ArrayBufferFieldSubType extends BufferFieldSubType<Uint8Array> {
42
+ public static readonly Instance = new Uint8ArrayBufferFieldSubType();
43
+
44
+ protected constructor() {
45
+ super();
46
+ }
47
+
48
+ public toBuffer(value: Uint8Array): Uint8Array {
49
+ return value;
50
+ }
51
+
52
+ public toValue(buffer: Uint8Array): Uint8Array {
53
+ return buffer;
54
+ }
55
+
56
+ public getSize(value: Uint8Array): number {
57
+ return value.byteLength;
58
+ }
59
+ }
60
+
61
+ /** An `BufferFieldSubType` that converts between `Uint8Array` and `string` */
62
+ export class StringBufferFieldSubType<TTypeScriptType = string>
63
+ extends BufferFieldSubType<string, TTypeScriptType> {
64
+ public static readonly Instance = new StringBufferFieldSubType();
65
+
66
+ public toBuffer(value: string): Uint8Array {
67
+ return encodeUtf8(value);
68
+ }
69
+
70
+ public toValue(array: Uint8Array): string {
71
+ return decodeUtf8(array);
72
+ }
73
+
74
+ public getSize(): number {
75
+ // Return `-1`, so `BufferLikeFieldDefinition` will
76
+ // convert this `value` into an `Uint8Array` (and cache the result),
77
+ // Then get the size from that `Uint8Array`
78
+ return -1;
79
+ }
80
+ }
81
+
82
+ const EmptyBuffer = new Uint8Array(0);
83
+
84
+ export abstract class BufferLikeFieldDefinition<
85
+ TType extends BufferFieldSubType<any, any> = BufferFieldSubType<unknown, unknown>,
86
+ TOptions = void,
87
+ TOmitInitKey extends PropertyKey = never,
88
+ > extends StructFieldDefinition<
89
+ TOptions,
90
+ TType['TTypeScriptType'],
91
+ TOmitInitKey
92
+ >{
93
+ public readonly type: TType;
94
+
95
+ public constructor(type: TType, options: TOptions) {
96
+ super(options);
97
+ this.type = type;
98
+ }
99
+
100
+ protected getDeserializeSize(struct: StructValue): number {
101
+ return this.getSize();
102
+ }
103
+
104
+ /**
105
+ * When implemented in derived classes, creates a `StructFieldValue` for the current field definition.
106
+ */
107
+ public create(
108
+ options: Readonly<StructOptions>,
109
+ struct: StructValue,
110
+ value: TType['TTypeScriptType'],
111
+ array?: Uint8Array,
112
+ ): BufferLikeFieldValue<this> {
113
+ return new BufferLikeFieldValue(this, options, struct, value, array);
114
+ }
115
+
116
+ public override deserialize(
117
+ options: Readonly<StructOptions>,
118
+ stream: StructDeserializeStream,
119
+ struct: StructValue,
120
+ ): BufferLikeFieldValue<this>;
121
+ public override deserialize(
122
+ options: Readonly<StructOptions>,
123
+ stream: StructAsyncDeserializeStream,
124
+ struct: StructValue,
125
+ ): Promise<BufferLikeFieldValue<this>>;
126
+ public override deserialize(
127
+ options: Readonly<StructOptions>,
128
+ stream: StructDeserializeStream | StructAsyncDeserializeStream,
129
+ struct: StructValue,
130
+ ): ValueOrPromise<BufferLikeFieldValue<this>> {
131
+ return Syncbird.try(() => {
132
+ const size = this.getDeserializeSize(struct);
133
+ if (size === 0) {
134
+ return EmptyBuffer;
135
+ } else {
136
+ return stream.read(size);
137
+ }
138
+ }).then(array => {
139
+ const value = this.type.toValue(array);
140
+ return this.create(options, struct, value, array);
141
+ }).valueOrPromise();
142
+ }
143
+ }
144
+
145
+ export class BufferLikeFieldValue<
146
+ TDefinition extends BufferLikeFieldDefinition<BufferFieldSubType<unknown, unknown>, any, any>,
147
+ > extends StructFieldValue<TDefinition> {
148
+ protected array: Uint8Array | undefined;
149
+
150
+ public constructor(
151
+ definition: TDefinition,
152
+ options: Readonly<StructOptions>,
153
+ struct: StructValue,
154
+ value: TDefinition['TValue'],
155
+ array?: Uint8Array,
156
+ ) {
157
+ super(definition, options, struct, value);
158
+ this.array = array;
159
+ }
160
+
161
+ public override set(value: TDefinition['TValue']): void {
162
+ super.set(value);
163
+ // When value changes, clear the cached `array`
164
+ // It will be lazily calculated in `serialize()`
165
+ this.array = undefined;
166
+ }
167
+
168
+ public serialize(dataView: DataView, offset: number): void {
169
+ if (!this.array) {
170
+ this.array = this.definition.type.toBuffer(this.value);
171
+ }
172
+
173
+ new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength)
174
+ .set(this.array, offset);
175
+ }
176
+ }
@@ -0,0 +1,17 @@
1
+ import { BufferLikeFieldDefinition, type BufferFieldSubType } from "./base.js";
2
+
3
+ export interface FixedLengthBufferLikeFieldOptions {
4
+ length: number;
5
+ }
6
+
7
+ export class FixedLengthBufferLikeFieldDefinition<
8
+ TType extends BufferFieldSubType = BufferFieldSubType,
9
+ TOptions extends FixedLengthBufferLikeFieldOptions = FixedLengthBufferLikeFieldOptions,
10
+ > extends BufferLikeFieldDefinition<
11
+ TType,
12
+ TOptions
13
+ > {
14
+ public getSize(): number {
15
+ return this.options.length;
16
+ }
17
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./base.js";
2
+ export * from "./fixed-length.js";
3
+ export * from "./variable-length.js";
@@ -0,0 +1,156 @@
1
+ import { StructFieldValue, type StructFieldDefinition, type StructOptions, type StructValue } from '../../basic/index.js';
2
+ import type { KeysOfType } from '../../utils.js';
3
+ import { BufferLikeFieldDefinition, BufferLikeFieldValue, type BufferFieldSubType } from './base.js';
4
+
5
+ export type LengthField<TFields> = KeysOfType<TFields, number | string>;
6
+
7
+ export interface VariableLengthBufferLikeFieldOptions<
8
+ TFields = object,
9
+ TLengthField extends LengthField<TFields> = any,
10
+ > {
11
+ /**
12
+ * The name of the field that contains the length of the buffer.
13
+ *
14
+ * This field must be a `number` or `string` (can't be `bigint`) field.
15
+ */
16
+ lengthField: TLengthField;
17
+
18
+ /**
19
+ * If the `lengthField` refers to a string field,
20
+ * what radix to use when converting the string to a number.
21
+ *
22
+ * @default 10
23
+ */
24
+ lengthFieldRadix?: number;
25
+ }
26
+
27
+ export class VariableLengthBufferLikeFieldDefinition<
28
+ TType extends BufferFieldSubType = BufferFieldSubType,
29
+ TOptions extends VariableLengthBufferLikeFieldOptions = VariableLengthBufferLikeFieldOptions
30
+ > extends BufferLikeFieldDefinition<
31
+ TType,
32
+ TOptions,
33
+ TOptions['lengthField']
34
+ > {
35
+ public getSize(): number {
36
+ return 0;
37
+ }
38
+
39
+ protected override getDeserializeSize(struct: StructValue) {
40
+ let value = struct.value[this.options.lengthField] as number | string;
41
+ if (typeof value === 'string') {
42
+ value = Number.parseInt(value, this.options.lengthFieldRadix ?? 10);
43
+ }
44
+ return value;
45
+ }
46
+
47
+ public override create(
48
+ options: Readonly<StructOptions>,
49
+ struct: StructValue,
50
+ value: TType['TTypeScriptType'],
51
+ array?: Uint8Array
52
+ ): VariableLengthBufferLikeStructFieldValue<this> {
53
+ return new VariableLengthBufferLikeStructFieldValue(
54
+ this,
55
+ options,
56
+ struct,
57
+ value,
58
+ array,
59
+ );
60
+ }
61
+ }
62
+
63
+ export class VariableLengthBufferLikeStructFieldValue<
64
+ TDefinition extends VariableLengthBufferLikeFieldDefinition = VariableLengthBufferLikeFieldDefinition,
65
+ > extends BufferLikeFieldValue<TDefinition> {
66
+ protected length: number | undefined;
67
+
68
+ protected lengthFieldValue: VariableLengthBufferLikeFieldLengthValue;
69
+
70
+ public constructor(
71
+ definition: TDefinition,
72
+ options: Readonly<StructOptions>,
73
+ struct: StructValue,
74
+ value: TDefinition['TValue'],
75
+ array?: Uint8Array,
76
+ ) {
77
+ super(definition, options, struct, value, array);
78
+
79
+ if (array) {
80
+ this.length = array.byteLength;
81
+ }
82
+
83
+ // Patch the associated length field.
84
+ const lengthField = this.definition.options.lengthField;
85
+
86
+ const originalValue = struct.get(lengthField);
87
+ this.lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue(
88
+ originalValue,
89
+ this,
90
+ );
91
+ struct.set(lengthField, this.lengthFieldValue);
92
+ }
93
+
94
+ public override getSize() {
95
+ if (this.length === undefined) {
96
+ this.length = this.definition.type.getSize(this.value);
97
+ if (this.length === -1) {
98
+ this.array = this.definition.type.toBuffer(this.value);
99
+ this.length = this.array.byteLength;
100
+ }
101
+ }
102
+
103
+ return this.length;
104
+ }
105
+
106
+ public override set(value: unknown) {
107
+ super.set(value);
108
+ this.array = undefined;
109
+ this.length = undefined;
110
+ }
111
+ }
112
+
113
+ // Not using `VariableLengthBufferLikeStructFieldValue` directly makes writing tests much easier...
114
+ type VariableLengthBufferLikeFieldValueLike =
115
+ StructFieldValue<StructFieldDefinition<VariableLengthBufferLikeFieldOptions, any, any>>;
116
+
117
+ export class VariableLengthBufferLikeFieldLengthValue
118
+ extends StructFieldValue {
119
+ protected originalField: StructFieldValue;
120
+
121
+ protected bufferField: VariableLengthBufferLikeFieldValueLike;
122
+
123
+ public constructor(
124
+ originalField: StructFieldValue,
125
+ arrayBufferField: VariableLengthBufferLikeFieldValueLike,
126
+ ) {
127
+ super(originalField.definition, originalField.options, originalField.struct, 0);
128
+ this.originalField = originalField;
129
+ this.bufferField = arrayBufferField;
130
+ }
131
+
132
+ public override getSize() {
133
+ return this.originalField.getSize();
134
+ }
135
+
136
+ public override get() {
137
+ let value: string | number = this.bufferField.getSize();
138
+
139
+ const originalValue = this.originalField.get();
140
+ if (typeof originalValue === 'string') {
141
+ value = value.toString(this.bufferField.definition.options.lengthFieldRadix ?? 10);
142
+ }
143
+
144
+ return value;
145
+ }
146
+
147
+ public override set() {
148
+ // Ignore setting
149
+ // It will always be in sync with the buffer size
150
+ }
151
+
152
+ serialize(dataView: DataView, offset: number) {
153
+ this.originalField.set(this.get());
154
+ this.originalField.serialize(dataView, offset);
155
+ }
156
+ }
@@ -1,4 +1,3 @@
1
- export * from './array-buffer';
2
- export * from './fixed-length-array-buffer';
3
- export * from './number';
4
- export * from './variable-length-array-buffer';
1
+ export * from "./bigint.js";
2
+ export * from "./buffer/index.js";
3
+ export * from "./number.js";