base64.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true,
  4. });
  5. exports.base64 = base64;
  6. exports.unbase64 = unbase64;
  7. function base64(input) {
  8. const utf8Array = stringToUTF8Array(input);
  9. let result = '';
  10. const length = utf8Array.length;
  11. const rest = length % 3;
  12. for (let i = 0; i < length - rest; i += 3) {
  13. const a = utf8Array[i];
  14. const b = utf8Array[i + 1];
  15. const c = utf8Array[i + 2];
  16. result += first6Bits(a);
  17. result += last2BitsAndFirst4Bits(a, b);
  18. result += last4BitsAndFirst2Bits(b, c);
  19. result += last6Bits(c);
  20. }
  21. if (rest === 1) {
  22. const a = utf8Array[length - 1];
  23. result += first6Bits(a) + last2BitsAndFirst4Bits(a, 0) + '==';
  24. } else if (rest === 2) {
  25. const a = utf8Array[length - 2];
  26. const b = utf8Array[length - 1];
  27. result +=
  28. first6Bits(a) +
  29. last2BitsAndFirst4Bits(a, b) +
  30. last4BitsAndFirst2Bits(b, 0) +
  31. '=';
  32. }
  33. return result;
  34. }
  35. function first6Bits(a) {
  36. return toBase64Char((a >> 2) & 63);
  37. }
  38. function last2BitsAndFirst4Bits(a, b) {
  39. return toBase64Char(((a << 4) | (b >> 4)) & 63);
  40. }
  41. function last4BitsAndFirst2Bits(b, c) {
  42. return toBase64Char(((b << 2) | (c >> 6)) & 63);
  43. }
  44. function last6Bits(c) {
  45. return toBase64Char(c & 63);
  46. }
  47. function unbase64(input) {
  48. const utf8Array = [];
  49. for (let i = 0; i < input.length; i += 4) {
  50. const a = fromBase64Char(input[i]);
  51. const b = fromBase64Char(input[i + 1]);
  52. const c = fromBase64Char(input[i + 2]);
  53. const d = fromBase64Char(input[i + 3]);
  54. if (a === -1 || b === -1 || c === -1 || d === -1) {
  55. /*
  56. * Previously we used Node's API for parsing Base64 and following code
  57. * Buffer.from(i, 'utf8').toString('base64')
  58. * That silently ignored incorrect input and returned empty string instead
  59. * Let's keep this behaviour for a time being and hopefully fix it in the future.
  60. */
  61. return '';
  62. }
  63. const bitmap24 = (a << 18) | (b << 12) | (c << 6) | d;
  64. utf8Array.push((bitmap24 >> 16) & 255);
  65. utf8Array.push((bitmap24 >> 8) & 255);
  66. utf8Array.push(bitmap24 & 255);
  67. }
  68. let paddingIndex = input.length - 1;
  69. while (input[paddingIndex] === '=') {
  70. --paddingIndex;
  71. utf8Array.pop();
  72. }
  73. return utf8ArrayToString(utf8Array);
  74. }
  75. const b64CharacterSet =
  76. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  77. function toBase64Char(bitMap6) {
  78. return b64CharacterSet.charAt(bitMap6);
  79. }
  80. function fromBase64Char(base64Char) {
  81. if (base64Char === undefined) {
  82. return -1;
  83. }
  84. return base64Char === '=' ? 0 : b64CharacterSet.indexOf(base64Char);
  85. }
  86. function stringToUTF8Array(input) {
  87. const result = [];
  88. for (const utfChar of input) {
  89. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  90. const code = utfChar.codePointAt(0);
  91. if (code < 0x80) {
  92. result.push(code);
  93. } else if (code < 0x800) {
  94. result.push(0xc0 | (code >> 6));
  95. result.push(0x80 | (code & 0x3f));
  96. } else if (code < 0x10000) {
  97. result.push(0xe0 | (code >> 12));
  98. result.push(0x80 | ((code >> 6) & 0x3f));
  99. result.push(0x80 | (code & 0x3f));
  100. } else {
  101. result.push(0xf0 | (code >> 18));
  102. result.push(0x80 | ((code >> 12) & 0x3f));
  103. result.push(0x80 | ((code >> 6) & 0x3f));
  104. result.push(0x80 | (code & 0x3f));
  105. }
  106. }
  107. return result;
  108. }
  109. function utf8ArrayToString(input) {
  110. let result = '';
  111. for (let i = 0; i < input.length; ) {
  112. const a = input[i++];
  113. if ((a & 0x80) === 0) {
  114. result += fromCodePoint(a);
  115. continue;
  116. }
  117. const b = input[i++];
  118. if ((a & 0xe0) === 0xc0) {
  119. result += fromCodePoint(((a & 0x1f) << 6) | (b & 0x3f));
  120. continue;
  121. }
  122. const c = input[i++];
  123. if ((a & 0xf0) === 0xe0) {
  124. result += fromCodePoint(
  125. ((a & 0x0f) << 12) | ((b & 0x3f) << 6) | (c & 0x3f),
  126. );
  127. continue;
  128. }
  129. const d = input[i++];
  130. result += fromCodePoint(
  131. ((a & 0x07) << 18) | ((b & 0x3f) << 12) | ((c & 0x3f) << 6) | (d & 0x3f),
  132. );
  133. }
  134. return result;
  135. }
  136. function fromCodePoint(code) {
  137. if (code > 0x10ffff) {
  138. /*
  139. * Previously we used Node's API for parsing Base64 and following code
  140. * Buffer.from(i, 'base64').toString('utf8')
  141. * That silently ignored incorrect input and returned empty string instead
  142. * Let's keep this behaviour for a time being and hopefully fix it in the future.
  143. */
  144. return '';
  145. }
  146. return String.fromCodePoint(code);
  147. }