Merge pull request #16040 from Snuffleupagus/arrayBuffersToBytes

Re-factor the `arraysToBytes` helper function (PR 16032 follow-up)
This commit is contained in:
Tim van der Meij 2023-02-12 11:47:57 +01:00 committed by GitHub
commit 22618213c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 66 deletions

View File

@ -13,12 +13,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { arrayBuffersToBytes, MissingDataException } from "./core_utils.js";
arraysToBytes, import { assert, createPromiseCapability } from "../shared/util.js";
assert,
createPromiseCapability,
} from "../shared/util.js";
import { MissingDataException } from "./core_utils.js";
import { Stream } from "./stream.js"; import { Stream } from "./stream.js";
class ChunkedStream extends Stream { class ChunkedStream extends Stream {
@ -294,7 +290,7 @@ class ChunkedStreamManager {
const readChunk = ({ value, done }) => { const readChunk = ({ value, done }) => {
try { try {
if (done) { if (done) {
const chunkData = arraysToBytes(chunks); const chunkData = arrayBuffersToBytes(chunks);
chunks = null; chunks = null;
resolve(chunkData); resolve(chunkData);
return; return;

View File

@ -80,6 +80,44 @@ class XRefParseException extends BaseException {
} }
} }
/**
* Combines multiple ArrayBuffers into a single Uint8Array.
* @param {Array<ArrayBuffer>} arr - An array of ArrayBuffers.
* @returns {Uint8Array}
*/
function arrayBuffersToBytes(arr) {
if (
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || TESTING")
) {
for (const item of arr) {
assert(
item instanceof ArrayBuffer,
"arrayBuffersToBytes - expected an ArrayBuffer."
);
}
}
const length = arr.length;
if (length === 0) {
return new Uint8Array(0);
}
if (length === 1) {
return new Uint8Array(arr[0]);
}
let dataLength = 0;
for (let i = 0; i < length; i++) {
dataLength += arr[i].byteLength;
}
const data = new Uint8Array(dataLength);
let pos = 0;
for (let i = 0; i < length; i++) {
const item = new Uint8Array(arr[i]);
data.set(item, pos);
pos += item.byteLength;
}
return data;
}
/** /**
* Get the value of an inheritable property. * Get the value of an inheritable property.
* *
@ -579,6 +617,7 @@ function getRotationMatrix(rotation, width, height) {
} }
export { export {
arrayBuffersToBytes,
collectActions, collectActions,
encodeToXmlString, encodeToXmlString,
escapePDFName, escapePDFName,

View File

@ -15,7 +15,6 @@
import { import {
AbortException, AbortException,
arraysToBytes,
assert, assert,
createPromiseCapability, createPromiseCapability,
getVerbosityLevel, getVerbosityLevel,
@ -30,8 +29,12 @@ import {
VerbosityLevel, VerbosityLevel,
warn, warn,
} from "../shared/util.js"; } from "../shared/util.js";
import {
arrayBuffersToBytes,
getNewAnnotationsMap,
XRefParseException,
} from "./core_utils.js";
import { Dict, Ref } from "./primitives.js"; import { Dict, Ref } from "./primitives.js";
import { getNewAnnotationsMap, XRefParseException } from "./core_utils.js";
import { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js"; import { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js";
import { clearGlobalCaches } from "./cleanup_helper.js"; import { clearGlobalCaches } from "./cleanup_helper.js";
import { incrementalUpdate } from "./writer.js"; import { incrementalUpdate } from "./writer.js";
@ -93,7 +96,7 @@ class WorkerMessageHandler {
let pdfManager; let pdfManager;
let terminated = false; let terminated = false;
let cancelXHRs = null; let cancelXHRs = null;
const WorkerTasks = []; const WorkerTasks = new Set();
const verbosity = getVerbosityLevel(); const verbosity = getVerbosityLevel();
const { docId, apiVersion } = docParams; const { docId, apiVersion } = docParams;
@ -151,13 +154,12 @@ class WorkerMessageHandler {
} }
function startWorkerTask(task) { function startWorkerTask(task) {
WorkerTasks.push(task); WorkerTasks.add(task);
} }
function finishWorkerTask(task) { function finishWorkerTask(task) {
task.finish(); task.finish();
const i = WorkerTasks.indexOf(task); WorkerTasks.delete(task);
WorkerTasks.splice(i, 1);
} }
async function loadDocument(recoveryMode) { async function loadDocument(recoveryMode) {
@ -277,7 +279,7 @@ class WorkerMessageHandler {
let loaded = 0; let loaded = 0;
const flushChunks = function () { const flushChunks = function () {
const pdfFile = arraysToBytes(cachedChunks); const pdfFile = arrayBuffersToBytes(cachedChunks);
if (length && pdfFile.length !== length) { if (length && pdfFile.length !== length) {
warn("reported HTTP length is different from actual"); warn("reported HTTP length is different from actual");
} }

View File

@ -597,56 +597,6 @@ function stringToBytes(str) {
return bytes; return bytes;
} }
/**
* Gets length of the array (Array, Uint8Array, or string) in bytes.
* @param {Array<any>|Uint8Array|string} arr
* @returns {number}
*/
// eslint-disable-next-line consistent-return
function arrayByteLength(arr) {
if (arr.length !== undefined) {
return arr.length;
}
if (arr.byteLength !== undefined) {
return arr.byteLength;
}
unreachable("Invalid argument for arrayByteLength");
}
/**
* Combines array items (arrays) into single Uint8Array object.
* @param {Array<Array<any>|Uint8Array|string>} arr - the array of the arrays
* (Array, Uint8Array, or string).
* @returns {Uint8Array}
*/
function arraysToBytes(arr) {
const length = arr.length;
// Shortcut: if first and only item is Uint8Array, return it.
if (length === 1 && arr[0] instanceof Uint8Array) {
return arr[0];
}
let resultLength = 0;
for (let i = 0; i < length; i++) {
resultLength += arrayByteLength(arr[i]);
}
let pos = 0;
const data = new Uint8Array(resultLength);
for (let i = 0; i < length; i++) {
let item = arr[i];
if (!(item instanceof Uint8Array)) {
if (typeof item === "string") {
item = stringToBytes(item);
} else {
item = new Uint8Array(item);
}
}
const itemLength = item.byteLength;
data.set(item, pos);
pos += itemLength;
}
return data;
}
function string32(value) { function string32(value) {
if ( if (
typeof PDFJSDev === "undefined" || typeof PDFJSDev === "undefined" ||
@ -1115,7 +1065,6 @@ export {
AnnotationReviewState, AnnotationReviewState,
AnnotationStateModelType, AnnotationStateModelType,
AnnotationType, AnnotationType,
arraysToBytes,
assert, assert,
BaseException, BaseException,
BASELINE_FACTOR, BASELINE_FACTOR,

View File

@ -13,8 +13,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { Dict, Ref } from "../../src/core/primitives.js";
import { import {
arrayBuffersToBytes,
encodeToXmlString, encodeToXmlString,
escapePDFName, escapePDFName,
escapeString, escapeString,
@ -30,9 +30,36 @@ import {
toRomanNumerals, toRomanNumerals,
validateCSSFont, validateCSSFont,
} from "../../src/core/core_utils.js"; } from "../../src/core/core_utils.js";
import { Dict, Ref } from "../../src/core/primitives.js";
import { XRefMock } from "./test_utils.js"; import { XRefMock } from "./test_utils.js";
describe("core_utils", function () { describe("core_utils", function () {
describe("arrayBuffersToBytes", function () {
it("handles zero ArrayBuffers", function () {
const bytes = arrayBuffersToBytes([]);
expect(bytes).toEqual(new Uint8Array(0));
});
it("handles one ArrayBuffer", function () {
const buffer = new Uint8Array([1, 2, 3]).buffer;
const bytes = arrayBuffersToBytes([buffer]);
expect(bytes).toEqual(new Uint8Array([1, 2, 3]));
// Ensure that the fast-path works correctly.
expect(bytes.buffer).toBe(buffer);
});
it("handles multiple ArrayBuffers", function () {
const buffer1 = new Uint8Array([1, 2, 3]).buffer,
buffer2 = new Uint8Array(0).buffer,
buffer3 = new Uint8Array([4, 5]).buffer;
const bytes = arrayBuffersToBytes([buffer1, buffer2, buffer3]);
expect(bytes).toEqual(new Uint8Array([1, 2, 3, 4, 5]));
});
});
describe("getInheritableProperty", function () { describe("getInheritableProperty", function () {
it("handles non-dictionary arguments", function () { it("handles non-dictionary arguments", function () {
expect(getInheritableProperty({ dict: null, key: "foo" })).toEqual( expect(getInheritableProperty({ dict: null, key: "foo" })).toEqual(