perf(fromArrayBuffer): use less memory for large buffers (#242)

diff --git a/src/common/base64.js b/src/common/base64.js
index 5c0b3f4..06c39ff 100644
--- a/src/common/base64.js
+++ b/src/common/base64.js
@@ -23,7 +23,7 @@
 
 base64.fromArrayBuffer = function (arrayBuffer) {
     var array = new Uint8Array(arrayBuffer);
-    return uint8ToBase64(array);
+    return btoa(bytesToBinaryString(array));
 };
 
 base64.toArrayBuffer = function (str) {
@@ -36,46 +36,12 @@
     return arrayBuffer;
 };
 
-// ------------------------------------------------------------------------------
-
-/* This code is based on the performance tests at http://jsperf.com/b64tests
- * This 12-bit-at-a-time algorithm was the best performing version on all
- * platforms tested.
- */
-
-var b64_6bit = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
-var b64_12bit;
-
-var b64_12bitTable = function () {
-    b64_12bit = [];
-    for (var i = 0; i < 64; i++) {
-        for (var j = 0; j < 64; j++) {
-            b64_12bit[i * 64 + j] = b64_6bit[i] + b64_6bit[j];
-        }
+function bytesToBinaryString (bytes) {
+    var CHUNK_SIZE = 1 << 15;
+    var string = '';
+    for (var i = 0; i < bytes.length; i += CHUNK_SIZE) {
+        var chunk = bytes.subarray(i, i + CHUNK_SIZE);
+        string += String.fromCharCode.apply(null, chunk);
     }
-    b64_12bitTable = function () { return b64_12bit; };
-    return b64_12bit;
-};
-
-function uint8ToBase64 (rawData) {
-    var numBytes = rawData.byteLength;
-    var output = '';
-    var segment;
-    var table = b64_12bitTable();
-    for (var i = 0; i < numBytes - 2; i += 3) {
-        segment = (rawData[i] << 16) + (rawData[i + 1] << 8) + rawData[i + 2];
-        output += table[segment >> 12];
-        output += table[segment & 0xfff];
-    }
-    if (numBytes - i === 2) {
-        segment = (rawData[i] << 16) + (rawData[i + 1] << 8);
-        output += table[segment >> 12];
-        output += b64_6bit[(segment & 0xfff) >> 6];
-        output += '=';
-    } else if (numBytes - i === 1) {
-        segment = (rawData[i] << 16);
-        output += table[segment >> 12];
-        output += '==';
-    }
-    return output;
+    return string;
 }
diff --git a/test/test.base64.js b/test/test.base64.js
index a326a5e..1f7e726 100644
--- a/test/test.base64.js
+++ b/test/test.base64.js
@@ -53,6 +53,14 @@
         );
     });
 
+    it('can base64 encode big files reasonably fast', function () {
+        var bytes = Uint8Array.from({ length: 1 << 24 }, (v, i) => i & 0xff);
+
+        var start = Date.now();
+        base64.fromArrayBuffer(bytes.buffer);
+        expect(Date.now() - start).toBeLessThan(1000);
+    });
+
     it('Test#003 : can base64 encode an text string in an ArrayBuffer', function () {
         var orig = 'Some Awesome Test This Is!';
         var base64string = btoa(orig);