-
Notifications
You must be signed in to change notification settings - Fork 115
Expand file tree
/
Copy pathcrockford.ts
More file actions
88 lines (83 loc) · 2.9 KB
/
Copy pathcrockford.ts
File metadata and controls
88 lines (83 loc) · 2.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Code from https://github.com/devbanana/crockford-base32/blob/develop/src/index.ts
import { B32_CHARACTERS, B32_CHARACTERS_LOOKUP, ENCODING, ENCODING_LEN, ENCODING_LOOKUP } from "./constants.js";
import { ULIDError, ULIDErrorCode } from "./error.js";
import { replaceCharAt } from "./utils.js";
export function crockfordEncode(input: Uint8Array): string {
const output: number[] = [];
let bitsRead = 0;
let buffer = 0;
const reversedInput = new Uint8Array(input.slice().reverse());
for (const byte of reversedInput) {
buffer |= byte << bitsRead;
bitsRead += 8;
while (bitsRead >= 5) {
output.unshift(buffer & 0x1f);
buffer >>>= 5;
bitsRead -= 5;
}
}
if (bitsRead > 0) {
output.unshift(buffer & 0x1f);
}
return output.map(byte => B32_CHARACTERS.charAt(byte)).join("");
}
export function crockfordDecode(input: string): Uint8Array {
const sanitizedInput = input.toUpperCase().split("").reverse().join("");
const output: number[] = [];
let bitsRead = 0;
let buffer = 0;
for (const character of sanitizedInput) {
const byte = B32_CHARACTERS_LOOKUP.get(character);
if (byte === undefined) {
throw new Error(`Invalid base 32 character found in string: ${character}`);
}
buffer |= byte << bitsRead;
bitsRead += 5;
while (bitsRead >= 8) {
output.unshift(buffer & 0xff);
buffer >>>= 8;
bitsRead -= 8;
}
}
if (bitsRead >= 5 || buffer > 0) {
output.unshift(buffer & 0xff);
}
return new Uint8Array(output);
}
/**
* Fix a ULID's Base32 encoding -
* i and l (case-insensitive) will be treated as 1 and o (case-insensitive) will be treated as 0.
* hyphens are ignored during decoding.
* @param id The ULID
* @returns The cleaned up ULID
*/
export function fixULIDBase32(id: string): string {
return id.replace(/i/gi, "1").replace(/l/gi, "1").replace(/o/gi, "0").replace(/-/g, "");
}
export function incrementBase32(str: string): string {
let done: string | undefined = undefined,
index = str.length,
char: string,
charIndex: number,
output = str;
const maxCharIndex = ENCODING_LEN - 1;
while (!done && index-- >= 0) {
char = output[index];
charIndex = ENCODING_LOOKUP.get(char);
if (charIndex === undefined) {
throw new ULIDError(
ULIDErrorCode.Base32IncorrectEncoding,
"Incorrectly encoded string"
);
}
if (charIndex === maxCharIndex) {
output = replaceCharAt(output, index, ENCODING[0]);
continue;
}
done = replaceCharAt(output, index, ENCODING[charIndex + 1]);
}
if (typeof done === "string") {
return done;
}
throw new ULIDError(ULIDErrorCode.Base32IncorrectEncoding, "Failed incrementing string");
}