base64.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /**
  2. * base64.ts
  3. *
  4. * Licensed under the BSD 3-Clause License.
  5. * http://opensource.org/licenses/BSD-3-Clause
  6. *
  7. * References:
  8. * http://en.wikipedia.org/wiki/Base64
  9. *
  10. * @author Dan Kogai (https://github.com/dankogai)
  11. */
  12. const version = '3.6.1';
  13. /**
  14. * @deprecated use lowercase `version`.
  15. */
  16. const VERSION = version;
  17. const _hasatob = typeof atob === 'function';
  18. const _hasbtoa = typeof btoa === 'function';
  19. const _hasBuffer = typeof Buffer === 'function';
  20. const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
  21. const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
  22. const b64ch =
  23. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  24. const b64chs = [...b64ch];
  25. const b64tab = ((a) => {
  26. let tab = {};
  27. a.forEach((c, i) => tab[c] = i);
  28. return tab;
  29. })(b64chs);
  30. const b64re =
  31. /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
  32. const _fromCC = String.fromCharCode.bind(String);
  33. const _U8Afrom = typeof Uint8Array.from === 'function'
  34. ? Uint8Array.from.bind(Uint8Array)
  35. : (it, fn: (any) => number = (x) => x) =>
  36. new Uint8Array(Array.prototype.slice.call(it, 0).map(fn));
  37. const _mkUriSafe = (src: string) => src
  38. .replace(/[+\/]/g, (m0) => m0 == '+' ? '-' : '_')
  39. .replace(/=+$/m, '');
  40. const _tidyB64 = (s: string) => s.replace(/[^A-Za-z0-9\+\/]/g, '');
  41. /**
  42. * polyfill version of `btoa`
  43. */
  44. const btoaPolyfill = (bin: string) => {
  45. // console.log('polyfilled');
  46. let u32, c0, c1, c2, asc = '';
  47. const pad = bin.length % 3;
  48. for (let i = 0; i < bin.length;) {
  49. if ((c0 = bin.charCodeAt(i++)) > 255 ||
  50. (c1 = bin.charCodeAt(i++)) > 255 ||
  51. (c2 = bin.charCodeAt(i++)) > 255)
  52. throw new TypeError('invalid character found');
  53. u32 = (c0 << 16) | (c1 << 8) | c2;
  54. asc += b64chs[u32 >> 18 & 63]
  55. + b64chs[u32 >> 12 & 63]
  56. + b64chs[u32 >> 6 & 63]
  57. + b64chs[u32 & 63];
  58. }
  59. return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
  60. };
  61. /**
  62. * does what `window.btoa` of web browsers do.
  63. * @param {String} bin binary string
  64. * @returns {string} Base64-encoded string
  65. */
  66. const _btoa = _hasbtoa ? (bin: string) => btoa(bin)
  67. : _hasBuffer ? (bin: string) => Buffer.from(bin, 'binary').toString('base64')
  68. : btoaPolyfill;
  69. const _fromUint8Array = _hasBuffer
  70. ? (u8a: Uint8Array) => Buffer.from(u8a).toString('base64')
  71. : (u8a: Uint8Array) => {
  72. // cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326
  73. const maxargs = 0x1000;
  74. let strs = [];
  75. for (let i = 0, l = u8a.length; i < l; i += maxargs) {
  76. strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
  77. }
  78. return _btoa(strs.join(''));
  79. };
  80. /**
  81. * converts a Uint8Array to a Base64 string.
  82. * @param {boolean} [urlsafe] URL-and-filename-safe a la RFC4648 §5
  83. * @returns {string} Base64 string
  84. */
  85. const fromUint8Array = (u8a: Uint8Array, urlsafe = false) =>
  86. urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
  87. // This trick is found broken https://github.com/dankogai/js-base64/issues/130
  88. // const utob = (src: string) => unescape(encodeURIComponent(src));
  89. // reverting good old fationed regexp
  90. const cb_utob = (c: string) => {
  91. if (c.length < 2) {
  92. var cc = c.charCodeAt(0);
  93. return cc < 0x80 ? c
  94. : cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6))
  95. + _fromCC(0x80 | (cc & 0x3f)))
  96. : (_fromCC(0xe0 | ((cc >>> 12) & 0x0f))
  97. + _fromCC(0x80 | ((cc >>> 6) & 0x3f))
  98. + _fromCC(0x80 | (cc & 0x3f)));
  99. } else {
  100. var cc = 0x10000
  101. + (c.charCodeAt(0) - 0xD800) * 0x400
  102. + (c.charCodeAt(1) - 0xDC00);
  103. return (_fromCC(0xf0 | ((cc >>> 18) & 0x07))
  104. + _fromCC(0x80 | ((cc >>> 12) & 0x3f))
  105. + _fromCC(0x80 | ((cc >>> 6) & 0x3f))
  106. + _fromCC(0x80 | (cc & 0x3f)));
  107. }
  108. };
  109. const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
  110. /**
  111. * @deprecated should have been internal use only.
  112. * @param {string} src UTF-8 string
  113. * @returns {string} UTF-16 string
  114. */
  115. const utob = (u: string) => u.replace(re_utob, cb_utob);
  116. //
  117. const _encode = _hasBuffer
  118. ? (s: string) => Buffer.from(s, 'utf8').toString('base64')
  119. : _TE
  120. ? (s: string) => _fromUint8Array(_TE.encode(s))
  121. : (s: string) => _btoa(utob(s));
  122. /**
  123. * converts a UTF-8-encoded string to a Base64 string.
  124. * @param {boolean} [urlsafe] if `true` make the result URL-safe
  125. * @returns {string} Base64 string
  126. */
  127. const encode = (src: string, urlsafe = false) => urlsafe
  128. ? _mkUriSafe(_encode(src))
  129. : _encode(src);
  130. /**
  131. * converts a UTF-8-encoded string to URL-safe Base64 RFC4648 §5.
  132. * @returns {string} Base64 string
  133. */
  134. const encodeURI = (src: string) => encode(src, true);
  135. // This trick is found broken https://github.com/dankogai/js-base64/issues/130
  136. // const btou = (src: string) => decodeURIComponent(escape(src));
  137. // reverting good old fationed regexp
  138. const re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
  139. const cb_btou = (cccc: string) => {
  140. switch (cccc.length) {
  141. case 4:
  142. var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
  143. | ((0x3f & cccc.charCodeAt(1)) << 12)
  144. | ((0x3f & cccc.charCodeAt(2)) << 6)
  145. | (0x3f & cccc.charCodeAt(3)),
  146. offset = cp - 0x10000;
  147. return (_fromCC((offset >>> 10) + 0xD800)
  148. + _fromCC((offset & 0x3FF) + 0xDC00));
  149. case 3:
  150. return _fromCC(
  151. ((0x0f & cccc.charCodeAt(0)) << 12)
  152. | ((0x3f & cccc.charCodeAt(1)) << 6)
  153. | (0x3f & cccc.charCodeAt(2))
  154. );
  155. default:
  156. return _fromCC(
  157. ((0x1f & cccc.charCodeAt(0)) << 6)
  158. | (0x3f & cccc.charCodeAt(1))
  159. );
  160. }
  161. };
  162. /**
  163. * @deprecated should have been internal use only.
  164. * @param {string} src UTF-16 string
  165. * @returns {string} UTF-8 string
  166. */
  167. const btou = (b: string) => b.replace(re_btou, cb_btou);
  168. /**
  169. * polyfill version of `atob`
  170. */
  171. const atobPolyfill = (asc: string) => {
  172. // console.log('polyfilled');
  173. asc = asc.replace(/\s+/g, '');
  174. if (!b64re.test(asc)) throw new TypeError('malformed base64.');
  175. asc += '=='.slice(2 - (asc.length & 3));
  176. let u24, bin = '', r1, r2;
  177. for (let i = 0; i < asc.length;) {
  178. u24 = b64tab[asc.charAt(i++)] << 18
  179. | b64tab[asc.charAt(i++)] << 12
  180. | (r1 = b64tab[asc.charAt(i++)]) << 6
  181. | (r2 = b64tab[asc.charAt(i++)]);
  182. bin += r1 === 64 ? _fromCC(u24 >> 16 & 255)
  183. : r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255)
  184. : _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
  185. }
  186. return bin;
  187. };
  188. /**
  189. * does what `window.atob` of web browsers do.
  190. * @param {String} asc Base64-encoded string
  191. * @returns {string} binary string
  192. */
  193. const _atob = _hasatob ? (asc: string) => atob(_tidyB64(asc))
  194. : _hasBuffer ? (asc: string) => Buffer.from(asc, 'base64').toString('binary')
  195. : atobPolyfill;
  196. //
  197. const _toUint8Array = _hasBuffer
  198. ? (a: string) => _U8Afrom(Buffer.from(a, 'base64'))
  199. : (a: string) => _U8Afrom(_atob(a), c => c.charCodeAt(0));
  200. /**
  201. * converts a Base64 string to a Uint8Array.
  202. */
  203. const toUint8Array = (a: string): Uint8Array => _toUint8Array(_unURI(a));
  204. //
  205. const _decode = _hasBuffer
  206. ? (a: string) => Buffer.from(a, 'base64').toString('utf8')
  207. : _TD
  208. ? (a: string) => _TD.decode(_toUint8Array(a))
  209. : (a: string) => btou(_atob(a));
  210. const _unURI = (a: string) =>
  211. _tidyB64(a.replace(/[-_]/g, (m0) => m0 == '-' ? '+' : '/'));
  212. /**
  213. * converts a Base64 string to a UTF-8 string.
  214. * @param {String} src Base64 string. Both normal and URL-safe are supported
  215. * @returns {string} UTF-8 string
  216. */
  217. const decode = (src: string) => _decode(_unURI(src));
  218. /**
  219. * check if a value is a valid Base64 string
  220. * @param {String} src a value to check
  221. */
  222. const isValid = (src: any) => {
  223. if (typeof src !== 'string') return false;
  224. const s = src.replace(/\s+/g, '').replace(/=+$/, '');
  225. return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s);
  226. };
  227. //
  228. const _noEnum = (v) => {
  229. return {
  230. value: v, enumerable: false, writable: true, configurable: true
  231. };
  232. };
  233. /**
  234. * extend String.prototype with relevant methods
  235. */
  236. const extendString = function () {
  237. const _add = (name, body) => Object.defineProperty(
  238. String.prototype, name, _noEnum(body)
  239. );
  240. _add('fromBase64', function () { return decode(this) });
  241. _add('toBase64', function (urlsafe) { return encode(this, urlsafe) });
  242. _add('toBase64URI', function () { return encode(this, true) });
  243. _add('toBase64URL', function () { return encode(this, true) });
  244. _add('toUint8Array', function () { return toUint8Array(this) });
  245. };
  246. /**
  247. * extend Uint8Array.prototype with relevant methods
  248. */
  249. const extendUint8Array = function () {
  250. const _add = (name, body) => Object.defineProperty(
  251. Uint8Array.prototype, name, _noEnum(body)
  252. );
  253. _add('toBase64', function (urlsafe) { return fromUint8Array(this, urlsafe) });
  254. _add('toBase64URI', function () { return fromUint8Array(this, true) });
  255. _add('toBase64URL', function () { return fromUint8Array(this, true) });
  256. };
  257. /**
  258. * extend Builtin prototypes with relevant methods
  259. */
  260. const extendBuiltins = () => {
  261. extendString();
  262. extendUint8Array();
  263. }
  264. const gBase64 = {
  265. version: version,
  266. VERSION: VERSION,
  267. atob: _atob,
  268. atobPolyfill: atobPolyfill,
  269. btoa: _btoa,
  270. btoaPolyfill: btoaPolyfill,
  271. fromBase64: decode,
  272. toBase64: encode,
  273. encode: encode,
  274. encodeURI: encodeURI,
  275. encodeURL: encodeURI,
  276. utob: utob,
  277. btou: btou,
  278. decode: decode,
  279. isValid: isValid,
  280. fromUint8Array: fromUint8Array,
  281. toUint8Array: toUint8Array,
  282. extendString: extendString,
  283. extendUint8Array: extendUint8Array,
  284. extendBuiltins: extendBuiltins,
  285. }
  286. // makecjs:CUT //
  287. export { version };
  288. export { VERSION };
  289. export { _atob as atob };
  290. export { atobPolyfill };
  291. export { _btoa as btoa };
  292. export { btoaPolyfill }
  293. export { decode as fromBase64 };
  294. export { encode as toBase64 };
  295. export { utob };
  296. export { encode };
  297. export { encodeURI };
  298. export { encodeURI as encodeURL };
  299. export { btou };
  300. export { decode };
  301. export { isValid };
  302. export { fromUint8Array };
  303. export { toUint8Array };
  304. export { extendString };
  305. export { extendUint8Array };
  306. export { extendBuiltins };
  307. // and finally,
  308. export { gBase64 as Base64 };