decode-ranges.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. const base64enc =
  2. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
  3. const base64dec = Object.freeze(Object.fromEntries(
  4. Array.from(base64enc, (c, i) => [c, i])
  5. ));
  6. class UnicodeRange {
  7. constructor(begin, end) {
  8. this.begin = begin;
  9. this.end = end;
  10. this.length = end - begin;
  11. }
  12. *keys() {
  13. const { begin, end } = this;
  14. for (let i = begin; i < end; ++i) {
  15. yield i;
  16. }
  17. }
  18. *values() {
  19. const { begin, end } = this;
  20. for (let i = begin; i < end; ++i) {
  21. yield String.fromCodePoint(i);
  22. }
  23. }
  24. }
  25. /**
  26. * Base64 decode variable-length deltas (5/10/15/21-bit).
  27. */
  28. function decodeDeltas(input) {
  29. const output = [];
  30. for (let i = 0; i < input.length; ) {
  31. let x = base64dec[input[i++]];
  32. switch (x & 56) {
  33. case 32:
  34. case 40:
  35. x = (x & 15) << 6;
  36. x |= base64dec[input[i++]];
  37. break;
  38. case 48:
  39. x = (x & 7) << 12;
  40. x |= base64dec[input[i++]] << 6;
  41. x |= base64dec[input[i++]];
  42. break;
  43. case 56:
  44. x = (x & 7) << 18;
  45. x |= base64dec[input[i++]] << 12;
  46. x |= base64dec[input[i++]] << 6;
  47. x |= base64dec[input[i++]];
  48. break;
  49. }
  50. output.push(x);
  51. }
  52. return output;
  53. }
  54. /**
  55. * Base64 encode variable-length deltas (5/10/15/21-bit).
  56. */
  57. function encodeDeltas(input) {
  58. const output = [];
  59. for (let i = 0; i < input.length; ++i) {
  60. const x = input[i];
  61. if ((x >> 5) === 0) {
  62. output.push(x);
  63. } else if ((x >> 10) === 0) {
  64. output.push(32 + (x >> 6), x);
  65. } else if ((x >> 15) === 0) {
  66. output.push(48 + (x >> 12), x >> 6, x);
  67. } else {
  68. console.assert((x >> 21) === 0, `delta ${x} out of range`);
  69. output.push(56 + (x >> 18), x >> 12, x >> 6, x);
  70. }
  71. }
  72. return output.map(x => base64enc[x & 63]).join('');
  73. }
  74. /**
  75. * RLE + base64 decode code point ranges.
  76. */
  77. function decodeRanges(input) {
  78. const deltas = decodeDeltas(input);
  79. const ranges = [];
  80. for (let end = -1, i = 1; i < deltas.length; i += 2) {
  81. const begin = end + 1 + deltas[i - 1];
  82. const length = 1 + deltas[i];
  83. end = begin + length;
  84. ranges.push(new UnicodeRange(begin, end));
  85. }
  86. return ranges;
  87. }
  88. /**
  89. * RLE + base64 encode code point ranges.
  90. */
  91. function encodeRanges(values) {
  92. const deltas = [];
  93. for (let end = -1, i = 0; i < values.length; ) {
  94. const begin = values[i];
  95. console.assert(begin > end, `code point ${begin} out of order`);
  96. deltas.push(begin - end - 1);
  97. end = begin + 1;
  98. while (++i < values.length && values[i] === end) {
  99. ++end;
  100. }
  101. deltas.push(end - begin - 1);
  102. }
  103. return encodeDeltas(deltas);
  104. }
  105. module.exports = Object.defineProperty(
  106. decodeRanges, 'encode', { value: encodeRanges }
  107. );