Created
March 13, 2024 10:45
-
-
Save yanislav-igonin/8b6f179dc3da3dbcf10b47557d2f0141 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const encoder = new TextEncoder(); | |
const decoder = new TextDecoder('utf-8', { ignoreBOM: true }); | |
const U8 = { | |
/** @param {string} data */ | |
encode: (data) => { | |
return encoder.encode(data); | |
}, | |
/** @param {Uint8Array} data */ | |
decode: (data) => { | |
return decoder.decode(data); | |
}, | |
}; | |
const Uint1Array = { | |
/* API */ | |
encode: (bytes) => { | |
const bits = new Uint8Array(bytes.length * 8); | |
for (let cursor = 0, i = 0, l = bytes.length; i < l; i++) { | |
const byte = bytes[i]; | |
bits[cursor++] = (byte >> 7) & 1; | |
bits[cursor++] = (byte >> 6) & 1; | |
bits[cursor++] = (byte >> 5) & 1; | |
bits[cursor++] = (byte >> 4) & 1; | |
bits[cursor++] = (byte >> 3) & 1; | |
bits[cursor++] = (byte >> 2) & 1; | |
bits[cursor++] = (byte >> 1) & 1; | |
bits[cursor++] = byte & 1; | |
} | |
return bits; | |
}, | |
decode: (bits) => { | |
const bytes = new Uint8Array(Math.ceil(bits.length / 8)); | |
for (let cursor = 0, i = 0, l = bits.length; i < l; i += 8) { | |
const b1 = bits[i]; | |
const b2 = bits[i + 1] | 0; | |
const b3 = bits[i + 2] | 0; | |
const b4 = bits[i + 3] | 0; | |
const b5 = bits[i + 4] | 0; | |
const b6 = bits[i + 5] | 0; | |
const b7 = bits[i + 6] | 0; | |
const b8 = bits[i + 7] | 0; | |
bytes[cursor++] = | |
(b1 << 7) | | |
(b2 << 6) | | |
(b3 << 5) | | |
(b4 << 4) | | |
(b5 << 3) | | |
(b6 << 2) | | |
(b7 << 1) | | |
b8; | |
} | |
return bytes; | |
}, | |
}; | |
const Uint7Array = { | |
/* API */ | |
encode: (bits) => { | |
const uint7 = new Uint8Array(Math.ceil(bits.length / 7) + 1); | |
for (let cursor = 0, i = 0, l = bits.length; i < l; i += 7) { | |
const b1 = bits[i]; | |
const b2 = bits[i + 1] | 0; | |
const b3 = bits[i + 2] | 0; | |
const b4 = bits[i + 3] | 0; | |
const b5 = bits[i + 4] | 0; | |
const b6 = bits[i + 5] | 0; | |
const b7 = bits[i + 6] | 0; | |
uint7[cursor++] = | |
(b1 << 6) | (b2 << 5) | (b3 << 4) | (b4 << 3) | (b5 << 2) | (b6 << 1) | b7; | |
} | |
uint7[uint7.length - 1] = 7 - (bits.length % 7 || 7); // The last item contains the number of extra bits used for padding, that should be trimmed when decoding | |
return uint7; | |
}, | |
decode: (uint7) => { | |
const bits = new Uint8Array( | |
Math.max(0, (uint7.length - 1) * 7 - (uint7[uint7.length - 1] || 0)) | |
); | |
for (let cursor = 0, i = 0, l = uint7.length - 1; i < l; i++) { | |
const byte = uint7[i]; | |
bits[cursor++] = (byte >> 6) & 1; | |
bits[cursor++] = (byte >> 5) & 1; | |
bits[cursor++] = (byte >> 4) & 1; | |
bits[cursor++] = (byte >> 3) & 1; | |
bits[cursor++] = (byte >> 2) & 1; | |
bits[cursor++] = (byte >> 1) & 1; | |
bits[cursor++] = byte & 1; | |
} | |
return bits; | |
}, | |
}; | |
const MAX_ARGUMENTS_LENGTH = 8192; // Above this threshold we risk a "call stack size exceeded" error | |
/** @param {ArrayLike<number> | Uint8Array | Uint16Array | Uint32Array} charCodes @returns {string} */ | |
const fromCharCodes = (charCodes) => { | |
if (!Array.isArray(charCodes)) return fromCharCodes(Array.from(charCodes)); | |
const { length } = charCodes; | |
if (length <= MAX_ARGUMENTS_LENGTH) { | |
// Small-enough length, no need to use chunks | |
return String.fromCharCode.apply(String, charCodes); | |
} else { | |
// Large-enough length, encoding in chunks | |
const chunks = []; | |
for (let ci = 0, i = 0; i < length; ci++) { | |
const slice = charCodes.slice(i, (i += MAX_ARGUMENTS_LENGTH)); | |
chunks[ci] = String.fromCharCode.apply(String, slice); | |
} | |
return chunks.join(''); | |
} | |
}; | |
const Base128 = { | |
/* API */ | |
encode: (data) => { | |
return fromCharCodes(Uint7Array.encode(Uint1Array.encode(data))); | |
}, | |
encodeStr: (data) => { | |
return Base128.encode(U8.encode(data)); | |
}, | |
decode: (data) => { | |
const uint7 = new Uint8Array(data.length); | |
for (let i = 0, l = data.length; i < l; i++) { | |
uint7[i] = data.charCodeAt(i); | |
} | |
return Uint1Array.decode(Uint7Array.decode(uint7)); | |
}, | |
decodeStr: (data) => { | |
return U8.decode(Base128.decode(data)); | |
}, | |
}; | |
const message = '201873|720489|123qweas|123qweas'; | |
const encoded = Base128.encodeStr(message); | |
const decoded = Base128.decodeStr(encoded); | |
console.log(encoded); | |
console.log(decoded); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment