鉴于原生的btoa() atob() TextEncoder() TextDecoder()存在的问题,我自己动手丰衣足食,修改自一些著名的C语言实现,见以下的参考链接。
UTF-8编解码,参考:
const utf8_encoding_table = [
{ cmask: 0x80, cval: 0x00, shift: 0, lmask: 0x7F, lval: 0 },
{ cmask: 0xE0, cval: 0xC0, shift: 6, lmask: 0x7FF, lval: 0x80 },
{ cmask: 0xF0, cval: 0xE0, shift: 12, lmask: 0xFFFF, lval: 0x800 },
{ cmask: 0xF8, cval: 0xF0, shift: 18, lmask: 0x1FFFFF, lval: 0x10000 },
{ cmask: 0xFC, cval: 0xF8, shift: 24, lmask: 0x3FFFFFF, lval: 0x200000 },
{ cmask: 0xFE, cval: 0xFC, shift: 30, lmask: 0x7FFFFFFF, lval: 0x4000000 },
];
export const utf8_encode = s => {
let chars = [...s].map(c => c.codePointAt());
let bytes = [];
for (let wc of chars) {
let l = wc;
for (let t of utf8_encoding_table) {
if (l <= t.lmask) {
let c = t.shift;
bytes.push(t.cval | (l >> c));
while (c > 0) {
c -= 6;
bytes.push(0x80 | ((l >> c) & 0x3F));
}
break;
}
}
}
return bytes;
};
export const utf8_decode = bytes => {
// console.log(bytes);
let chars = [];
for (let s = 0; s < bytes.length; s++) {
let n = bytes.length - s;
let nc = 0;
let c0 = bytes[s] & 0xFF;
let l = c0;
for (let t of utf8_encoding_table) {
// console.log(l);
nc++;
if ((c0 & t.cmask) == t.cval) {
l &= t.lmask;
if (l < t.lval) break;
chars.push(l);
break;
}
if (n <= nc) break;
s++;
let c = (bytes[s] ^ 0x80) & 0xFF;
if (c & 0xC0) break;
l = (l << 6) | c;
}
}
// console.log(chars);
return String.fromCodePoint(...chars);
};
Base64编解码,参考:
- https://www.mycplus.com/source-code/c-source-code/base64-encode-decode/
- https://base64.guru/standards/base64url
const base64_encoding_table = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'];
const base64_decoding_table = {};
for (let i = 0; i < base64_encoding_table.length; i++) {
base64_decoding_table[base64_encoding_table[i]] = i;
}
export const base64_encode = s => {
let bytes = utf8_encode(s);
// console.log(bytes);
let qro = Math.ceil(bytes.length / 3);
let rem = qro * 3 - bytes.length;
let encoded_length = qro * 4 - rem;
let encoded = [];
for (let i = 0, j = 0; i < bytes.length;) {
let octet_a = i < bytes.length ? bytes[i++] : 0;
let octet_b = i < bytes.length ? bytes[i++] : 0;
let octet_c = i < bytes.length ? bytes[i++] : 0;
let triple = (octet_a << 16) + (octet_b << 8) + octet_c;
if (j < encoded_length) encoded[j++] = base64_encoding_table[(triple >> 18) & 0x3F];
if (j < encoded_length) encoded[j++] = base64_encoding_table[(triple >> 12) & 0x3F];
if (j < encoded_length) encoded[j++] = base64_encoding_table[(triple >> 6) & 0x3F];
if (j < encoded_length) encoded[j++] = base64_encoding_table[triple & 0x3F];
}
return encoded.join('');
};
export const base64_decode = s => {
s = [...s];
let qro = Math.ceil(s.length / 4);
let rem = qro * 4 - s.length;
if (rem == 3) {
throw new RangeError('Encoded string length can only be integral multiples of 4 minus 1 or 2');
}
let byte_length = qro * 3 - rem;
let bytes = [];
for (let i = 0, j = 0; i < s.length;) {
let sextet_a = i < s.length ? base64_decoding_table[s[i++]] : 0;
let sextet_b = i < s.length ? base64_decoding_table[s[i++]] : 0;
let sextet_c = i < s.length ? base64_decoding_table[s[i++]] : 0;
let sextet_d = i < s.length ? base64_decoding_table[s[i++]] : 0;
let triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d;
if (j < byte_length) bytes[j++] = (triple >> 16) & 0xFF;
if (j < byte_length) bytes[j++] = (triple >> 8) & 0xFF;
if (j < byte_length) bytes[j++] = triple & 0xFF;
}
// console.log(bytes);
return utf8_decode(bytes);
};