Page MenuHomePhabricator

D7223.diff
No OneTemporary

D7223.diff

diff --git a/lib/media/data-utils.js b/lib/media/data-utils.js
new file mode 100644
--- /dev/null
+++ b/lib/media/data-utils.js
@@ -0,0 +1,44 @@
+// @flow
+
+import { fileInfoFromData } from './file-utils.js';
+import { base64FromIntArray } from '../utils/third-party/base64.js';
+
+/**
+ * Returns a hex string representation of the given Uint8Array.
+ */
+function uintArrayToHexString(data: Uint8Array): string {
+ return Array.from(data)
+ .map(byte => byte.toString(16).padStart(2, '0'))
+ .join('');
+}
+
+/**
+ * Returns a Uint8Array representation of the given hex string.
+ */
+function hexToUintArray(hex: string): Uint8Array {
+ const result = new Uint8Array(hex.length / 2);
+ for (let i = 0; i < hex.length; i += 2) {
+ result[i / 2] = parseInt(hex.substr(i, 2), 16);
+ }
+ return result;
+}
+
+/**
+ * Returns a data URL representation of the given Uint8Array.
+ * Returns `null` if MIME type cannot be determined by reading the data header.
+ */
+function uintArrayToDataURL(bytes: Uint8Array): ?string {
+ const base64 = base64FromIntArray(bytes);
+ const { mime } = fileInfoFromData(bytes);
+ if (!mime) {
+ return null;
+ }
+ return `data:${mime};base64,${base64}`;
+}
+
+export {
+ uintArrayToHexString,
+ hexToUintArray,
+ base64FromIntArray,
+ uintArrayToDataURL,
+};
diff --git a/lib/media/data-utils.test.js b/lib/media/data-utils.test.js
new file mode 100644
--- /dev/null
+++ b/lib/media/data-utils.test.js
@@ -0,0 +1,42 @@
+// @flow
+import {
+ uintArrayToHexString,
+ hexToUintArray,
+ uintArrayToDataURL,
+} from './data-utils.js';
+
+describe('uintArrayToHexString', () => {
+ it('converts Uint8Array to hex string', () => {
+ const data = new Uint8Array([0x00, 0x01, 0x02, 0x1a, 0x2b, 0xff]);
+ expect(uintArrayToHexString(data)).toStrictEqual('0001021a2bff');
+ });
+});
+
+describe('hexToUintArray', () => {
+ it('converts hex string to Uint8Array', () => {
+ const hex = '0001021a2bff';
+ expect(hexToUintArray(hex)).toStrictEqual(
+ new Uint8Array([0x00, 0x01, 0x02, 0x1a, 0x2b, 0xff]),
+ );
+ });
+});
+
+describe('uintArrayToDataURL', () => {
+ it('converts JPEG Uint8Array to data URL', () => {
+ const jpegMagicBytes = new Uint8Array([0xff, 0xd8, 0xff, 0xe0]);
+ const jpegData = new Uint8Array(jpegMagicBytes.length + 10);
+ jpegData.set(jpegMagicBytes);
+ jpegData.set(
+ new Uint8Array([0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01]),
+ jpegMagicBytes.length,
+ );
+
+ const jpegBase64 = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAAA=';
+ expect(uintArrayToDataURL(jpegData)).toStrictEqual(jpegBase64);
+ });
+
+ it('returns null if MIME type cannot be determined', () => {
+ const data = new Uint8Array([0x00, 0x01, 0x02, 0x1a, 0x2b, 0xff]);
+ expect(uintArrayToDataURL(data)).toBeNull();
+ });
+});
diff --git a/lib/utils/third-party/base64.js b/lib/utils/third-party/base64.js
new file mode 100644
--- /dev/null
+++ b/lib/utils/third-party/base64.js
@@ -0,0 +1,68 @@
+/*
+MIT LICENSE
+Copyright 2011 Jon Leighton
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions: The above copyright notice and this
+permission notice shall be included in all copies or substantial portions of the
+Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/**
+ * Converts a Uint8Array to a base64 string. This is a temporary workaround
+ * until we can use implement native method to directly save Uint8Array to file.
+ *
+ * It is ~4-6x faster than using
+ * ```js
+ * let base64 = await blobToDataURI(new Blob([bytes]));
+ * base64 = base64.substring(base64.indexOf(',') + 1);
+ * ```
+ *
+ * This function is a slightly modified version of
+ * https://gist.github.com/jonleighton/958841
+ */
+// @flow
+function base64FromIntArray(bytes: Uint8Array): string {
+ let base64 = '';
+ const alphabet =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+ const remainder = bytes.length % 3;
+ const mainLength = bytes.length - remainder;
+
+ let a, b, c, d;
+ let chunk;
+ for (let i = 0; i < mainLength; i += 3) {
+ chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
+ a = (chunk & 16515072) >> 18;
+ b = (chunk & 258048) >> 12;
+ c = (chunk & 4032) >> 6;
+ d = chunk & 63;
+ base64 += alphabet[a] + alphabet[b] + alphabet[c] + alphabet[d];
+ }
+
+ if (remainder === 1) {
+ chunk = bytes[mainLength];
+ a = (chunk & 252) >> 2;
+ b = (chunk & 3) << 4;
+ base64 += alphabet[a] + alphabet[b] + '==';
+ } else if (remainder === 2) {
+ chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
+ a = (chunk & 64512) >> 10;
+ b = (chunk & 1008) >> 4;
+ c = (chunk & 15) << 2;
+ base64 += alphabet[a] + alphabet[b] + alphabet[c] + '=';
+ }
+ return base64;
+}
+
+export { base64FromIntArray };

File Metadata

Mime Type
text/plain
Expires
Mon, Dec 23, 9:08 AM (18 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2694256
Default Alt Text
D7223.diff (5 KB)

Event Timeline