@solana/codecs-data-structures 6.3.1 → 6.3.2-canary-20260313112147
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/package.json +6 -5
- package/src/array.ts +300 -0
- package/src/assertions.ts +16 -0
- package/src/bit-array.ts +203 -0
- package/src/boolean.ts +163 -0
- package/src/bytes.ts +115 -0
- package/src/constant.ts +135 -0
- package/src/discriminated-union.ts +384 -0
- package/src/enum-helpers.ts +102 -0
- package/src/enum.ts +309 -0
- package/src/hidden-prefix.ts +180 -0
- package/src/hidden-suffix.ts +180 -0
- package/src/index.ts +30 -0
- package/src/literal-union.ts +249 -0
- package/src/map.ts +285 -0
- package/src/nullable.ts +356 -0
- package/src/pattern-match.ts +312 -0
- package/src/predicate.ts +214 -0
- package/src/set.ts +206 -0
- package/src/struct.ts +239 -0
- package/src/tuple.ts +228 -0
- package/src/union.ts +257 -0
- package/src/unit.ts +111 -0
- package/src/utils.ts +32 -0
package/src/predicate.ts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Codec,
|
|
3
|
+
Decoder,
|
|
4
|
+
Encoder,
|
|
5
|
+
FixedSizeCodec,
|
|
6
|
+
FixedSizeDecoder,
|
|
7
|
+
FixedSizeEncoder,
|
|
8
|
+
ReadonlyUint8Array,
|
|
9
|
+
VariableSizeCodec,
|
|
10
|
+
VariableSizeDecoder,
|
|
11
|
+
VariableSizeEncoder,
|
|
12
|
+
} from '@solana/codecs-core';
|
|
13
|
+
|
|
14
|
+
import { getUnionCodec, getUnionDecoder, getUnionEncoder } from './union';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns an encoder that selects between two encoders based on a predicate.
|
|
18
|
+
*
|
|
19
|
+
* This encoder uses a boolean predicate function to determine which of two
|
|
20
|
+
* encoders to use for a given value. If the predicate returns `true`, the
|
|
21
|
+
* `ifTrue` encoder is used; otherwise, the `ifFalse` encoder is used.
|
|
22
|
+
*
|
|
23
|
+
* @typeParam TFrom - The type of the value to encode.
|
|
24
|
+
*
|
|
25
|
+
* @param predicate - A function that returns `true` or `false` for a given value.
|
|
26
|
+
* @param ifTrue - The encoder to use when the predicate returns `true`.
|
|
27
|
+
* @param ifFalse - The encoder to use when the predicate returns `false`.
|
|
28
|
+
* @returns An `Encoder` based on the provided encoders.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* Encoding small and large numbers differently.
|
|
32
|
+
* ```ts
|
|
33
|
+
* const encoder = getPredicateEncoder(
|
|
34
|
+
* (n: number) => n < 256,
|
|
35
|
+
* getU8Encoder(),
|
|
36
|
+
* getU32Encoder()
|
|
37
|
+
* );
|
|
38
|
+
*
|
|
39
|
+
* encoder.encode(42);
|
|
40
|
+
* // 0x2a
|
|
41
|
+
* // └── Small number encoded as u8
|
|
42
|
+
*
|
|
43
|
+
* encoder.encode(1000);
|
|
44
|
+
* // 0xe8030000
|
|
45
|
+
* // └── Large number encoded as u32
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @see {@link getPredicateCodec}
|
|
49
|
+
*/
|
|
50
|
+
export function getPredicateEncoder<TFrom, TSize extends number>(
|
|
51
|
+
predicate: (value: TFrom) => boolean,
|
|
52
|
+
ifTrue: FixedSizeEncoder<TFrom, TSize>,
|
|
53
|
+
ifFalse: FixedSizeEncoder<TFrom, TSize>,
|
|
54
|
+
): FixedSizeEncoder<TFrom, TSize>;
|
|
55
|
+
export function getPredicateEncoder<TFrom>(
|
|
56
|
+
predicate: (value: TFrom) => boolean,
|
|
57
|
+
ifTrue: FixedSizeEncoder<TFrom>,
|
|
58
|
+
ifFalse: FixedSizeEncoder<TFrom>,
|
|
59
|
+
): FixedSizeEncoder<TFrom>;
|
|
60
|
+
export function getPredicateEncoder<TFrom>(
|
|
61
|
+
predicate: (value: TFrom) => boolean,
|
|
62
|
+
ifTrue: VariableSizeEncoder<TFrom>,
|
|
63
|
+
ifFalse: VariableSizeEncoder<TFrom>,
|
|
64
|
+
): VariableSizeEncoder<TFrom>;
|
|
65
|
+
export function getPredicateEncoder<TFrom>(
|
|
66
|
+
predicate: (value: TFrom) => boolean,
|
|
67
|
+
ifTrue: Encoder<TFrom>,
|
|
68
|
+
ifFalse: Encoder<TFrom>,
|
|
69
|
+
): Encoder<TFrom>;
|
|
70
|
+
export function getPredicateEncoder<TFrom>(
|
|
71
|
+
predicate: (value: TFrom) => boolean,
|
|
72
|
+
ifTrue: Encoder<TFrom>,
|
|
73
|
+
ifFalse: Encoder<TFrom>,
|
|
74
|
+
): Encoder<TFrom> {
|
|
75
|
+
return getUnionEncoder([ifTrue, ifFalse], (value: TFrom) => (predicate(value) ? 0 : 1));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns a decoder that selects between two decoders based on a predicate.
|
|
80
|
+
*
|
|
81
|
+
* This decoder uses a boolean predicate function on the raw bytes to determine
|
|
82
|
+
* which of two decoders to use. If the predicate returns `true`, the `ifTrue`
|
|
83
|
+
* decoder is used; otherwise, the `ifFalse` decoder is used.
|
|
84
|
+
*
|
|
85
|
+
* @typeParam TTo - The type of the value to decode.
|
|
86
|
+
*
|
|
87
|
+
* @param predicate - A function that returns `true` or `false` for a given byte array.
|
|
88
|
+
* @param ifTrue - The decoder to use when the predicate returns `true`.
|
|
89
|
+
* @param ifFalse - The decoder to use when the predicate returns `false`.
|
|
90
|
+
* @returns A `Decoder` based on the provided decoders.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* Decoding small and large numbers based on byte length.
|
|
94
|
+
* ```ts
|
|
95
|
+
* const decoder = getPredicateDecoder(
|
|
96
|
+
* bytes => bytes.length === 1,
|
|
97
|
+
* getU8Decoder(),
|
|
98
|
+
* getU32Decoder()
|
|
99
|
+
* );
|
|
100
|
+
*
|
|
101
|
+
* decoder.decode(new Uint8Array([0x2a]));
|
|
102
|
+
* // 42 (decoded as u8)
|
|
103
|
+
*
|
|
104
|
+
* decoder.decode(new Uint8Array([0xe8, 0x03, 0x00, 0x00]));
|
|
105
|
+
* // 1000 (decoded as u32)
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @see {@link getPredicateCodec}
|
|
109
|
+
*/
|
|
110
|
+
export function getPredicateDecoder<TTo, TSize extends number>(
|
|
111
|
+
predicate: (value: ReadonlyUint8Array) => boolean,
|
|
112
|
+
ifTrue: FixedSizeDecoder<TTo, TSize>,
|
|
113
|
+
ifFalse: FixedSizeDecoder<TTo, TSize>,
|
|
114
|
+
): FixedSizeDecoder<TTo, TSize>;
|
|
115
|
+
export function getPredicateDecoder<TTo>(
|
|
116
|
+
predicate: (value: ReadonlyUint8Array) => boolean,
|
|
117
|
+
ifTrue: FixedSizeDecoder<TTo>,
|
|
118
|
+
ifFalse: FixedSizeDecoder<TTo>,
|
|
119
|
+
): FixedSizeDecoder<TTo>;
|
|
120
|
+
export function getPredicateDecoder<TTo>(
|
|
121
|
+
predicate: (value: ReadonlyUint8Array) => boolean,
|
|
122
|
+
ifTrue: VariableSizeDecoder<TTo>,
|
|
123
|
+
ifFalse: VariableSizeDecoder<TTo>,
|
|
124
|
+
): VariableSizeDecoder<TTo>;
|
|
125
|
+
export function getPredicateDecoder<TTo>(
|
|
126
|
+
predicate: (value: ReadonlyUint8Array) => boolean,
|
|
127
|
+
ifTrue: Decoder<TTo>,
|
|
128
|
+
ifFalse: Decoder<TTo>,
|
|
129
|
+
): Decoder<TTo>;
|
|
130
|
+
export function getPredicateDecoder<TTo>(
|
|
131
|
+
predicate: (value: ReadonlyUint8Array) => boolean,
|
|
132
|
+
ifTrue: Decoder<TTo>,
|
|
133
|
+
ifFalse: Decoder<TTo>,
|
|
134
|
+
): Decoder<TTo> {
|
|
135
|
+
return getUnionDecoder([ifTrue, ifFalse], (value: ReadonlyUint8Array) => (predicate(value) ? 0 : 1));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Returns a codec that selects between two codecs based on predicates.
|
|
140
|
+
*
|
|
141
|
+
* This codec uses boolean predicate functions to determine which of two codecs
|
|
142
|
+
* to use for encoding and decoding. If the encoding predicate returns `true`
|
|
143
|
+
* for a value, the `ifTrue` codec is used to encode it; otherwise `ifFalse`.
|
|
144
|
+
* Similarly, if the decoding predicate returns `true` for the bytes, the
|
|
145
|
+
* `ifTrue` codec is used to decode them.
|
|
146
|
+
*
|
|
147
|
+
* @typeParam TFrom - The type of the value to encode.
|
|
148
|
+
* @typeParam TTo - The type of the value to decode.
|
|
149
|
+
*
|
|
150
|
+
* @param encodePredicate - A function that returns `true` or `false` for a given value.
|
|
151
|
+
* @param decodePredicate - A function that returns `true` or `false` for a given byte array.
|
|
152
|
+
* @param ifTrue - The codec to use when the respective predicate returns `true`.
|
|
153
|
+
* @param ifFalse - The codec to use when the respective predicate returns `false`.
|
|
154
|
+
* @returns A `Codec` based on the provided codecs.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* Encoding and decoding small and large numbers differently.
|
|
158
|
+
* ```ts
|
|
159
|
+
* const codec = getPredicateCodec(
|
|
160
|
+
* (n: number) => n < 256,
|
|
161
|
+
* bytes => bytes.length === 1,
|
|
162
|
+
* getU8Codec(),
|
|
163
|
+
* getU32Codec()
|
|
164
|
+
* );
|
|
165
|
+
*
|
|
166
|
+
* const smallBytes = codec.encode(42);
|
|
167
|
+
* // 0x2a (encoded as u8)
|
|
168
|
+
*
|
|
169
|
+
* const largeBytes = codec.encode(1000);
|
|
170
|
+
* // 0xe8030000 (encoded as u32)
|
|
171
|
+
*
|
|
172
|
+
* codec.decode(smallBytes); // 42
|
|
173
|
+
* codec.decode(largeBytes); // 1000
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @see {@link getPredicateEncoder}
|
|
177
|
+
* @see {@link getPredicateDecoder}
|
|
178
|
+
*/
|
|
179
|
+
export function getPredicateCodec<TFrom, TTo extends TFrom, TSize extends number>(
|
|
180
|
+
encodePredicate: (value: TFrom) => boolean,
|
|
181
|
+
decodePredicate: (value: ReadonlyUint8Array) => boolean,
|
|
182
|
+
ifTrue: FixedSizeCodec<TFrom, TTo, TSize>,
|
|
183
|
+
ifFalse: FixedSizeCodec<TFrom, TTo, TSize>,
|
|
184
|
+
): FixedSizeCodec<TFrom, TTo, TSize>;
|
|
185
|
+
export function getPredicateCodec<TFrom, TTo extends TFrom>(
|
|
186
|
+
encodePredicate: (value: TFrom) => boolean,
|
|
187
|
+
decodePredicate: (value: ReadonlyUint8Array) => boolean,
|
|
188
|
+
ifTrue: FixedSizeCodec<TFrom, TTo>,
|
|
189
|
+
ifFalse: FixedSizeCodec<TFrom, TTo>,
|
|
190
|
+
): FixedSizeCodec<TFrom, TTo>;
|
|
191
|
+
export function getPredicateCodec<TFrom, TTo extends TFrom>(
|
|
192
|
+
encodePredicate: (value: TFrom) => boolean,
|
|
193
|
+
decodePredicate: (value: ReadonlyUint8Array) => boolean,
|
|
194
|
+
ifTrue: VariableSizeCodec<TFrom, TTo>,
|
|
195
|
+
ifFalse: VariableSizeCodec<TFrom, TTo>,
|
|
196
|
+
): VariableSizeCodec<TFrom, TTo>;
|
|
197
|
+
export function getPredicateCodec<TFrom, TTo extends TFrom>(
|
|
198
|
+
encodePredicate: (value: TFrom) => boolean,
|
|
199
|
+
decodePredicate: (value: ReadonlyUint8Array) => boolean,
|
|
200
|
+
ifTrue: Codec<TFrom, TTo>,
|
|
201
|
+
ifFalse: Codec<TFrom, TTo>,
|
|
202
|
+
): Codec<TFrom, TTo>;
|
|
203
|
+
export function getPredicateCodec<TFrom, TTo extends TFrom>(
|
|
204
|
+
encodePredicate: (value: TFrom) => boolean,
|
|
205
|
+
decodePredicate: (value: ReadonlyUint8Array) => boolean,
|
|
206
|
+
ifTrue: Codec<TFrom, TTo>,
|
|
207
|
+
ifFalse: Codec<TFrom, TTo>,
|
|
208
|
+
): Codec<TFrom, TTo> {
|
|
209
|
+
return getUnionCodec(
|
|
210
|
+
[ifTrue, ifFalse],
|
|
211
|
+
(value: TFrom) => (encodePredicate(value) ? 0 : 1),
|
|
212
|
+
(value: ReadonlyUint8Array) => (decodePredicate(value) ? 0 : 1),
|
|
213
|
+
);
|
|
214
|
+
}
|
package/src/set.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Codec,
|
|
3
|
+
combineCodec,
|
|
4
|
+
Decoder,
|
|
5
|
+
Encoder,
|
|
6
|
+
FixedSizeCodec,
|
|
7
|
+
FixedSizeDecoder,
|
|
8
|
+
FixedSizeEncoder,
|
|
9
|
+
transformDecoder,
|
|
10
|
+
transformEncoder,
|
|
11
|
+
VariableSizeCodec,
|
|
12
|
+
VariableSizeDecoder,
|
|
13
|
+
VariableSizeEncoder,
|
|
14
|
+
} from '@solana/codecs-core';
|
|
15
|
+
import { NumberCodec, NumberDecoder, NumberEncoder } from '@solana/codecs-numbers';
|
|
16
|
+
|
|
17
|
+
import { ArrayLikeCodecSize, getArrayDecoder, getArrayEncoder } from './array';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Defines the configuration options for set codecs.
|
|
21
|
+
*
|
|
22
|
+
* This configuration allows specifying how the size of the set is encoded.
|
|
23
|
+
* The `size` option can be:
|
|
24
|
+
*
|
|
25
|
+
* - A {@link NumberCodec}, {@link NumberEncoder}, or {@link NumberDecoder} to store the size as a prefix.
|
|
26
|
+
* - A fixed number of items, enforcing a strict length.
|
|
27
|
+
* - The string `'remainder'` to infer the set size from the remaining bytes (only for fixed-size items).
|
|
28
|
+
*
|
|
29
|
+
* @typeParam TPrefix - The type used for encoding the size of the set.
|
|
30
|
+
*/
|
|
31
|
+
export type SetCodecConfig<TPrefix extends NumberCodec | NumberDecoder | NumberEncoder> = {
|
|
32
|
+
/**
|
|
33
|
+
* The size encoding strategy for the set.
|
|
34
|
+
* @defaultValue Uses a `u32` prefix.
|
|
35
|
+
*/
|
|
36
|
+
size?: ArrayLikeCodecSize<TPrefix>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns an encoder for sets of items.
|
|
41
|
+
*
|
|
42
|
+
* This encoder serializes `Set<T>` values by encoding each item using the provided item encoder.
|
|
43
|
+
* The number of items is stored as a prefix using a `u32` codec by default.
|
|
44
|
+
*
|
|
45
|
+
* For more details, see {@link getSetCodec}.
|
|
46
|
+
*
|
|
47
|
+
* @typeParam TFrom - The type of the items in the set before encoding.
|
|
48
|
+
*
|
|
49
|
+
* @param item - The encoder to use for each set item.
|
|
50
|
+
* @param config - Optional configuration specifying the size strategy.
|
|
51
|
+
* @returns An `Encoder<Set<TFrom>>` for encoding sets of items.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* Encoding a set of `u8` numbers.
|
|
55
|
+
* ```ts
|
|
56
|
+
* const encoder = getSetEncoder(getU8Encoder());
|
|
57
|
+
* const bytes = encoder.encode(new Set([1, 2, 3]));
|
|
58
|
+
* // 0x03000000010203
|
|
59
|
+
* // | └-- 3 items of 1 byte each.
|
|
60
|
+
* // └-- 4-byte prefix indicating 3 items.
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @see {@link getSetCodec}
|
|
64
|
+
*/
|
|
65
|
+
export function getSetEncoder<TFrom>(
|
|
66
|
+
item: Encoder<TFrom>,
|
|
67
|
+
config: SetCodecConfig<NumberEncoder> & { size: 0 },
|
|
68
|
+
): FixedSizeEncoder<Set<TFrom>, 0>;
|
|
69
|
+
export function getSetEncoder<TFrom>(
|
|
70
|
+
item: FixedSizeEncoder<TFrom>,
|
|
71
|
+
config: SetCodecConfig<NumberEncoder> & { size: number },
|
|
72
|
+
): FixedSizeEncoder<Set<TFrom>>;
|
|
73
|
+
export function getSetEncoder<TFrom>(
|
|
74
|
+
item: Encoder<TFrom>,
|
|
75
|
+
config?: SetCodecConfig<NumberEncoder>,
|
|
76
|
+
): VariableSizeEncoder<Set<TFrom>>;
|
|
77
|
+
export function getSetEncoder<TFrom>(
|
|
78
|
+
item: Encoder<TFrom>,
|
|
79
|
+
config: SetCodecConfig<NumberEncoder> = {},
|
|
80
|
+
): Encoder<Set<TFrom>> {
|
|
81
|
+
return transformEncoder(getArrayEncoder(item, config as object), (set: Set<TFrom>): TFrom[] => [...set]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Returns a decoder for sets of items.
|
|
86
|
+
*
|
|
87
|
+
* This decoder deserializes a `Set<T>` from a byte array by decoding each item using the provided item decoder.
|
|
88
|
+
* The number of items is determined by a `u32` size prefix by default.
|
|
89
|
+
*
|
|
90
|
+
* For more details, see {@link getSetCodec}.
|
|
91
|
+
*
|
|
92
|
+
* @typeParam TTo - The type of the items in the set after decoding.
|
|
93
|
+
*
|
|
94
|
+
* @param item - The decoder to use for each set item.
|
|
95
|
+
* @param config - Optional configuration specifying the size strategy.
|
|
96
|
+
* @returns A `Decoder<Set<TTo>>` for decoding sets of items.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* Decoding a set of `u8` numbers.
|
|
100
|
+
* ```ts
|
|
101
|
+
* const decoder = getSetDecoder(getU8Decoder());
|
|
102
|
+
* const value = decoder.decode(new Uint8Array([0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03]));
|
|
103
|
+
* // new Set([1, 2, 3])
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @see {@link getSetCodec}
|
|
107
|
+
*/
|
|
108
|
+
export function getSetDecoder<TTo>(
|
|
109
|
+
item: Decoder<TTo>,
|
|
110
|
+
config: SetCodecConfig<NumberDecoder> & { size: 0 },
|
|
111
|
+
): FixedSizeDecoder<Set<TTo>, 0>;
|
|
112
|
+
export function getSetDecoder<TTo>(
|
|
113
|
+
item: FixedSizeDecoder<TTo>,
|
|
114
|
+
config: SetCodecConfig<NumberDecoder> & { size: number },
|
|
115
|
+
): FixedSizeDecoder<Set<TTo>>;
|
|
116
|
+
export function getSetDecoder<TTo>(
|
|
117
|
+
item: Decoder<TTo>,
|
|
118
|
+
config?: SetCodecConfig<NumberDecoder>,
|
|
119
|
+
): VariableSizeDecoder<Set<TTo>>;
|
|
120
|
+
export function getSetDecoder<TTo>(item: Decoder<TTo>, config: SetCodecConfig<NumberDecoder> = {}): Decoder<Set<TTo>> {
|
|
121
|
+
return transformDecoder(getArrayDecoder(item, config as object), (entries: TTo[]): Set<TTo> => new Set(entries));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Returns a codec for encoding and decoding sets of items.
|
|
126
|
+
*
|
|
127
|
+
* This codec serializes `Set<T>` values by encoding each item using the provided item codec.
|
|
128
|
+
* The number of items is stored as a prefix using a `u32` codec by default.
|
|
129
|
+
*
|
|
130
|
+
* @typeParam TFrom - The type of the items in the set before encoding.
|
|
131
|
+
* @typeParam TTo - The type of the items in the set after decoding.
|
|
132
|
+
*
|
|
133
|
+
* @param item - The codec to use for each set item.
|
|
134
|
+
* @param config - Optional configuration specifying the size strategy.
|
|
135
|
+
* @returns A `Codec<Set<TFrom>, Set<TTo>>` for encoding and decoding sets.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* Encoding and decoding a set of `u8` numbers.
|
|
139
|
+
* ```ts
|
|
140
|
+
* const codec = getSetCodec(getU8Codec());
|
|
141
|
+
* const bytes = codec.encode(new Set([1, 2, 3]));
|
|
142
|
+
* // 0x03000000010203
|
|
143
|
+
* // | └-- 3 items of 1 byte each.
|
|
144
|
+
* // └-- 4-byte prefix indicating 3 items.
|
|
145
|
+
*
|
|
146
|
+
* const value = codec.decode(bytes);
|
|
147
|
+
* // new Set([1, 2, 3])
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* Using a `u16` prefix for size.
|
|
152
|
+
* ```ts
|
|
153
|
+
* const codec = getSetCodec(getU8Codec(), { size: getU16Codec() });
|
|
154
|
+
* const bytes = codec.encode(new Set([1, 2, 3]));
|
|
155
|
+
* // 0x0300010203
|
|
156
|
+
* // | └-- 3 items of 1 byte each.
|
|
157
|
+
* // └-- 2-byte prefix indicating 3 items.
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* Using a fixed-size set.
|
|
162
|
+
* ```ts
|
|
163
|
+
* const codec = getSetCodec(getU8Codec(), { size: 3 });
|
|
164
|
+
* const bytes = codec.encode(new Set([1, 2, 3]));
|
|
165
|
+
* // 0x010203
|
|
166
|
+
* // └-- Exactly 3 items of 1 byte each.
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* Using remainder to infer set size.
|
|
171
|
+
* ```ts
|
|
172
|
+
* const codec = getSetCodec(getU8Codec(), { size: 'remainder' });
|
|
173
|
+
* const bytes = codec.encode(new Set([1, 2, 3]));
|
|
174
|
+
* // 0x010203
|
|
175
|
+
* // └-- 3 items of 1 byte each. The size is inferred from the remaining bytes.
|
|
176
|
+
* ```
|
|
177
|
+
*
|
|
178
|
+
* @remarks
|
|
179
|
+
* Separate {@link getSetEncoder} and {@link getSetDecoder} functions are available.
|
|
180
|
+
*
|
|
181
|
+
* ```ts
|
|
182
|
+
* const bytes = getSetEncoder(getU8Encoder()).encode(new Set([1, 2, 3]));
|
|
183
|
+
* const value = getSetDecoder(getU8Decoder()).decode(bytes);
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* @see {@link getSetEncoder}
|
|
187
|
+
* @see {@link getSetDecoder}
|
|
188
|
+
*/
|
|
189
|
+
export function getSetCodec<TFrom, TTo extends TFrom = TFrom>(
|
|
190
|
+
item: Codec<TFrom, TTo>,
|
|
191
|
+
config: SetCodecConfig<NumberCodec> & { size: 0 },
|
|
192
|
+
): FixedSizeCodec<Set<TFrom>, Set<TTo>, 0>;
|
|
193
|
+
export function getSetCodec<TFrom, TTo extends TFrom = TFrom>(
|
|
194
|
+
item: FixedSizeCodec<TFrom, TTo>,
|
|
195
|
+
config: SetCodecConfig<NumberCodec> & { size: number },
|
|
196
|
+
): FixedSizeCodec<Set<TFrom>, Set<TTo>>;
|
|
197
|
+
export function getSetCodec<TFrom, TTo extends TFrom = TFrom>(
|
|
198
|
+
item: Codec<TFrom, TTo>,
|
|
199
|
+
config?: SetCodecConfig<NumberCodec>,
|
|
200
|
+
): VariableSizeCodec<Set<TFrom>, Set<TTo>>;
|
|
201
|
+
export function getSetCodec<TFrom, TTo extends TFrom = TFrom>(
|
|
202
|
+
item: Codec<TFrom, TTo>,
|
|
203
|
+
config: SetCodecConfig<NumberCodec> = {},
|
|
204
|
+
): Codec<Set<TFrom>, Set<TTo>> {
|
|
205
|
+
return combineCodec(getSetEncoder(item, config as object), getSetDecoder(item, config as object));
|
|
206
|
+
}
|
package/src/struct.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import {
|
|
3
|
+
Codec,
|
|
4
|
+
combineCodec,
|
|
5
|
+
createDecoder,
|
|
6
|
+
createEncoder,
|
|
7
|
+
Decoder,
|
|
8
|
+
Encoder,
|
|
9
|
+
FixedSizeCodec,
|
|
10
|
+
FixedSizeDecoder,
|
|
11
|
+
FixedSizeEncoder,
|
|
12
|
+
getEncodedSize,
|
|
13
|
+
ReadonlyUint8Array,
|
|
14
|
+
VariableSizeCodec,
|
|
15
|
+
VariableSizeDecoder,
|
|
16
|
+
VariableSizeEncoder,
|
|
17
|
+
} from '@solana/codecs-core';
|
|
18
|
+
|
|
19
|
+
import { DrainOuterGeneric, getFixedSize, getMaxSize, sumCodecSizes } from './utils';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents a collection of named fields used in struct codecs.
|
|
23
|
+
*
|
|
24
|
+
* Each field is defined as a tuple containing:
|
|
25
|
+
* - A string key representing the field name.
|
|
26
|
+
* - A codec used to encode and decode the field's value.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam T - The codec type used for each field.
|
|
29
|
+
*/
|
|
30
|
+
type Fields<T> = readonly (readonly [string, T])[];
|
|
31
|
+
|
|
32
|
+
type ArrayIndices<T extends readonly unknown[]> = Exclude<Partial<T>['length'], T['length']> & number;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Infers the TypeScript type for an object that can be encoded using a struct codec.
|
|
36
|
+
*
|
|
37
|
+
* This type maps the provided field encoders to their corresponding values.
|
|
38
|
+
*
|
|
39
|
+
* @typeParam TFields - The fields of the struct, each paired with an encoder.
|
|
40
|
+
*/
|
|
41
|
+
type GetEncoderTypeFromFields<TFields extends Fields<Encoder<any>>> = DrainOuterGeneric<{
|
|
42
|
+
[I in ArrayIndices<TFields> as TFields[I][0]]: TFields[I][1] extends Encoder<infer TFrom> ? TFrom : never;
|
|
43
|
+
}>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Infers the TypeScript type for an object that can be decoded using a struct codec.
|
|
47
|
+
*
|
|
48
|
+
* This type maps the provided field decoders to their corresponding values.
|
|
49
|
+
*
|
|
50
|
+
* @typeParam TFields - The fields of the struct, each paired with a decoder.
|
|
51
|
+
*/
|
|
52
|
+
type GetDecoderTypeFromFields<TFields extends Fields<Decoder<any>>> = DrainOuterGeneric<{
|
|
53
|
+
[I in ArrayIndices<TFields> as TFields[I][0]]: TFields[I][1] extends Decoder<infer TTo> ? TTo : never;
|
|
54
|
+
}>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Returns an encoder for custom objects.
|
|
58
|
+
*
|
|
59
|
+
* This encoder serializes an object by encoding its fields sequentially,
|
|
60
|
+
* using the provided field encoders.
|
|
61
|
+
*
|
|
62
|
+
* For more details, see {@link getStructCodec}.
|
|
63
|
+
*
|
|
64
|
+
* @typeParam TFields - The fields of the struct, each paired with an encoder.
|
|
65
|
+
*
|
|
66
|
+
* @param fields - The name and encoder of each field.
|
|
67
|
+
* @returns A `FixedSizeEncoder` or `VariableSizeEncoder` for encoding custom objects.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* Encoding a custom struct.
|
|
71
|
+
* ```ts
|
|
72
|
+
* const encoder = getStructEncoder([
|
|
73
|
+
* ['name', fixCodecSize(getUtf8Encoder(), 5)],
|
|
74
|
+
* ['age', getU8Encoder()]
|
|
75
|
+
* ]);
|
|
76
|
+
*
|
|
77
|
+
* const bytes = encoder.encode({ name: 'Alice', age: 42 });
|
|
78
|
+
* // 0x416c6963652a
|
|
79
|
+
* // | └── Age (42)
|
|
80
|
+
* // └── Name ("Alice")
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @see {@link getStructCodec}
|
|
84
|
+
*/
|
|
85
|
+
export function getStructEncoder<const TFields extends Fields<FixedSizeEncoder<any>>>(
|
|
86
|
+
fields: TFields,
|
|
87
|
+
): FixedSizeEncoder<GetEncoderTypeFromFields<TFields>>;
|
|
88
|
+
export function getStructEncoder<const TFields extends Fields<Encoder<any>>>(
|
|
89
|
+
fields: TFields,
|
|
90
|
+
): VariableSizeEncoder<GetEncoderTypeFromFields<TFields>>;
|
|
91
|
+
export function getStructEncoder<const TFields extends Fields<Encoder<any>>>(
|
|
92
|
+
fields: TFields,
|
|
93
|
+
): Encoder<GetEncoderTypeFromFields<TFields>> {
|
|
94
|
+
type TFrom = GetEncoderTypeFromFields<TFields>;
|
|
95
|
+
const fieldCodecs = fields.map(([, codec]) => codec);
|
|
96
|
+
const fixedSize = sumCodecSizes(fieldCodecs.map(getFixedSize));
|
|
97
|
+
const maxSize = sumCodecSizes(fieldCodecs.map(getMaxSize)) ?? undefined;
|
|
98
|
+
|
|
99
|
+
return createEncoder({
|
|
100
|
+
...(fixedSize === null
|
|
101
|
+
? {
|
|
102
|
+
getSizeFromValue: (value: TFrom) =>
|
|
103
|
+
fields
|
|
104
|
+
.map(([key, codec]) => getEncodedSize(value[key as keyof TFrom], codec))
|
|
105
|
+
.reduce((all, one) => all + one, 0),
|
|
106
|
+
maxSize,
|
|
107
|
+
}
|
|
108
|
+
: { fixedSize }),
|
|
109
|
+
write: (struct: TFrom, bytes, offset) => {
|
|
110
|
+
fields.forEach(([key, codec]) => {
|
|
111
|
+
offset = codec.write(struct[key as keyof TFrom], bytes, offset);
|
|
112
|
+
});
|
|
113
|
+
return offset;
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns a decoder for custom objects.
|
|
120
|
+
*
|
|
121
|
+
* This decoder deserializes an object by decoding its fields sequentially,
|
|
122
|
+
* using the provided field decoders.
|
|
123
|
+
*
|
|
124
|
+
* For more details, see {@link getStructCodec}.
|
|
125
|
+
*
|
|
126
|
+
* @typeParam TFields - The fields of the struct, each paired with a decoder.
|
|
127
|
+
*
|
|
128
|
+
* @param fields - The name and decoder of each field.
|
|
129
|
+
* @returns A `FixedSizeDecoder` or `VariableSizeDecoder` for decoding custom objects.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* Decoding a custom struct.
|
|
133
|
+
* ```ts
|
|
134
|
+
* const decoder = getStructDecoder([
|
|
135
|
+
* ['name', fixCodecSize(getUtf8Decoder(), 5)],
|
|
136
|
+
* ['age', getU8Decoder()]
|
|
137
|
+
* ]);
|
|
138
|
+
*
|
|
139
|
+
* const struct = decoder.decode(new Uint8Array([
|
|
140
|
+
* 0x41,0x6c,0x69,0x63,0x65,0x2a
|
|
141
|
+
* ]));
|
|
142
|
+
* // { name: 'Alice', age: 42 }
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* @see {@link getStructCodec}
|
|
146
|
+
*/
|
|
147
|
+
export function getStructDecoder<const TFields extends Fields<FixedSizeDecoder<any>>>(
|
|
148
|
+
fields: TFields,
|
|
149
|
+
): FixedSizeDecoder<GetDecoderTypeFromFields<TFields>>;
|
|
150
|
+
export function getStructDecoder<const TFields extends Fields<Decoder<any>>>(
|
|
151
|
+
fields: TFields,
|
|
152
|
+
): VariableSizeDecoder<GetDecoderTypeFromFields<TFields>>;
|
|
153
|
+
export function getStructDecoder<const TFields extends Fields<Decoder<any>>>(
|
|
154
|
+
fields: TFields,
|
|
155
|
+
): Decoder<GetDecoderTypeFromFields<TFields>> {
|
|
156
|
+
type TTo = GetDecoderTypeFromFields<TFields>;
|
|
157
|
+
const fieldCodecs = fields.map(([, codec]) => codec);
|
|
158
|
+
const fixedSize = sumCodecSizes(fieldCodecs.map(getFixedSize));
|
|
159
|
+
const maxSize = sumCodecSizes(fieldCodecs.map(getMaxSize)) ?? undefined;
|
|
160
|
+
|
|
161
|
+
return createDecoder({
|
|
162
|
+
...(fixedSize === null ? { maxSize } : { fixedSize }),
|
|
163
|
+
read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => {
|
|
164
|
+
const struct = {} as TTo;
|
|
165
|
+
fields.forEach(([key, codec]) => {
|
|
166
|
+
const [value, newOffset] = codec.read(bytes, offset);
|
|
167
|
+
offset = newOffset;
|
|
168
|
+
struct[key as keyof TTo] = value;
|
|
169
|
+
});
|
|
170
|
+
return [struct, offset];
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Returns a codec for encoding and decoding custom objects.
|
|
177
|
+
*
|
|
178
|
+
* This codec serializes objects by encoding and decoding each field sequentially.
|
|
179
|
+
*
|
|
180
|
+
* @typeParam TFields - The fields of the struct, each paired with a codec.
|
|
181
|
+
*
|
|
182
|
+
* @param fields - The name and codec of each field.
|
|
183
|
+
* @returns A `FixedSizeCodec` or `VariableSizeCodec` for encoding and decoding custom objects.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* Encoding and decoding a custom struct.
|
|
187
|
+
* ```ts
|
|
188
|
+
* const codec = getStructCodec([
|
|
189
|
+
* ['name', fixCodecSize(getUtf8Codec(), 5)],
|
|
190
|
+
* ['age', getU8Codec()]
|
|
191
|
+
* ]);
|
|
192
|
+
*
|
|
193
|
+
* const bytes = codec.encode({ name: 'Alice', age: 42 });
|
|
194
|
+
* // 0x416c6963652a
|
|
195
|
+
* // | └── Age (42)
|
|
196
|
+
* // └── Name ("Alice")
|
|
197
|
+
*
|
|
198
|
+
* const struct = codec.decode(bytes);
|
|
199
|
+
* // { name: 'Alice', age: 42 }
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* @remarks
|
|
203
|
+
* Separate {@link getStructEncoder} and {@link getStructDecoder} functions are available.
|
|
204
|
+
*
|
|
205
|
+
* ```ts
|
|
206
|
+
* const bytes = getStructEncoder([
|
|
207
|
+
* ['name', fixCodecSize(getUtf8Encoder(), 5)],
|
|
208
|
+
* ['age', getU8Encoder()]
|
|
209
|
+
* ]).encode({ name: 'Alice', age: 42 });
|
|
210
|
+
*
|
|
211
|
+
* const struct = getStructDecoder([
|
|
212
|
+
* ['name', fixCodecSize(getUtf8Decoder(), 5)],
|
|
213
|
+
* ['age', getU8Decoder()]
|
|
214
|
+
* ]).decode(bytes);
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
217
|
+
* @see {@link getStructEncoder}
|
|
218
|
+
* @see {@link getStructDecoder}
|
|
219
|
+
*/
|
|
220
|
+
export function getStructCodec<const TFields extends Fields<FixedSizeCodec<any>>>(
|
|
221
|
+
fields: TFields,
|
|
222
|
+
): FixedSizeCodec<
|
|
223
|
+
GetEncoderTypeFromFields<TFields>,
|
|
224
|
+
GetDecoderTypeFromFields<TFields> & GetEncoderTypeFromFields<TFields>
|
|
225
|
+
>;
|
|
226
|
+
export function getStructCodec<const TFields extends Fields<Codec<any>>>(
|
|
227
|
+
fields: TFields,
|
|
228
|
+
): VariableSizeCodec<
|
|
229
|
+
GetEncoderTypeFromFields<TFields>,
|
|
230
|
+
GetDecoderTypeFromFields<TFields> & GetEncoderTypeFromFields<TFields>
|
|
231
|
+
>;
|
|
232
|
+
export function getStructCodec<const TFields extends Fields<Codec<any>>>(
|
|
233
|
+
fields: TFields,
|
|
234
|
+
): Codec<GetEncoderTypeFromFields<TFields>, GetDecoderTypeFromFields<TFields> & GetEncoderTypeFromFields<TFields>> {
|
|
235
|
+
return combineCodec(
|
|
236
|
+
getStructEncoder(fields),
|
|
237
|
+
getStructDecoder(fields) as Decoder<GetDecoderTypeFromFields<TFields> & GetEncoderTypeFromFields<TFields>>,
|
|
238
|
+
);
|
|
239
|
+
}
|