123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- const base64enc =
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
- const base64dec = Object.freeze(Object.fromEntries(
- Array.from(base64enc, (c, i) => [c, i])
- ));
- class UnicodeRange {
- constructor(begin, end) {
- this.begin = begin;
- this.end = end;
- this.length = end - begin;
- }
- *keys() {
- const { begin, end } = this;
- for (let i = begin; i < end; ++i) {
- yield i;
- }
- }
- *values() {
- const { begin, end } = this;
- for (let i = begin; i < end; ++i) {
- yield String.fromCodePoint(i);
- }
- }
- }
- /**
- * Base64 decode variable-length deltas (5/10/15/21-bit).
- */
- function decodeDeltas(input) {
- const output = [];
- for (let i = 0; i < input.length; ) {
- let x = base64dec[input[i++]];
- switch (x & 56) {
- case 32:
- case 40:
- x = (x & 15) << 6;
- x |= base64dec[input[i++]];
- break;
- case 48:
- x = (x & 7) << 12;
- x |= base64dec[input[i++]] << 6;
- x |= base64dec[input[i++]];
- break;
- case 56:
- x = (x & 7) << 18;
- x |= base64dec[input[i++]] << 12;
- x |= base64dec[input[i++]] << 6;
- x |= base64dec[input[i++]];
- break;
- }
- output.push(x);
- }
- return output;
- }
- /**
- * Base64 encode variable-length deltas (5/10/15/21-bit).
- */
- function encodeDeltas(input) {
- const output = [];
- for (let i = 0; i < input.length; ++i) {
- const x = input[i];
- if ((x >> 5) === 0) {
- output.push(x);
- } else if ((x >> 10) === 0) {
- output.push(32 + (x >> 6), x);
- } else if ((x >> 15) === 0) {
- output.push(48 + (x >> 12), x >> 6, x);
- } else {
- console.assert((x >> 21) === 0, `delta ${x} out of range`);
- output.push(56 + (x >> 18), x >> 12, x >> 6, x);
- }
- }
- return output.map(x => base64enc[x & 63]).join('');
- }
- /**
- * RLE + base64 decode code point ranges.
- */
- function decodeRanges(input) {
- const deltas = decodeDeltas(input);
- const ranges = [];
- for (let end = -1, i = 1; i < deltas.length; i += 2) {
- const begin = end + 1 + deltas[i - 1];
- const length = 1 + deltas[i];
- end = begin + length;
- ranges.push(new UnicodeRange(begin, end));
- }
- return ranges;
- }
- /**
- * RLE + base64 encode code point ranges.
- */
- function encodeRanges(values) {
- const deltas = [];
- for (let end = -1, i = 0; i < values.length; ) {
- const begin = values[i];
- console.assert(begin > end, `code point ${begin} out of order`);
- deltas.push(begin - end - 1);
- end = begin + 1;
- while (++i < values.length && values[i] === end) {
- ++end;
- }
- deltas.push(end - begin - 1);
- }
- return encodeDeltas(deltas);
- }
- module.exports = Object.defineProperty(
- decodeRanges, 'encode', { value: encodeRanges }
- );
|