@solana/codecs-core 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 +4 -3
- package/src/add-codec-sentinel.ts +186 -0
- package/src/add-codec-size-prefix.ts +161 -0
- package/src/array-buffers.ts +25 -0
- package/src/assertions.ts +103 -0
- package/src/bytes.ts +145 -0
- package/src/codec.ts +925 -0
- package/src/combine-codec.ts +133 -0
- package/src/decoder-entire-byte-array.ts +45 -0
- package/src/fix-codec-size.ts +170 -0
- package/src/index.ts +668 -0
- package/src/offset-codec.ts +379 -0
- package/src/pad-codec.ts +197 -0
- package/src/readonly-uint8array.ts +21 -0
- package/src/resize-codec.ts +209 -0
- package/src/reverse-codec.ts +159 -0
- package/src/transform-codec.ts +208 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH, SolanaError } from '@solana/errors';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Codec,
|
|
5
|
+
createDecoder,
|
|
6
|
+
createEncoder,
|
|
7
|
+
Decoder,
|
|
8
|
+
Encoder,
|
|
9
|
+
FixedSizeCodec,
|
|
10
|
+
FixedSizeDecoder,
|
|
11
|
+
FixedSizeEncoder,
|
|
12
|
+
isFixedSize,
|
|
13
|
+
} from './codec';
|
|
14
|
+
import { combineCodec } from './combine-codec';
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
type AnyEncoder = Encoder<any>;
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
type AnyDecoder = Decoder<any>;
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
type AnyCodec = Codec<any>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Updates the size of a given encoder.
|
|
25
|
+
*
|
|
26
|
+
* This function modifies the size of an encoder using a provided transformation function.
|
|
27
|
+
* For fixed-size encoders, it updates the `fixedSize` property, and for variable-size
|
|
28
|
+
* encoders, it adjusts the size calculation based on the encoded value.
|
|
29
|
+
*
|
|
30
|
+
* If the new size is negative, an error will be thrown.
|
|
31
|
+
*
|
|
32
|
+
* For more details, see {@link resizeCodec}.
|
|
33
|
+
*
|
|
34
|
+
* @typeParam TFrom - The type of the value to encode.
|
|
35
|
+
* @typeParam TSize - The original fixed size of the encoded value.
|
|
36
|
+
* @typeParam TNewSize - The new fixed size after resizing.
|
|
37
|
+
*
|
|
38
|
+
* @param encoder - The encoder whose size will be updated.
|
|
39
|
+
* @param resize - A function that takes the current size and returns the new size.
|
|
40
|
+
* @returns A new encoder with the updated size.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* Increasing the size of a `u16` encoder by 2 bytes.
|
|
44
|
+
* ```ts
|
|
45
|
+
* const encoder = resizeEncoder(getU16Encoder(), size => size + 2);
|
|
46
|
+
* encoder.encode(0xffff); // 0xffff0000 (two extra bytes added)
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* Shrinking a `u32` encoder to only use 2 bytes.
|
|
51
|
+
* ```ts
|
|
52
|
+
* const encoder = resizeEncoder(getU32Encoder(), () => 2);
|
|
53
|
+
* encoder.fixedSize; // 2
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @see {@link resizeCodec}
|
|
57
|
+
* @see {@link resizeDecoder}
|
|
58
|
+
*/
|
|
59
|
+
export function resizeEncoder<TFrom, TSize extends number, TNewSize extends number>(
|
|
60
|
+
encoder: FixedSizeEncoder<TFrom, TSize>,
|
|
61
|
+
resize: (size: TSize) => TNewSize,
|
|
62
|
+
): FixedSizeEncoder<TFrom, TNewSize>;
|
|
63
|
+
export function resizeEncoder<TEncoder extends AnyEncoder>(
|
|
64
|
+
encoder: TEncoder,
|
|
65
|
+
resize: (size: number) => number,
|
|
66
|
+
): TEncoder;
|
|
67
|
+
export function resizeEncoder<TEncoder extends AnyEncoder>(
|
|
68
|
+
encoder: TEncoder,
|
|
69
|
+
resize: (size: number) => number,
|
|
70
|
+
): TEncoder {
|
|
71
|
+
if (isFixedSize(encoder)) {
|
|
72
|
+
const fixedSize = resize(encoder.fixedSize);
|
|
73
|
+
if (fixedSize < 0) {
|
|
74
|
+
throw new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH, {
|
|
75
|
+
bytesLength: fixedSize,
|
|
76
|
+
codecDescription: 'resizeEncoder',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return createEncoder({ ...encoder, fixedSize }) as TEncoder;
|
|
80
|
+
}
|
|
81
|
+
return createEncoder({
|
|
82
|
+
...encoder,
|
|
83
|
+
getSizeFromValue: value => {
|
|
84
|
+
const newSize = resize(encoder.getSizeFromValue(value));
|
|
85
|
+
if (newSize < 0) {
|
|
86
|
+
throw new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH, {
|
|
87
|
+
bytesLength: newSize,
|
|
88
|
+
codecDescription: 'resizeEncoder',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return newSize;
|
|
92
|
+
},
|
|
93
|
+
}) as TEncoder;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Updates the size of a given decoder.
|
|
98
|
+
*
|
|
99
|
+
* This function modifies the size of a decoder using a provided transformation function.
|
|
100
|
+
* For fixed-size decoders, it updates the `fixedSize` property to reflect the new size.
|
|
101
|
+
* Variable-size decoders remain unchanged, as their size is determined dynamically.
|
|
102
|
+
*
|
|
103
|
+
* If the new size is negative, an error will be thrown.
|
|
104
|
+
*
|
|
105
|
+
* For more details, see {@link resizeCodec}.
|
|
106
|
+
*
|
|
107
|
+
* @typeParam TTo - The type of the decoded value.
|
|
108
|
+
* @typeParam TSize - The original fixed size of the decoded value.
|
|
109
|
+
* @typeParam TNewSize - The new fixed size after resizing.
|
|
110
|
+
*
|
|
111
|
+
* @param decoder - The decoder whose size will be updated.
|
|
112
|
+
* @param resize - A function that takes the current size and returns the new size.
|
|
113
|
+
* @returns A new decoder with the updated size.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* Expanding a `u16` decoder to read 4 bytes instead of 2.
|
|
117
|
+
* ```ts
|
|
118
|
+
* const decoder = resizeDecoder(getU16Decoder(), size => size + 2);
|
|
119
|
+
* decoder.fixedSize; // 4
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* Shrinking a `u32` decoder to only read 2 bytes.
|
|
124
|
+
* ```ts
|
|
125
|
+
* const decoder = resizeDecoder(getU32Decoder(), () => 2);
|
|
126
|
+
* decoder.fixedSize; // 2
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @see {@link resizeCodec}
|
|
130
|
+
* @see {@link resizeEncoder}
|
|
131
|
+
*/
|
|
132
|
+
export function resizeDecoder<TFrom, TSize extends number, TNewSize extends number>(
|
|
133
|
+
decoder: FixedSizeDecoder<TFrom, TSize>,
|
|
134
|
+
resize: (size: TSize) => TNewSize,
|
|
135
|
+
): FixedSizeDecoder<TFrom, TNewSize>;
|
|
136
|
+
export function resizeDecoder<TDecoder extends AnyDecoder>(
|
|
137
|
+
decoder: TDecoder,
|
|
138
|
+
resize: (size: number) => number,
|
|
139
|
+
): TDecoder;
|
|
140
|
+
export function resizeDecoder<TDecoder extends AnyDecoder>(
|
|
141
|
+
decoder: TDecoder,
|
|
142
|
+
resize: (size: number) => number,
|
|
143
|
+
): TDecoder {
|
|
144
|
+
if (isFixedSize(decoder)) {
|
|
145
|
+
const fixedSize = resize(decoder.fixedSize);
|
|
146
|
+
if (fixedSize < 0) {
|
|
147
|
+
throw new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH, {
|
|
148
|
+
bytesLength: fixedSize,
|
|
149
|
+
codecDescription: 'resizeDecoder',
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return createDecoder({ ...decoder, fixedSize }) as TDecoder;
|
|
153
|
+
}
|
|
154
|
+
return decoder;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Updates the size of a given codec.
|
|
159
|
+
*
|
|
160
|
+
* This function modifies the size of both the codec using a provided
|
|
161
|
+
* transformation function. It is useful for adjusting the allocated byte size for
|
|
162
|
+
* encoding and decoding without altering the underlying data structure.
|
|
163
|
+
*
|
|
164
|
+
* If the new size is negative, an error will be thrown.
|
|
165
|
+
*
|
|
166
|
+
* @typeParam TFrom - The type of the value to encode.
|
|
167
|
+
* @typeParam TTo - The type of the decoded value.
|
|
168
|
+
* @typeParam TSize - The original fixed size of the encoded/decoded value (for fixed-size codecs).
|
|
169
|
+
* @typeParam TNewSize - The new fixed size after resizing (for fixed-size codecs).
|
|
170
|
+
*
|
|
171
|
+
* @param codec - The codec whose size will be updated.
|
|
172
|
+
* @param resize - A function that takes the current size and returns the new size.
|
|
173
|
+
* @returns A new codec with the updated size.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* Expanding a `u16` codec from 2 to 4 bytes.
|
|
177
|
+
* ```ts
|
|
178
|
+
* const codec = resizeCodec(getU16Codec(), size => size + 2);
|
|
179
|
+
* const bytes = codec.encode(0xffff); // 0xffff0000 (two extra bytes added)
|
|
180
|
+
* const value = codec.decode(bytes); // 0xffff (reads original two bytes)
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* Shrinking a `u32` codec to only use 2 bytes.
|
|
185
|
+
* ```ts
|
|
186
|
+
* const codec = resizeCodec(getU32Codec(), () => 2);
|
|
187
|
+
* codec.fixedSize; // 2
|
|
188
|
+
* ```
|
|
189
|
+
*
|
|
190
|
+
* @remarks
|
|
191
|
+
* If you only need to resize an encoder, use {@link resizeEncoder}.
|
|
192
|
+
* If you only need to resize a decoder, use {@link resizeDecoder}.
|
|
193
|
+
*
|
|
194
|
+
* ```ts
|
|
195
|
+
* const bytes = resizeEncoder(getU32Encoder(), (size) => size + 2).encode(0xffff);
|
|
196
|
+
* const value = resizeDecoder(getU32Decoder(), (size) => size + 2).decode(bytes);
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* @see {@link resizeEncoder}
|
|
200
|
+
* @see {@link resizeDecoder}
|
|
201
|
+
*/
|
|
202
|
+
export function resizeCodec<TFrom, TTo extends TFrom, TSize extends number, TNewSize extends number>(
|
|
203
|
+
codec: FixedSizeCodec<TFrom, TTo, TSize>,
|
|
204
|
+
resize: (size: TSize) => TNewSize,
|
|
205
|
+
): FixedSizeCodec<TFrom, TTo, TNewSize>;
|
|
206
|
+
export function resizeCodec<TCodec extends AnyCodec>(codec: TCodec, resize: (size: number) => number): TCodec;
|
|
207
|
+
export function resizeCodec<TCodec extends AnyCodec>(codec: TCodec, resize: (size: number) => number): TCodec {
|
|
208
|
+
return combineCodec(resizeEncoder(codec, resize), resizeDecoder(codec, resize)) as TCodec;
|
|
209
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertIsFixedSize,
|
|
3
|
+
createDecoder,
|
|
4
|
+
createEncoder,
|
|
5
|
+
FixedSizeCodec,
|
|
6
|
+
FixedSizeDecoder,
|
|
7
|
+
FixedSizeEncoder,
|
|
8
|
+
} from './codec';
|
|
9
|
+
import { combineCodec } from './combine-codec';
|
|
10
|
+
import { ReadonlyUint8Array } from './readonly-uint8array';
|
|
11
|
+
|
|
12
|
+
function copySourceToTargetInReverse(
|
|
13
|
+
source: ReadonlyUint8Array,
|
|
14
|
+
target_WILL_MUTATE: Uint8Array,
|
|
15
|
+
sourceOffset: number,
|
|
16
|
+
sourceLength: number,
|
|
17
|
+
targetOffset: number = 0,
|
|
18
|
+
) {
|
|
19
|
+
while (sourceOffset < --sourceLength) {
|
|
20
|
+
const leftValue = source[sourceOffset];
|
|
21
|
+
target_WILL_MUTATE[sourceOffset + targetOffset] = source[sourceLength];
|
|
22
|
+
target_WILL_MUTATE[sourceLength + targetOffset] = leftValue;
|
|
23
|
+
sourceOffset++;
|
|
24
|
+
}
|
|
25
|
+
if (sourceOffset === sourceLength) {
|
|
26
|
+
target_WILL_MUTATE[sourceOffset + targetOffset] = source[sourceOffset];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Reverses the bytes of a fixed-size encoder.
|
|
32
|
+
*
|
|
33
|
+
* Given a `FixedSizeEncoder`, this function returns a new `FixedSizeEncoder` that
|
|
34
|
+
* reverses the bytes within the fixed-size byte array when encoding.
|
|
35
|
+
*
|
|
36
|
+
* This can be useful to modify endianness or for other byte-order transformations.
|
|
37
|
+
*
|
|
38
|
+
* For more details, see {@link reverseCodec}.
|
|
39
|
+
*
|
|
40
|
+
* @typeParam TFrom - The type of the value to encode.
|
|
41
|
+
* @typeParam TSize - The fixed size of the encoded value in bytes.
|
|
42
|
+
*
|
|
43
|
+
* @param encoder - The fixed-size encoder to reverse.
|
|
44
|
+
* @returns A new encoder that writes bytes in reverse order.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* Encoding a `u16` value in reverse order.
|
|
48
|
+
* ```ts
|
|
49
|
+
* const encoder = reverseEncoder(getU16Encoder({ endian: Endian.Big }));
|
|
50
|
+
* const bytes = encoder.encode(0x1234); // 0x3412 (bytes are flipped)
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @see {@link reverseCodec}
|
|
54
|
+
* @see {@link reverseDecoder}
|
|
55
|
+
*/
|
|
56
|
+
export function reverseEncoder<TFrom, TSize extends number>(
|
|
57
|
+
encoder: FixedSizeEncoder<TFrom, TSize>,
|
|
58
|
+
): FixedSizeEncoder<TFrom, TSize> {
|
|
59
|
+
assertIsFixedSize(encoder);
|
|
60
|
+
return createEncoder({
|
|
61
|
+
...encoder,
|
|
62
|
+
write: (value: TFrom, bytes, offset) => {
|
|
63
|
+
const newOffset = encoder.write(value, bytes, offset);
|
|
64
|
+
copySourceToTargetInReverse(
|
|
65
|
+
bytes /* source */,
|
|
66
|
+
bytes /* target_WILL_MUTATE */,
|
|
67
|
+
offset /* sourceOffset */,
|
|
68
|
+
offset + encoder.fixedSize /* sourceLength */,
|
|
69
|
+
);
|
|
70
|
+
return newOffset;
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Reverses the bytes of a fixed-size decoder.
|
|
77
|
+
*
|
|
78
|
+
* Given a `FixedSizeDecoder`, this function returns a new `FixedSizeDecoder` that
|
|
79
|
+
* reverses the bytes within the fixed-size byte array before decoding.
|
|
80
|
+
*
|
|
81
|
+
* This can be useful to modify endianness or for other byte-order transformations.
|
|
82
|
+
*
|
|
83
|
+
* For more details, see {@link reverseCodec}.
|
|
84
|
+
*
|
|
85
|
+
* @typeParam TTo - The type of the decoded value.
|
|
86
|
+
* @typeParam TSize - The fixed size of the decoded value in bytes.
|
|
87
|
+
*
|
|
88
|
+
* @param decoder - The fixed-size decoder to reverse.
|
|
89
|
+
* @returns A new decoder that reads bytes in reverse order.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* Decoding a reversed `u16` value.
|
|
93
|
+
* ```ts
|
|
94
|
+
* const decoder = reverseDecoder(getU16Decoder({ endian: Endian.Big }));
|
|
95
|
+
* const value = decoder.decode(new Uint8Array([0x34, 0x12])); // 0x1234 (bytes are flipped back)
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @see {@link reverseCodec}
|
|
99
|
+
* @see {@link reverseEncoder}
|
|
100
|
+
*/
|
|
101
|
+
export function reverseDecoder<TTo, TSize extends number>(
|
|
102
|
+
decoder: FixedSizeDecoder<TTo, TSize>,
|
|
103
|
+
): FixedSizeDecoder<TTo, TSize> {
|
|
104
|
+
assertIsFixedSize(decoder);
|
|
105
|
+
return createDecoder({
|
|
106
|
+
...decoder,
|
|
107
|
+
read: (bytes, offset) => {
|
|
108
|
+
const reversedBytes = bytes.slice();
|
|
109
|
+
copySourceToTargetInReverse(
|
|
110
|
+
bytes /* source */,
|
|
111
|
+
reversedBytes /* target_WILL_MUTATE */,
|
|
112
|
+
offset /* sourceOffset */,
|
|
113
|
+
offset + decoder.fixedSize /* sourceLength */,
|
|
114
|
+
);
|
|
115
|
+
return decoder.read(reversedBytes, offset);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Reverses the bytes of a fixed-size codec.
|
|
122
|
+
*
|
|
123
|
+
* Given a `FixedSizeCodec`, this function returns a new `FixedSizeCodec` that
|
|
124
|
+
* reverses the bytes within the fixed-size byte array during encoding and decoding.
|
|
125
|
+
*
|
|
126
|
+
* This can be useful to modify endianness or for other byte-order transformations.
|
|
127
|
+
*
|
|
128
|
+
* @typeParam TFrom - The type of the value to encode.
|
|
129
|
+
* @typeParam TTo - The type of the decoded value.
|
|
130
|
+
* @typeParam TSize - The fixed size of the encoded/decoded value in bytes.
|
|
131
|
+
*
|
|
132
|
+
* @param codec - The fixed-size codec to reverse.
|
|
133
|
+
* @returns A new codec that encodes and decodes bytes in reverse order.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* Reversing a `u16` codec.
|
|
137
|
+
* ```ts
|
|
138
|
+
* const codec = reverseCodec(getU16Codec({ endian: Endian.Big }));
|
|
139
|
+
* const bytes = codec.encode(0x1234); // 0x3412 (bytes are flipped)
|
|
140
|
+
* const value = codec.decode(bytes); // 0x1234 (bytes are flipped back)
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* @remarks
|
|
144
|
+
* If you only need to reverse an encoder, use {@link reverseEncoder}.
|
|
145
|
+
* If you only need to reverse a decoder, use {@link reverseDecoder}.
|
|
146
|
+
*
|
|
147
|
+
* ```ts
|
|
148
|
+
* const bytes = reverseEncoder(getU16Encoder()).encode(0x1234);
|
|
149
|
+
* const value = reverseDecoder(getU16Decoder()).decode(bytes);
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @see {@link reverseEncoder}
|
|
153
|
+
* @see {@link reverseDecoder}
|
|
154
|
+
*/
|
|
155
|
+
export function reverseCodec<TFrom, TTo extends TFrom, TSize extends number>(
|
|
156
|
+
codec: FixedSizeCodec<TFrom, TTo, TSize>,
|
|
157
|
+
): FixedSizeCodec<TFrom, TTo, TSize> {
|
|
158
|
+
return combineCodec(reverseEncoder(codec), reverseDecoder(codec));
|
|
159
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Codec,
|
|
3
|
+
createCodec,
|
|
4
|
+
createDecoder,
|
|
5
|
+
createEncoder,
|
|
6
|
+
Decoder,
|
|
7
|
+
Encoder,
|
|
8
|
+
FixedSizeCodec,
|
|
9
|
+
FixedSizeDecoder,
|
|
10
|
+
FixedSizeEncoder,
|
|
11
|
+
isVariableSize,
|
|
12
|
+
VariableSizeCodec,
|
|
13
|
+
VariableSizeDecoder,
|
|
14
|
+
VariableSizeEncoder,
|
|
15
|
+
} from './codec';
|
|
16
|
+
import { ReadonlyUint8Array } from './readonly-uint8array';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Transforms an encoder by mapping its input values.
|
|
20
|
+
*
|
|
21
|
+
* This function takes an existing `Encoder<A>` and returns an `Encoder<B>`, allowing values of type `B`
|
|
22
|
+
* to be converted into values of type `A` before encoding. The transformation is applied via the `unmap` function.
|
|
23
|
+
*
|
|
24
|
+
* This is useful for handling type conversions, applying default values, or structuring data before encoding.
|
|
25
|
+
*
|
|
26
|
+
* For more details, see {@link transformCodec}.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam TOldFrom - The original type expected by the encoder.
|
|
29
|
+
* @typeParam TNewFrom - The new type that will be transformed before encoding.
|
|
30
|
+
*
|
|
31
|
+
* @param encoder - The encoder to transform.
|
|
32
|
+
* @param unmap - A function that converts values of `TNewFrom` into `TOldFrom` before encoding.
|
|
33
|
+
* @returns A new encoder that accepts `TNewFrom` values and transforms them before encoding.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* Encoding a string by counting its characters and storing the length as a `u32`.
|
|
37
|
+
* ```ts
|
|
38
|
+
* const encoder = transformEncoder(getU32Encoder(), (value: string) => value.length);
|
|
39
|
+
* encoder.encode("hello"); // 0x05000000 (stores length 5)
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @see {@link transformCodec}
|
|
43
|
+
* @see {@link transformDecoder}
|
|
44
|
+
*/
|
|
45
|
+
export function transformEncoder<TOldFrom, TNewFrom, TSize extends number>(
|
|
46
|
+
encoder: FixedSizeEncoder<TOldFrom, TSize>,
|
|
47
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
48
|
+
): FixedSizeEncoder<TNewFrom, TSize>;
|
|
49
|
+
export function transformEncoder<TOldFrom, TNewFrom>(
|
|
50
|
+
encoder: VariableSizeEncoder<TOldFrom>,
|
|
51
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
52
|
+
): VariableSizeEncoder<TNewFrom>;
|
|
53
|
+
export function transformEncoder<TOldFrom, TNewFrom>(
|
|
54
|
+
encoder: Encoder<TOldFrom>,
|
|
55
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
56
|
+
): Encoder<TNewFrom>;
|
|
57
|
+
export function transformEncoder<TOldFrom, TNewFrom>(
|
|
58
|
+
encoder: Encoder<TOldFrom>,
|
|
59
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
60
|
+
): Encoder<TNewFrom> {
|
|
61
|
+
return createEncoder({
|
|
62
|
+
...(isVariableSize(encoder)
|
|
63
|
+
? { ...encoder, getSizeFromValue: (value: TNewFrom) => encoder.getSizeFromValue(unmap(value)) }
|
|
64
|
+
: encoder),
|
|
65
|
+
write: (value: TNewFrom, bytes, offset) => encoder.write(unmap(value), bytes, offset),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Transforms a decoder by mapping its output values.
|
|
71
|
+
*
|
|
72
|
+
* This function takes an existing `Decoder<A>` and returns a `Decoder<B>`, allowing values of type `A`
|
|
73
|
+
* to be converted into values of type `B` after decoding. The transformation is applied via the `map` function.
|
|
74
|
+
*
|
|
75
|
+
* This is useful for post-processing, type conversions, or enriching decoded data.
|
|
76
|
+
*
|
|
77
|
+
* For more details, see {@link transformCodec}.
|
|
78
|
+
*
|
|
79
|
+
* @typeParam TOldTo - The original type returned by the decoder.
|
|
80
|
+
* @typeParam TNewTo - The new type that will be transformed after decoding.
|
|
81
|
+
*
|
|
82
|
+
* @param decoder - The decoder to transform.
|
|
83
|
+
* @param map - A function that converts values of `TOldTo` into `TNewTo` after decoding.
|
|
84
|
+
* @returns A new decoder that decodes into `TNewTo`.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* Decoding a stored `u32` length into a string of `'x'` characters.
|
|
88
|
+
* ```ts
|
|
89
|
+
* const decoder = transformDecoder(getU32Decoder(), (length) => 'x'.repeat(length));
|
|
90
|
+
* decoder.decode(new Uint8Array([0x05, 0x00, 0x00, 0x00])); // "xxxxx"
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @see {@link transformCodec}
|
|
94
|
+
* @see {@link transformEncoder}
|
|
95
|
+
*/
|
|
96
|
+
export function transformDecoder<TOldTo, TNewTo, TSize extends number>(
|
|
97
|
+
decoder: FixedSizeDecoder<TOldTo, TSize>,
|
|
98
|
+
map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo,
|
|
99
|
+
): FixedSizeDecoder<TNewTo, TSize>;
|
|
100
|
+
export function transformDecoder<TOldTo, TNewTo>(
|
|
101
|
+
decoder: VariableSizeDecoder<TOldTo>,
|
|
102
|
+
map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo,
|
|
103
|
+
): VariableSizeDecoder<TNewTo>;
|
|
104
|
+
export function transformDecoder<TOldTo, TNewTo>(
|
|
105
|
+
decoder: Decoder<TOldTo>,
|
|
106
|
+
map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo,
|
|
107
|
+
): Decoder<TNewTo>;
|
|
108
|
+
export function transformDecoder<TOldTo, TNewTo>(
|
|
109
|
+
decoder: Decoder<TOldTo>,
|
|
110
|
+
map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo,
|
|
111
|
+
): Decoder<TNewTo> {
|
|
112
|
+
return createDecoder({
|
|
113
|
+
...decoder,
|
|
114
|
+
read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => {
|
|
115
|
+
const [value, newOffset] = decoder.read(bytes, offset);
|
|
116
|
+
return [map(value, bytes, offset), newOffset];
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Transforms a codec by mapping its input and output values.
|
|
123
|
+
*
|
|
124
|
+
* This function takes an existing `Codec<A, B>` and returns a `Codec<C, D>`, allowing:
|
|
125
|
+
* - Values of type `C` to be transformed into `A` before encoding.
|
|
126
|
+
* - Values of type `B` to be transformed into `D` after decoding.
|
|
127
|
+
*
|
|
128
|
+
* This is useful for adapting codecs to work with different representations, handling default values, or
|
|
129
|
+
* converting between primitive and structured types.
|
|
130
|
+
*
|
|
131
|
+
* @typeParam TOldFrom - The original type expected by the codec.
|
|
132
|
+
* @typeParam TNewFrom - The new type that will be transformed before encoding.
|
|
133
|
+
* @typeParam TOldTo - The original type returned by the codec.
|
|
134
|
+
* @typeParam TNewTo - The new type that will be transformed after decoding.
|
|
135
|
+
*
|
|
136
|
+
* @param codec - The codec to transform.
|
|
137
|
+
* @param unmap - A function that converts values of `TNewFrom` into `TOldFrom` before encoding.
|
|
138
|
+
* @param map - A function that converts values of `TOldTo` into `TNewTo` after decoding (optional).
|
|
139
|
+
* @returns A new codec that encodes `TNewFrom` and decodes into `TNewTo`.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* Mapping a `u32` codec to encode string lengths and decode them into `'x'` characters.
|
|
143
|
+
* ```ts
|
|
144
|
+
* const codec = transformCodec(
|
|
145
|
+
* getU32Codec(),
|
|
146
|
+
* (value: string) => value.length, // Encode string length
|
|
147
|
+
* (length) => 'x'.repeat(length) // Decode length into a string of 'x's
|
|
148
|
+
* );
|
|
149
|
+
*
|
|
150
|
+
* const bytes = codec.encode("hello"); // 0x05000000 (stores length 5)
|
|
151
|
+
* const value = codec.decode(bytes); // "xxxxx"
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @remarks
|
|
155
|
+
* If only input transformation is needed, use {@link transformEncoder}.
|
|
156
|
+
* If only output transformation is needed, use {@link transformDecoder}.
|
|
157
|
+
*
|
|
158
|
+
* ```ts
|
|
159
|
+
* const bytes = transformEncoder(getU32Encoder(), (value: string) => value.length).encode("hello");
|
|
160
|
+
* const value = transformDecoder(getU32Decoder(), (length) => 'x'.repeat(length)).decode(bytes);
|
|
161
|
+
* ```
|
|
162
|
+
*
|
|
163
|
+
* @see {@link transformEncoder}
|
|
164
|
+
* @see {@link transformDecoder}
|
|
165
|
+
*/
|
|
166
|
+
export function transformCodec<TOldFrom, TNewFrom, TTo extends TNewFrom & TOldFrom, TSize extends number>(
|
|
167
|
+
codec: FixedSizeCodec<TOldFrom, TTo, TSize>,
|
|
168
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
169
|
+
): FixedSizeCodec<TNewFrom, TTo, TSize>;
|
|
170
|
+
export function transformCodec<TOldFrom, TNewFrom, TTo extends TNewFrom & TOldFrom>(
|
|
171
|
+
codec: VariableSizeCodec<TOldFrom, TTo>,
|
|
172
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
173
|
+
): VariableSizeCodec<TNewFrom, TTo>;
|
|
174
|
+
export function transformCodec<TOldFrom, TNewFrom, TTo extends TNewFrom & TOldFrom>(
|
|
175
|
+
codec: Codec<TOldFrom, TTo>,
|
|
176
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
177
|
+
): Codec<TNewFrom, TTo>;
|
|
178
|
+
export function transformCodec<
|
|
179
|
+
TOldFrom,
|
|
180
|
+
TNewFrom,
|
|
181
|
+
TOldTo extends TOldFrom,
|
|
182
|
+
TNewTo extends TNewFrom,
|
|
183
|
+
TSize extends number,
|
|
184
|
+
>(
|
|
185
|
+
codec: FixedSizeCodec<TOldFrom, TOldTo, TSize>,
|
|
186
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
187
|
+
map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo,
|
|
188
|
+
): FixedSizeCodec<TNewFrom, TNewTo, TSize>;
|
|
189
|
+
export function transformCodec<TOldFrom, TNewFrom, TOldTo extends TOldFrom, TNewTo extends TNewFrom>(
|
|
190
|
+
codec: VariableSizeCodec<TOldFrom, TOldTo>,
|
|
191
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
192
|
+
map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo,
|
|
193
|
+
): VariableSizeCodec<TNewFrom, TNewTo>;
|
|
194
|
+
export function transformCodec<TOldFrom, TNewFrom, TOldTo extends TOldFrom, TNewTo extends TNewFrom>(
|
|
195
|
+
codec: Codec<TOldFrom, TOldTo>,
|
|
196
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
197
|
+
map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo,
|
|
198
|
+
): Codec<TNewFrom, TNewTo>;
|
|
199
|
+
export function transformCodec<TOldFrom, TNewFrom, TOldTo extends TOldFrom, TNewTo extends TNewFrom>(
|
|
200
|
+
codec: Codec<TOldFrom, TOldTo>,
|
|
201
|
+
unmap: (value: TNewFrom) => TOldFrom,
|
|
202
|
+
map?: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo,
|
|
203
|
+
): Codec<TNewFrom, TNewTo> {
|
|
204
|
+
return createCodec({
|
|
205
|
+
...transformEncoder(codec, unmap),
|
|
206
|
+
read: map ? transformDecoder(codec, map).read : (codec.read as unknown as Decoder<TNewTo>['read']),
|
|
207
|
+
});
|
|
208
|
+
}
|