0
# Utility Functions
1
2
Low-level utility functions for building custom encoders using functional composition. These utilities enable creating domain-specific encoders by chaining simple operations together, following the same patterns used internally by @scure/base.
3
4
## Capabilities
5
6
### Chain Function
7
8
Composes multiple coders together in functional style, ensuring encode/decode operations maintain proper order.
9
10
```typescript { .api }
11
/**
12
* Chains multiple coders together in functional composition
13
* Automatically handles proper order for encode/decode operations
14
* @param args - Sequence of coders to chain together
15
* @returns Single coder combining all operations
16
*/
17
function chain<T extends Chain & AsChain<T>>(
18
...args: T
19
): Coder<Input<First<T>>, Output<Last<T>>>;
20
21
type Chain = [Coder<any, any>, ...Coder<any, any>[]];
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
import { utils } from "@scure/base";
28
29
// Create custom base32 encoder by chaining operations
30
const customBase32 = utils.chain(
31
utils.radix2(5), // Convert bytes to 5-bit values
32
utils.alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), // Map to base32 alphabet
33
utils.padding(5), // Add padding
34
utils.join('') // Join to string
35
);
36
37
const data = new Uint8Array([0x12, 0x34]);
38
const encoded = customBase32.encode(data); // Works like base32.encode()
39
const decoded = customBase32.decode(encoded); // Works like base32.decode()
40
```
41
42
### Alphabet Function
43
44
Maps between number arrays and string arrays using a custom alphabet.
45
46
```typescript { .api }
47
/**
48
* Creates encoder for number array to string array mapping using custom alphabet
49
* @param letters - Alphabet as string or array of strings
50
* @returns Coder mapping number indices to alphabet characters
51
*/
52
function alphabet(letters: string | string[]): Coder<number[], string[]>;
53
```
54
55
**Usage Examples:**
56
57
```typescript
58
import { utils } from "@scure/base";
59
60
// Create alphabet mapper
61
const hexAlphabet = utils.alphabet('0123456789abcdef');
62
63
const digits = [15, 14, 13, 12]; // Indices into alphabet
64
const letters = hexAlphabet.encode(digits); // ['f', 'e', 'd', 'c']
65
const restored = hexAlphabet.decode(letters); // [15, 14, 13, 12]
66
67
// Can also use string array
68
const customAlphabet = utils.alphabet(['zero', 'one', 'two']);
69
const words = customAlphabet.encode([0, 2, 1]); // ['zero', 'two', 'one']
70
```
71
72
### Radix Conversion
73
74
Convert between arbitrary numeric bases. Note: O(n^2) complexity for non-power-of-2 bases.
75
76
```typescript { .api }
77
/**
78
* Converts bytes to arbitrary radix representation
79
* @param num - Target radix/base number
80
* @returns Coder converting bytes to number array in specified radix
81
*/
82
function radix(num: number): Coder<Uint8Array, number[]>;
83
84
/**
85
* Optimized radix conversion for power-of-2 bases
86
* @param bits - Number of bits per output digit (must be ≤32)
87
* @param revPadding - Reverse padding direction
88
* @returns Coder for efficient power-of-2 base conversion
89
*/
90
function radix2(bits: number, revPadding?: boolean): Coder<Uint8Array, number[]>;
91
```
92
93
**Usage Examples:**
94
95
```typescript
96
import { utils } from "@scure/base";
97
98
// Convert to base 58 (O(n^2) - use sparingly)
99
const base58Radix = utils.radix(58);
100
const data = new Uint8Array([0x12, 0x34]);
101
const base58Digits = base58Radix.encode(data); // Array of base58 digit values
102
103
// Convert to base 32 (power of 2 - efficient)
104
const base32Radix = utils.radix2(5); // 2^5 = 32
105
const base32Digits = base32Radix.encode(data); // Array of 5-bit values
106
```
107
108
### String Operations
109
110
Join and split string arrays with configurable separators.
111
112
```typescript { .api }
113
/**
114
* Joins string arrays into single strings and splits them back
115
* @param separator - String to use for joining (default: empty string)
116
* @returns Coder for joining/splitting string arrays
117
*/
118
function join(separator?: string): Coder<string[], string>;
119
```
120
121
**Usage Examples:**
122
123
```typescript
124
import { utils } from "@scure/base";
125
126
// Join without separator (default)
127
const joiner = utils.join();
128
const joined = joiner.encode(['a', 'b', 'c']); // "abc"
129
const split = joiner.decode('abc'); // ['a', 'b', 'c'] - splits into chars
130
131
// Join with separator
132
const dashedJoiner = utils.join('-');
133
const dashed = dashedJoiner.encode(['hello', 'world']); // "hello-world"
134
const unDashed = dashedJoiner.decode('hello-world'); // ['hello', 'world']
135
```
136
137
### Padding Operations
138
139
Add and remove padding from string arrays to ensure proper bit alignment.
140
141
```typescript { .api }
142
/**
143
* Adds/removes padding to ensure string array represents whole number of bits
144
* @param bits - Number of bits per character in the encoding
145
* @param chr - Padding character (default: '=')
146
* @returns Coder for adding/removing padding
147
*/
148
function padding(bits: number, chr?: string): Coder<string[], string[]>;
149
```
150
151
**Usage Examples:**
152
153
```typescript
154
import { utils } from "@scure/base";
155
156
// Base64 uses 6 bits per character, padded with '='
157
const base64Padding = utils.padding(6, '=');
158
const data = ['Q', 'W']; // Incomplete group
159
const padded = base64Padding.encode(data); // ['Q', 'W', '=', '=']
160
const unpadded = base64Padding.decode(padded); // ['Q', 'W']
161
162
// Base32 uses 5 bits per character
163
const base32Padding = utils.padding(5);
164
const base32Data = ['A', 'B', 'C'];
165
const paddedB32 = base32Padding.encode(base32Data); // Adds padding if needed
166
```
167
168
### Checksum Validation
169
170
Add checksum validation to any encoder for error detection.
171
172
```typescript { .api }
173
/**
174
* Adds checksum validation using provided hash function
175
* @param len - Number of checksum bytes to append
176
* @param fn - Hash function for generating checksum
177
* @returns Coder that appends/validates checksum
178
*/
179
function checksum(
180
len: number,
181
fn: (data: Uint8Array) => Uint8Array
182
): Coder<Uint8Array, Uint8Array>;
183
```
184
185
**Usage Examples:**
186
187
```typescript
188
import { utils } from "@scure/base";
189
import { sha256 } from "@noble/hashes/sha2";
190
191
// Create checksum validator using SHA-256 (like base58check)
192
const sha256Checksum = utils.checksum(4, (data) => sha256(sha256(data)));
193
194
const originalData = new Uint8Array([0x12, 0x34, 0x56]);
195
const withChecksum = sha256Checksum.encode(originalData); // Appends 4-byte checksum
196
const verified = sha256Checksum.decode(withChecksum); // Validates and removes checksum
197
198
// Error on invalid checksum
199
try {
200
const corrupted = new Uint8Array([...withChecksum]);
201
corrupted[corrupted.length - 1] = 0; // Corrupt checksum
202
sha256Checksum.decode(corrupted); // Throws "Invalid checksum"
203
} catch (error) {
204
console.log("Checksum validation failed");
205
}
206
```
207
208
### Normalization
209
210
Apply transformation functions during decode operations for input sanitization.
211
212
```typescript { .api }
213
/**
214
* Applies normalization function during decode operation
215
* @param fn - Function to transform input during decode
216
* @returns Coder that applies transformation on decode
217
*/
218
function normalize<T>(fn: (val: T) => T): Coder<T, T>;
219
```
220
221
**Usage Examples:**
222
223
```typescript
224
import { utils } from "@scure/base";
225
226
// Create case-insensitive decoder (like base32crockford)
227
const caseNormalizer = utils.normalize((s: string) => s.toUpperCase());
228
const encoded = caseNormalizer.encode("Hello"); // "Hello" (no change on encode)
229
const decoded = caseNormalizer.decode("hello"); // "HELLO" (normalized on decode)
230
231
// Create character substitution (like base32crockford)
232
const charNormalizer = utils.normalize((s: string) =>
233
s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1')
234
);
235
const normalized = charNormalizer.decode("hel1O"); // "HEL10" (O->0, I/L->1)
236
```
237
238
### Direct Radix Conversion
239
240
Low-level radix conversion functions for direct use.
241
242
```typescript { .api }
243
/**
244
* Direct radix conversion between arbitrary bases (O(n^2) complexity)
245
* @param data - Input digits in source base
246
* @param from - Source radix
247
* @param to - Target radix
248
* @returns Digits in target radix
249
*/
250
function convertRadix(data: number[], from: number, to: number): number[];
251
252
/**
253
* Optimized radix conversion for power-of-2 bases
254
* @param data - Input digits
255
* @param from - Source radix (power of 2, ≤32)
256
* @param to - Target radix (power of 2, ≤32)
257
* @param padding - Whether to add padding
258
* @returns Converted digits
259
*/
260
function convertRadix2(
261
data: number[],
262
from: number,
263
to: number,
264
padding: boolean
265
): number[];
266
```
267
268
**Usage Examples:**
269
270
```typescript
271
import { utils } from "@scure/base";
272
273
// Convert from base 10 to base 16
274
const decimal = [1, 2, 3]; // Represents 123 in base 10
275
const hex = utils.convertRadix(decimal, 10, 16); // Convert to base 16
276
277
// Efficient binary to base32 conversion
278
const binary = [1, 0, 1, 1, 0]; // 5 bits
279
const base32 = utils.convertRadix2(binary, 2, 32, true); // Convert 2^1 to 2^5
280
```
281
282
## Building Custom Encoders
283
284
Combine utilities to create domain-specific encoders:
285
286
```typescript
287
import { utils } from "@scure/base";
288
import { sha256 } from "@noble/hashes/sha2";
289
290
// Example: Custom cryptocurrency address encoder
291
const cryptoAddress = utils.chain(
292
utils.checksum(4, (data) => sha256(sha256(data))), // Double SHA-256 checksum
293
utils.radix(58), // Convert to base58 digits
294
utils.alphabet('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'),
295
utils.join('') // Join to string
296
);
297
298
// Example: Custom data format with metadata
299
const dataFormat = utils.chain(
300
utils.radix2(6), // 6-bit encoding like base64
301
utils.alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'),
302
utils.padding(6, '='), // Base64-style padding
303
utils.join('-'), // Use dashes as separators
304
utils.normalize((s: string) => s.toLowerCase()) // Normalize to lowercase
305
);
306
307
// Example: Compact binary encoding
308
const compactBinary = utils.chain(
309
utils.radix2(8), // Keep as bytes
310
utils.alphabet('0123456789ABCDEF'), // Hex alphabet
311
utils.join('') // Standard hex string
312
);
313
```
314
315
## Performance Notes
316
317
- `radix()` has O(n^2) complexity - use only with small inputs
318
- `radix2()` has O(n) complexity - preferred for power-of-2 bases
319
- `convertRadix()` and `convertRadix2()` are the underlying conversion functions
320
- Built-in encoders (base64, hex) use optimized implementations when available
321
322
## Error Handling
323
324
All utility functions validate inputs and provide descriptive errors:
325
326
```typescript
327
import { utils } from "@scure/base";
328
329
try {
330
utils.radix(1); // Base must be ≥ 2
331
} catch (error) {
332
console.log(error.message); // "convertRadix: invalid from=1, base cannot be less than 2"
333
}
334
335
try {
336
utils.alphabet('ABC').decode(['D']); // Invalid character
337
} catch (error) {
338
console.log(error.message); // "Unknown letter: D. Allowed: ABC"
339
}
340
341
try {
342
utils.padding(6).decode(['A', 'B', '=', '=', '=']); // Too much padding
343
} catch (error) {
344
console.log(error.message); // "padding: invalid, string has too much padding"
345
}
346
```