@solana/codecs-data-structures 6.3.1 → 6.3.2-canary-20260313143218

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/src/enum.ts ADDED
@@ -0,0 +1,309 @@
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 {
16
+ FixedSizeNumberCodec,
17
+ FixedSizeNumberDecoder,
18
+ FixedSizeNumberEncoder,
19
+ getU8Decoder,
20
+ getU8Encoder,
21
+ NumberCodec,
22
+ NumberDecoder,
23
+ NumberEncoder,
24
+ } from '@solana/codecs-numbers';
25
+ import {
26
+ SOLANA_ERROR__CODECS__CANNOT_USE_LEXICAL_VALUES_AS_ENUM_DISCRIMINATORS,
27
+ SOLANA_ERROR__CODECS__ENUM_DISCRIMINATOR_OUT_OF_RANGE,
28
+ SOLANA_ERROR__CODECS__INVALID_ENUM_VARIANT,
29
+ SolanaError,
30
+ } from '@solana/errors';
31
+
32
+ import {
33
+ EnumLookupObject,
34
+ formatNumericalValues,
35
+ GetEnumFrom,
36
+ getEnumIndexFromDiscriminator,
37
+ getEnumIndexFromVariant,
38
+ getEnumStats,
39
+ GetEnumTo,
40
+ } from './enum-helpers';
41
+
42
+ /**
43
+ * Defines the configuration options for enum codecs.
44
+ *
45
+ * The `size` option determines the numerical encoding used for the enum's discriminant.
46
+ * By default, enums are stored as a `u8` (1 byte).
47
+ *
48
+ * The `useValuesAsDiscriminators` option allows mapping the actual enum values
49
+ * as discriminators instead of using their positional index.
50
+ *
51
+ * @typeParam TDiscriminator - A number codec, encoder, or decoder used for the discriminant.
52
+ */
53
+ export type EnumCodecConfig<TDiscriminator extends NumberCodec | NumberDecoder | NumberEncoder> = {
54
+ /**
55
+ * The codec used to encode/decode the enum discriminator.
56
+ * @defaultValue `u8` discriminator.
57
+ */
58
+ size?: TDiscriminator;
59
+
60
+ /**
61
+ * If set to `true`, the enum values themselves will be used as discriminators.
62
+ * This is only valid for numerical enum values.
63
+ *
64
+ * @defaultValue `false`
65
+ */
66
+ useValuesAsDiscriminators?: boolean;
67
+ };
68
+
69
+ /**
70
+ * Returns an encoder for enums.
71
+ *
72
+ * This encoder serializes enums as a numerical discriminator.
73
+ * By default, the discriminator is based on the positional index of the enum variants.
74
+ *
75
+ * For more details, see {@link getEnumCodec}.
76
+ *
77
+ * @typeParam TEnum - The TypeScript enum or object mapping enum keys to values.
78
+ *
79
+ * @param constructor - The constructor of the enum.
80
+ * @param config - Configuration options for encoding the enum.
81
+ * @returns A `FixedSizeEncoder` or `VariableSizeEncoder` for encoding enums.
82
+ *
83
+ * @example
84
+ * Encoding enum values.
85
+ * ```ts
86
+ * enum Direction { Up, Down, Left, Right }
87
+ * const encoder = getEnumEncoder(Direction);
88
+ *
89
+ * encoder.encode(Direction.Up); // 0x00
90
+ * encoder.encode(Direction.Down); // 0x01
91
+ * encoder.encode(Direction.Left); // 0x02
92
+ * encoder.encode(Direction.Right); // 0x03
93
+ * ```
94
+ *
95
+ * @see {@link getEnumCodec}
96
+ */
97
+ export function getEnumEncoder<TEnum extends EnumLookupObject>(
98
+ constructor: TEnum,
99
+ config?: Omit<EnumCodecConfig<NumberEncoder>, 'size'>,
100
+ ): FixedSizeEncoder<GetEnumFrom<TEnum>, 1>;
101
+ export function getEnumEncoder<TEnum extends EnumLookupObject, TSize extends number>(
102
+ constructor: TEnum,
103
+ config: EnumCodecConfig<NumberEncoder> & { size: FixedSizeNumberEncoder<TSize> },
104
+ ): FixedSizeEncoder<GetEnumFrom<TEnum>, TSize>;
105
+ export function getEnumEncoder<TEnum extends EnumLookupObject>(
106
+ constructor: TEnum,
107
+ config?: EnumCodecConfig<NumberEncoder>,
108
+ ): VariableSizeEncoder<GetEnumFrom<TEnum>>;
109
+ export function getEnumEncoder<TEnum extends EnumLookupObject>(
110
+ constructor: TEnum,
111
+ config: EnumCodecConfig<NumberEncoder> = {},
112
+ ): Encoder<GetEnumFrom<TEnum>> {
113
+ const prefix = config.size ?? getU8Encoder();
114
+ const useValuesAsDiscriminators = config.useValuesAsDiscriminators ?? false;
115
+ const { enumKeys, enumValues, numericalValues, stringValues } = getEnumStats(constructor);
116
+ if (useValuesAsDiscriminators && enumValues.some(value => typeof value === 'string')) {
117
+ throw new SolanaError(SOLANA_ERROR__CODECS__CANNOT_USE_LEXICAL_VALUES_AS_ENUM_DISCRIMINATORS, {
118
+ stringValues: enumValues.filter((v): v is string => typeof v === 'string'),
119
+ });
120
+ }
121
+ return transformEncoder(prefix, (variant: GetEnumFrom<TEnum>): number => {
122
+ const index = getEnumIndexFromVariant({ enumKeys, enumValues, variant });
123
+ if (index < 0) {
124
+ throw new SolanaError(SOLANA_ERROR__CODECS__INVALID_ENUM_VARIANT, {
125
+ formattedNumericalValues: formatNumericalValues(numericalValues),
126
+ numericalValues,
127
+ stringValues,
128
+ variant,
129
+ });
130
+ }
131
+ return useValuesAsDiscriminators ? (enumValues[index] as number) : index;
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Returns a decoder for enums.
137
+ *
138
+ * This decoder deserializes enums from a numerical discriminator.
139
+ * By default, the discriminator is based on the positional index of the enum variants.
140
+ *
141
+ * For more details, see {@link getEnumCodec}.
142
+ *
143
+ * @typeParam TEnum - The TypeScript enum or object mapping enum keys to values.
144
+ *
145
+ * @param constructor - The constructor of the enum.
146
+ * @param config - Configuration options for decoding the enum.
147
+ * @returns A `FixedSizeDecoder` or `VariableSizeDecoder` for decoding enums.
148
+ *
149
+ * @example
150
+ * Decoding enum values.
151
+ * ```ts
152
+ * enum Direction { Up, Down, Left, Right }
153
+ * const decoder = getEnumDecoder(Direction);
154
+ *
155
+ * decoder.decode(new Uint8Array([0x00])); // Direction.Up
156
+ * decoder.decode(new Uint8Array([0x01])); // Direction.Down
157
+ * decoder.decode(new Uint8Array([0x02])); // Direction.Left
158
+ * decoder.decode(new Uint8Array([0x03])); // Direction.Right
159
+ * ```
160
+ *
161
+ * @see {@link getEnumCodec}
162
+ */
163
+ export function getEnumDecoder<TEnum extends EnumLookupObject>(
164
+ constructor: TEnum,
165
+ config?: Omit<EnumCodecConfig<NumberDecoder>, 'size'>,
166
+ ): FixedSizeDecoder<GetEnumTo<TEnum>, 1>;
167
+ export function getEnumDecoder<TEnum extends EnumLookupObject, TSize extends number>(
168
+ constructor: TEnum,
169
+ config: EnumCodecConfig<NumberDecoder> & { size: FixedSizeNumberDecoder<TSize> },
170
+ ): FixedSizeDecoder<GetEnumTo<TEnum>, TSize>;
171
+ export function getEnumDecoder<TEnum extends EnumLookupObject>(
172
+ constructor: TEnum,
173
+ config?: EnumCodecConfig<NumberDecoder>,
174
+ ): VariableSizeDecoder<GetEnumTo<TEnum>>;
175
+ export function getEnumDecoder<TEnum extends EnumLookupObject>(
176
+ constructor: TEnum,
177
+ config: EnumCodecConfig<NumberDecoder> = {},
178
+ ): Decoder<GetEnumTo<TEnum>> {
179
+ const prefix = config.size ?? getU8Decoder();
180
+ const useValuesAsDiscriminators = config.useValuesAsDiscriminators ?? false;
181
+ const { enumKeys, enumValues, numericalValues } = getEnumStats(constructor);
182
+ if (useValuesAsDiscriminators && enumValues.some(value => typeof value === 'string')) {
183
+ throw new SolanaError(SOLANA_ERROR__CODECS__CANNOT_USE_LEXICAL_VALUES_AS_ENUM_DISCRIMINATORS, {
184
+ stringValues: enumValues.filter((v): v is string => typeof v === 'string'),
185
+ });
186
+ }
187
+ return transformDecoder(prefix, (value: bigint | number): GetEnumTo<TEnum> => {
188
+ const discriminator = Number(value);
189
+ const index = getEnumIndexFromDiscriminator({
190
+ discriminator,
191
+ enumKeys,
192
+ enumValues,
193
+ useValuesAsDiscriminators,
194
+ });
195
+ if (index < 0) {
196
+ const validDiscriminators = useValuesAsDiscriminators
197
+ ? numericalValues
198
+ : [...Array(enumKeys.length).keys()];
199
+ throw new SolanaError(SOLANA_ERROR__CODECS__ENUM_DISCRIMINATOR_OUT_OF_RANGE, {
200
+ discriminator,
201
+ formattedValidDiscriminators: formatNumericalValues(validDiscriminators),
202
+ validDiscriminators,
203
+ });
204
+ }
205
+ return enumValues[index] as GetEnumTo<TEnum>;
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Returns a codec for encoding and decoding enums.
211
+ *
212
+ * This codec serializes enums as a numerical discriminator, allowing them
213
+ * to be efficiently stored and reconstructed from binary data.
214
+ *
215
+ * By default, the discriminator is derived from the positional index
216
+ * of the enum variant, but it can be configured to use the enum's numeric values instead.
217
+ *
218
+ * @typeParam TEnum - The TypeScript enum or object mapping enum keys to values.
219
+ *
220
+ * @param constructor - The constructor of the enum.
221
+ * @param config - Configuration options for encoding and decoding the enum.
222
+ * @returns A `FixedSizeCodec` or `VariableSizeCodec` for encoding and decoding enums.
223
+ *
224
+ * @example
225
+ * Encoding and decoding enums using positional indexes.
226
+ * ```ts
227
+ * enum Direction { Up, Down, Left, Right }
228
+ * const codec = getEnumCodec(Direction);
229
+ *
230
+ * codec.encode(Direction.Up); // 0x00
231
+ * codec.encode(Direction.Down); // 0x01
232
+ * codec.encode(Direction.Left); // 0x02
233
+ * codec.encode(Direction.Right); // 0x03
234
+ *
235
+ * codec.decode(new Uint8Array([0x00])); // Direction.Up
236
+ * codec.decode(new Uint8Array([0x01])); // Direction.Down
237
+ * codec.decode(new Uint8Array([0x02])); // Direction.Left
238
+ * codec.decode(new Uint8Array([0x03])); // Direction.Right
239
+ * ```
240
+ *
241
+ * @example
242
+ * Encoding and decoding enums using their numeric values.
243
+ * ```ts
244
+ * enum GameDifficulty { Easy = 1, Normal = 4, Hard = 7, Expert = 9 }
245
+ * const codec = getEnumCodec(GameDifficulty, { useValuesAsDiscriminators: true });
246
+ *
247
+ * codec.encode(GameDifficulty.Easy); // 0x01
248
+ * codec.encode(GameDifficulty.Normal); // 0x04
249
+ * codec.encode(GameDifficulty.Hard); // 0x07
250
+ * codec.encode(GameDifficulty.Expert); // 0x09
251
+ *
252
+ * codec.decode(new Uint8Array([0x01])); // GameDifficulty.Easy
253
+ * codec.decode(new Uint8Array([0x04])); // GameDifficulty.Normal
254
+ * codec.decode(new Uint8Array([0x07])); // GameDifficulty.Hard
255
+ * codec.decode(new Uint8Array([0x09])); // GameDifficulty.Expert
256
+ * ```
257
+ *
258
+ * Note that, when using values as discriminators, the enum values must be numerical.
259
+ * Otherwise, an error will be thrown.
260
+ *
261
+ * ```ts
262
+ * enum GameDifficulty { Easy = 'EASY', Normal = 'NORMAL', Hard = 'HARD' }
263
+ * getEnumCodec(GameDifficulty, { useValuesAsDiscriminators: true }); // Throws an error.
264
+ * ```
265
+ *
266
+ * @example
267
+ * Using a custom discriminator size.
268
+ * ```ts
269
+ * enum Status { Pending, Approved, Rejected }
270
+ * const codec = getEnumCodec(Status, { size: getU16Codec() });
271
+ *
272
+ * codec.encode(Status.Pending); // 0x0000
273
+ * codec.encode(Status.Approved); // 0x0100
274
+ * codec.encode(Status.Rejected); // 0x0200
275
+ *
276
+ * codec.decode(new Uint8Array([0x00, 0x00])); // Status.Pending
277
+ * codec.decode(new Uint8Array([0x01, 0x00])); // Status.Approved
278
+ * codec.decode(new Uint8Array([0x02, 0x00])); // Status.Rejected
279
+ * ```
280
+ *
281
+ * @remarks
282
+ * Separate {@link getEnumEncoder} and {@link getEnumDecoder} functions are available.
283
+ *
284
+ * ```ts
285
+ * const bytes = getEnumEncoder(Direction).encode(Direction.Up);
286
+ * const value = getEnumDecoder(Direction).decode(bytes);
287
+ * ```
288
+ *
289
+ * @see {@link getEnumEncoder}
290
+ * @see {@link getEnumDecoder}
291
+ */
292
+ export function getEnumCodec<TEnum extends EnumLookupObject>(
293
+ constructor: TEnum,
294
+ config?: Omit<EnumCodecConfig<NumberCodec>, 'size'>,
295
+ ): FixedSizeCodec<GetEnumFrom<TEnum>, GetEnumTo<TEnum>, 1>;
296
+ export function getEnumCodec<TEnum extends EnumLookupObject, TSize extends number>(
297
+ constructor: TEnum,
298
+ config: EnumCodecConfig<NumberCodec> & { size: FixedSizeNumberCodec<TSize> },
299
+ ): FixedSizeCodec<GetEnumFrom<TEnum>, GetEnumTo<TEnum>, TSize>;
300
+ export function getEnumCodec<TEnum extends EnumLookupObject>(
301
+ constructor: TEnum,
302
+ config?: EnumCodecConfig<NumberCodec>,
303
+ ): VariableSizeCodec<GetEnumFrom<TEnum>, GetEnumTo<TEnum>>;
304
+ export function getEnumCodec<TEnum extends EnumLookupObject>(
305
+ constructor: TEnum,
306
+ config: EnumCodecConfig<NumberCodec> = {},
307
+ ): Codec<GetEnumFrom<TEnum>, GetEnumTo<TEnum>> {
308
+ return combineCodec(getEnumEncoder(constructor, config), getEnumDecoder(constructor, config));
309
+ }
@@ -0,0 +1,180 @@
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
+
16
+ import { getTupleDecoder, getTupleEncoder } from './tuple';
17
+
18
+ /**
19
+ * Returns an encoder that prefixes encoded values with hidden data.
20
+ *
21
+ * This encoder applies a list of void encoders before encoding the main value.
22
+ * The prefixed data is encoded before the main value without being exposed to the user.
23
+ *
24
+ * For more details, see {@link getHiddenPrefixCodec}.
25
+ *
26
+ * @typeParam TFrom - The type of the main value being encoded.
27
+ *
28
+ * @param encoder - The encoder for the main value.
29
+ * @param prefixedEncoders - A list of void encoders that produce the hidden prefix.
30
+ * @returns A `FixedSizeEncoder` or `VariableSizeEncoder` that encodes the value with a hidden prefix.
31
+ *
32
+ * @example
33
+ * Prefixing a value with constants.
34
+ * ```ts
35
+ * const encoder = getHiddenPrefixEncoder(getUtf8Encoder(), [
36
+ * getConstantCodec(new Uint8Array([1, 2, 3])),
37
+ * getConstantCodec(new Uint8Array([4, 5, 6])),
38
+ * ]);
39
+ *
40
+ * encoder.encode('Hello');
41
+ * // 0x01020304050648656c6c6f
42
+ * // | | └-- Our encoded value ("Hello").
43
+ * // | └-- Our second hidden prefix.
44
+ * // └-- Our first hidden prefix.
45
+ * ```
46
+ *
47
+ * @see {@link getHiddenPrefixCodec}
48
+ */
49
+ export function getHiddenPrefixEncoder<TFrom>(
50
+ encoder: FixedSizeEncoder<TFrom>,
51
+ prefixedEncoders: readonly FixedSizeEncoder<void>[],
52
+ ): FixedSizeEncoder<TFrom>;
53
+ export function getHiddenPrefixEncoder<TFrom>(
54
+ encoder: Encoder<TFrom>,
55
+ prefixedEncoders: readonly Encoder<void>[],
56
+ ): VariableSizeEncoder<TFrom>;
57
+ export function getHiddenPrefixEncoder<TFrom>(
58
+ encoder: Encoder<TFrom>,
59
+ prefixedEncoders: readonly Encoder<void>[],
60
+ ): Encoder<TFrom> {
61
+ return transformEncoder(
62
+ getTupleEncoder([...prefixedEncoders, encoder]) as Encoder<readonly [...void[], TFrom]>,
63
+ (value: TFrom) => [...prefixedEncoders.map(() => undefined), value] as const,
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Returns a decoder that skips hidden prefixed data before decoding the main value.
69
+ *
70
+ * This decoder applies a list of void decoders before decoding the main value.
71
+ * The prefixed data is skipped during decoding without being exposed to the user.
72
+ *
73
+ * For more details, see {@link getHiddenPrefixCodec}.
74
+ *
75
+ * @typeParam TTo - The type of the main value being decoded.
76
+ *
77
+ * @param decoder - The decoder for the main value.
78
+ * @param prefixedDecoders - A list of void decoders that produce the hidden prefix.
79
+ * @returns A `FixedSizeDecoder` or `VariableSizeDecoder` that decodes values while ignoring the hidden prefix.
80
+ *
81
+ * @example
82
+ * Decoding a value with prefixed constants.
83
+ * ```ts
84
+ * const decoder = getHiddenPrefixDecoder(getUtf8Decoder(), [
85
+ * getConstantCodec(new Uint8Array([1, 2, 3])),
86
+ * getConstantCodec(new Uint8Array([4, 5, 6])),
87
+ * ]);
88
+ *
89
+ * decoder.decode(new Uint8Array([1, 2, 3, 4, 5, 6, 0x48, 0x65, 0x6C, 0x6C, 0x6F]));
90
+ * // 'Hello'
91
+ * ```
92
+ *
93
+ * @see {@link getHiddenPrefixCodec}
94
+ */
95
+ export function getHiddenPrefixDecoder<TTo>(
96
+ decoder: FixedSizeDecoder<TTo>,
97
+ prefixedDecoders: readonly FixedSizeDecoder<void>[],
98
+ ): FixedSizeDecoder<TTo>;
99
+ export function getHiddenPrefixDecoder<TTo>(
100
+ decoder: Decoder<TTo>,
101
+ prefixedDecoders: readonly Decoder<void>[],
102
+ ): VariableSizeDecoder<TTo>;
103
+ export function getHiddenPrefixDecoder<TTo>(
104
+ decoder: Decoder<TTo>,
105
+ prefixedDecoders: readonly Decoder<void>[],
106
+ ): Decoder<TTo> {
107
+ return transformDecoder(
108
+ getTupleDecoder([...prefixedDecoders, decoder]) as Decoder<readonly [...void[], TTo]>,
109
+ tuple => tuple[tuple.length - 1] as TTo,
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Returns a codec that encodes and decodes values with a hidden prefix.
115
+ *
116
+ * - **Encoding:** Prefixes the value with hidden data before encoding.
117
+ * - **Decoding:** Skips the hidden prefix before decoding the main value.
118
+ *
119
+ * This is useful for any implicit metadata that should be present in
120
+ * binary formats but omitted from the API.
121
+ *
122
+ * @typeParam TFrom - The type of the main value being encoded.
123
+ * @typeParam TTo - The type of the main value being decoded.
124
+ *
125
+ * @param codec - The codec for the main value.
126
+ * @param prefixedCodecs - A list of void codecs that produce the hidden prefix.
127
+ * @returns A `FixedSizeCodec` or `VariableSizeCodec` for encoding and decoding values with a hidden prefix.
128
+ *
129
+ * @example
130
+ * Encoding and decoding a value with prefixed constants.
131
+ * ```ts
132
+ * const codec = getHiddenPrefixCodec(getUtf8Codec(), [
133
+ * getConstantCodec(new Uint8Array([1, 2, 3])),
134
+ * getConstantCodec(new Uint8Array([4, 5, 6])),
135
+ * ]);
136
+ *
137
+ * const bytes = codec.encode('Hello');
138
+ * // 0x01020304050648656c6c6f
139
+ * // | | └-- Our encoded value ("Hello").
140
+ * // | └-- Our second hidden prefix.
141
+ * // └-- Our first hidden prefix.
142
+ *
143
+ * codec.decode(bytes);
144
+ * // 'Hello'
145
+ * ```
146
+ *
147
+ * @remarks
148
+ * If all you need is padding zeroes before a value, consider using {@link padLeftCodec} instead.
149
+ *
150
+ * Separate {@link getHiddenPrefixEncoder} and {@link getHiddenPrefixDecoder} functions are available.
151
+ *
152
+ * ```ts
153
+ * const bytes = getHiddenPrefixEncoder(getUtf8Encoder(), [
154
+ * getConstantEncoder(new Uint8Array([1, 2, 3])),
155
+ * getConstantEncoder(new Uint8Array([4, 5, 6])),
156
+ * ]).encode('Hello');
157
+ *
158
+ * const value = getHiddenPrefixDecoder(getUtf8Decoder(), [
159
+ * getConstantDecoder(new Uint8Array([1, 2, 3])),
160
+ * getConstantDecoder(new Uint8Array([4, 5, 6])),
161
+ * ]).decode(bytes);
162
+ * ```
163
+ *
164
+ * @see {@link getHiddenPrefixEncoder}
165
+ * @see {@link getHiddenPrefixDecoder}
166
+ */
167
+ export function getHiddenPrefixCodec<TFrom, TTo extends TFrom>(
168
+ codec: FixedSizeCodec<TFrom, TTo>,
169
+ prefixedCodecs: readonly FixedSizeCodec<void>[],
170
+ ): FixedSizeCodec<TFrom, TTo>;
171
+ export function getHiddenPrefixCodec<TFrom, TTo extends TFrom>(
172
+ codec: Codec<TFrom, TTo>,
173
+ prefixedCodecs: readonly Codec<void>[],
174
+ ): VariableSizeCodec<TFrom, TTo>;
175
+ export function getHiddenPrefixCodec<TFrom, TTo extends TFrom>(
176
+ codec: Codec<TFrom, TTo>,
177
+ prefixedCodecs: readonly Codec<void>[],
178
+ ): Codec<TFrom, TTo> {
179
+ return combineCodec(getHiddenPrefixEncoder(codec, prefixedCodecs), getHiddenPrefixDecoder(codec, prefixedCodecs));
180
+ }
@@ -0,0 +1,180 @@
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
+
16
+ import { getTupleDecoder, getTupleEncoder } from './tuple';
17
+
18
+ /**
19
+ * Returns an encoder that appends hidden data after the encoded value.
20
+ *
21
+ * This encoder applies a list of void encoders after encoding the main value.
22
+ * The suffixed data is encoded after the main value without being exposed to the user.
23
+ *
24
+ * For more details, see {@link getHiddenSuffixCodec}.
25
+ *
26
+ * @typeParam TFrom - The type of the main value being encoded.
27
+ *
28
+ * @param encoder - The encoder for the main value.
29
+ * @param suffixedEncoders - A list of void encoders that produce the hidden suffix.
30
+ * @returns A `FixedSizeEncoder` or `VariableSizeEncoder` that encodes the value with a hidden suffix.
31
+ *
32
+ * @example
33
+ * Suffixing a value with constants.
34
+ * ```ts
35
+ * const encoder = getHiddenSuffixEncoder(getUtf8Encoder(), [
36
+ * getConstantCodec(new Uint8Array([1, 2, 3])),
37
+ * getConstantCodec(new Uint8Array([4, 5, 6])),
38
+ * ]);
39
+ *
40
+ * encoder.encode('Hello');
41
+ * // 0x48656c6c6f010203040506
42
+ * // | | └-- Our second hidden suffix.
43
+ * // | └-- Our first hidden suffix.
44
+ * // └-- Our encoded value ("Hello").
45
+ * ```
46
+ *
47
+ * @see {@link getHiddenSuffixCodec}
48
+ */
49
+ export function getHiddenSuffixEncoder<TFrom>(
50
+ encoder: FixedSizeEncoder<TFrom>,
51
+ suffixedEncoders: readonly FixedSizeEncoder<void>[],
52
+ ): FixedSizeEncoder<TFrom>;
53
+ export function getHiddenSuffixEncoder<TFrom>(
54
+ encoder: Encoder<TFrom>,
55
+ suffixedEncoders: readonly Encoder<void>[],
56
+ ): VariableSizeEncoder<TFrom>;
57
+ export function getHiddenSuffixEncoder<TFrom>(
58
+ encoder: Encoder<TFrom>,
59
+ suffixedEncoders: readonly Encoder<void>[],
60
+ ): Encoder<TFrom> {
61
+ return transformEncoder(
62
+ getTupleEncoder([encoder, ...suffixedEncoders]) as Encoder<readonly [TFrom, ...void[]]>,
63
+ (value: TFrom) => [value, ...suffixedEncoders.map(() => undefined)] as const,
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Returns a decoder that skips hidden suffixed data after decoding the main value.
69
+ *
70
+ * This decoder applies a list of void decoders after decoding the main value.
71
+ * The suffixed data is skipped during decoding without being exposed to the user.
72
+ *
73
+ * For more details, see {@link getHiddenSuffixCodec}.
74
+ *
75
+ * @typeParam TTo - The type of the main value being decoded.
76
+ *
77
+ * @param decoder - The decoder for the main value.
78
+ * @param suffixedDecoders - A list of void decoders that produce the hidden suffix.
79
+ * @returns A `FixedSizeDecoder` or `VariableSizeDecoder` that decodes values while ignoring the hidden suffix.
80
+ *
81
+ * @example
82
+ * Decoding a value with suffixed constants.
83
+ * ```ts
84
+ * const decoder = getHiddenSuffixDecoder(getUtf8Decoder(), [
85
+ * getConstantCodec(new Uint8Array([1, 2, 3])),
86
+ * getConstantCodec(new Uint8Array([4, 5, 6])),
87
+ * ]);
88
+ *
89
+ * decoder.decode(new Uint8Array([0x48, 0x65, 0x6C, 0x6C, 0x6F, 1, 2, 3, 4, 5, 6]));
90
+ * // 'Hello'
91
+ * ```
92
+ *
93
+ * @see {@link getHiddenSuffixCodec}
94
+ */
95
+ export function getHiddenSuffixDecoder<TTo>(
96
+ decoder: FixedSizeDecoder<TTo>,
97
+ suffixedDecoders: readonly FixedSizeDecoder<void>[],
98
+ ): FixedSizeDecoder<TTo>;
99
+ export function getHiddenSuffixDecoder<TTo>(
100
+ decoder: Decoder<TTo>,
101
+ suffixedDecoders: readonly Decoder<void>[],
102
+ ): VariableSizeDecoder<TTo>;
103
+ export function getHiddenSuffixDecoder<TTo>(
104
+ decoder: Decoder<TTo>,
105
+ suffixedDecoders: readonly Decoder<void>[],
106
+ ): Decoder<TTo> {
107
+ return transformDecoder(
108
+ getTupleDecoder([decoder, ...suffixedDecoders]) as Decoder<readonly [TTo, ...void[]]>,
109
+ tuple => tuple[0],
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Returns a codec that encodes and decodes values with a hidden suffix.
115
+ *
116
+ * - **Encoding:** Appends hidden data after encoding the main value.
117
+ * - **Decoding:** Skips the hidden suffix after decoding the main value.
118
+ *
119
+ * This is useful for any implicit metadata that should be present in
120
+ * binary formats but omitted from the API.
121
+ *
122
+ * @typeParam TFrom - The type of the main value being encoded.
123
+ * @typeParam TTo - The type of the main value being decoded.
124
+ *
125
+ * @param codec - The codec for the main value.
126
+ * @param suffixedCodecs - A list of void codecs that produce the hidden suffix.
127
+ * @returns A `FixedSizeCodec` or `VariableSizeCodec` for encoding and decoding values with a hidden suffix.
128
+ *
129
+ * @example
130
+ * Encoding and decoding a value with suffixed constants.
131
+ * ```ts
132
+ * const codec = getHiddenSuffixCodec(getUtf8Codec(), [
133
+ * getConstantCodec(new Uint8Array([1, 2, 3])),
134
+ * getConstantCodec(new Uint8Array([4, 5, 6])),
135
+ * ]);
136
+ *
137
+ * const bytes = codec.encode('Hello');
138
+ * // 0x48656c6c6f010203040506
139
+ * // | | └-- Our second hidden suffix.
140
+ * // | └-- Our first hidden suffix.
141
+ * // └-- Our encoded value ("Hello").
142
+ *
143
+ * codec.decode(bytes);
144
+ * // 'Hello'
145
+ * ```
146
+ *
147
+ * @remarks
148
+ * If all you need is padding zeroes after a value, consider using {@link padRightCodec} instead.
149
+ *
150
+ * Separate {@link getHiddenSuffixEncoder} and {@link getHiddenSuffixDecoder} functions are available.
151
+ *
152
+ * ```ts
153
+ * const bytes = getHiddenSuffixEncoder(getUtf8Encoder(), [
154
+ * getConstantEncoder(new Uint8Array([1, 2, 3])),
155
+ * getConstantEncoder(new Uint8Array([4, 5, 6])),
156
+ * ]).encode('Hello');
157
+ *
158
+ * const value = getHiddenSuffixDecoder(getUtf8Decoder(), [
159
+ * getConstantDecoder(new Uint8Array([1, 2, 3])),
160
+ * getConstantDecoder(new Uint8Array([4, 5, 6])),
161
+ * ]).decode(bytes);
162
+ * ```
163
+ *
164
+ * @see {@link getHiddenSuffixEncoder}
165
+ * @see {@link getHiddenSuffixDecoder}
166
+ */
167
+ export function getHiddenSuffixCodec<TFrom, TTo extends TFrom>(
168
+ codec: FixedSizeCodec<TFrom, TTo>,
169
+ suffixedCodecs: readonly FixedSizeCodec<void>[],
170
+ ): FixedSizeCodec<TFrom, TTo>;
171
+ export function getHiddenSuffixCodec<TFrom, TTo extends TFrom>(
172
+ codec: Codec<TFrom, TTo>,
173
+ suffixedCodecs: readonly Codec<void>[],
174
+ ): VariableSizeCodec<TFrom, TTo>;
175
+ export function getHiddenSuffixCodec<TFrom, TTo extends TFrom>(
176
+ codec: Codec<TFrom, TTo>,
177
+ suffixedCodecs: readonly Codec<void>[],
178
+ ): Codec<TFrom, TTo> {
179
+ return combineCodec(getHiddenSuffixEncoder(codec, suffixedCodecs), getHiddenSuffixDecoder(codec, suffixedCodecs));
180
+ }
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * This package contains codecs for various data structures such as arrays, maps, structs, tuples, enums, etc.
3
+ * It can be used standalone, but it is also exported as part of Kit
4
+ * [`@solana/kit`](https://github.com/anza-xyz/kit/tree/main/packages/kit).
5
+ *
6
+ * This package is also part of the [`@solana/codecs` package](https://github.com/anza-xyz/kit/tree/main/packages/codecs)
7
+ * which acts as an entry point for all codec packages as well as for their documentation.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ export * from './array';
12
+ export * from './assertions';
13
+ export * from './bit-array';
14
+ export * from './boolean';
15
+ export * from './bytes';
16
+ export * from './constant';
17
+ export * from './discriminated-union';
18
+ export * from './enum';
19
+ export * from './hidden-prefix';
20
+ export * from './hidden-suffix';
21
+ export * from './literal-union';
22
+ export * from './map';
23
+ export * from './nullable';
24
+ export * from './pattern-match';
25
+ export * from './predicate';
26
+ export * from './set';
27
+ export * from './struct';
28
+ export * from './tuple';
29
+ export * from './union';
30
+ export * from './unit';