-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathencoding.ts
170 lines (148 loc) · 4.65 KB
/
encoding.ts
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* Utilities for encoding and decoding binary representations like hex and
* base64 strings.
* @module
*/
const encoder = new TextEncoder();
function asUint8Array(data: string | ArrayBufferLike | ArrayBufferView): Uint8Array {
let bytes: Uint8Array;
if (typeof data === "string") {
bytes = encoder.encode(data);
} else if (!(data instanceof Uint8Array)) {
if (ArrayBuffer.isView(data)) {
bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
} else {
bytes = new Uint8Array(data);
}
} else {
bytes = data;
}
return bytes;
}
/**
* Encodes the given data to a hex string.
*
* @example
* ```ts
* import { encodeHex } from "@ayonli/jsext/encoding";
*
* const hex = encodeHex("Hello, World!");
* console.log(hex); // "48656c6c6f2c20576f726c6421"
*
* const hex2 = encodeHex(new Uint8Array([72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33]));
* console.log(hex2); // "48656c6c6f2c20576f726c6421"
* ```
*/
export function encodeHex(data: string | ArrayBufferLike | ArrayBufferView): string {
const bytes = asUint8Array(data);
if (typeof Buffer === "function" && bytes instanceof Buffer) {
return bytes.toString("hex");
}
return bytes.reduce((str, byte) => {
return str + byte.toString(16).padStart(2, "0");
}, "");
}
/**
* Decodes the given hex string to a byte array.
*
* @example
* ```ts
* import { decodeHex } from "@ayonli/jsext/encoding";
*
* const data = decodeHex("48656c6c6f2c20576f726c6421");
* console.log(data); // Uint8Array(13) [ 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33 ]
*/
export function decodeHex(hex: string): Uint8Array {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
}
return bytes;
}
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* Encodes the given data to a base64 string.
*
* Unlike the built-in `btoa` function, this function is not limited to the
* Latin1 range.
*
* @example
* ```ts
* import { encodeBase64 } from "@ayonli/jsext/encoding";
*
* const base64 = encodeBase64("Hello, World!");
* console.log(base64); // "SGVsbG8sIFdvcmxkIQ=="
*
* const base64_2 = encodeBase64("你好,世界!");
* console.log(base64_2); // "5L2g5aW977yM5LiW55WM77yB"
* ```
*/
export function encodeBase64(data: string | ArrayBufferLike | Uint8Array): string {
const bytes = asUint8Array(data);
if (typeof Buffer === "function" && bytes instanceof Buffer) {
return bytes.toString("base64");
}
let result = "";
let i: number;
const l = bytes.length;
for (i = 2; i < l; i += 3) {
result += base64Chars[(bytes[i - 2]!) >> 2];
result += base64Chars[
(((bytes[i - 2]!) & 0x03) << 4) |
((bytes[i - 1]!) >> 4)
];
result += base64Chars[
(((bytes[i - 1]!) & 0x0f) << 2) |
((bytes[i]!) >> 6)
];
result += base64Chars[(bytes[i]!) & 0x3f];
}
if (i === l + 1) {
// 1 octet yet to write
result += base64Chars[(bytes[i - 2]!) >> 2];
result += base64Chars[((bytes[i - 2]!) & 0x03) << 4];
result += "==";
}
if (i === l) {
// 2 octets yet to write
result += base64Chars[(bytes[i - 2]!) >> 2];
result += base64Chars[
(((bytes[i - 2]!) & 0x03) << 4) |
((bytes[i - 1]!) >> 4)
];
result += base64Chars[((bytes[i - 1]!) & 0x0f) << 2];
result += "=";
}
return result;
}
/**
* Decodes the given base64 string to a byte array.
*
* Unlike the built-in `atob` function, this function is not limited to the
* Latin1 range.
*
* @example
* ```ts
* import { decodeBase64 } from "@ayonli/jsext/encoding";
*
* const data = decodeBase64("SGVsbG8sIFdvcmxkIQ==");
* console.log(new TextDecoder().decode(data)); // "Hello, World!"
*
* const data2 = decodeBase64("5L2g5aW977yM5LiW55WM77yB");
* console.log(new TextDecoder().decode(data2)); // "你好,世界!"
* ```
*/
export function decodeBase64(base64: string): Uint8Array {
if (typeof Buffer === "function" && typeof Buffer.from === "function") {
const buf = Buffer.from(base64, "base64");
const buffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
return new Uint8Array(buffer);
}
const binString = atob(base64);
const size = binString.length;
const bytes = new Uint8Array(size);
for (let i = 0; i < size; i++) {
bytes[i] = binString.charCodeAt(i);
}
return bytes;
}