Merge pull request #14245 from Snuffleupagus/PDFPageViewBuffer-class

Convert `PDFPageViewBuffer` to a standard class, and use a `Set` internally
This commit is contained in:
Tim van der Meij 2021-11-07 14:37:33 +01:00 committed by GitHub
commit 891f21fba6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 100 deletions

View File

@ -136,12 +136,12 @@ describe("BaseViewer", function () {
// Ensure that decreasing the size will evict the correct views, // Ensure that decreasing the size will evict the correct views,
// while re-ordering the remaining ones correctly. // while re-ordering the remaining ones correctly.
buffer.resize(3, new Set([1, 2, 3])); buffer.resize(3, new Set([1, 2, 5]));
expect(buffer._buffer).toEqual([ expect(buffer._buffer).toEqual([
viewsMap.get(1), viewsMap.get(1),
viewsMap.get(2), viewsMap.get(2),
viewsMap.get(3), viewsMap.get(5),
]); ]);
}); });

View File

@ -21,7 +21,6 @@ import {
getVisibleElements, getVisibleElements,
isPortraitOrientation, isPortraitOrientation,
isValidRotation, isValidRotation,
moveToEndOfArray,
parseQueryString, parseQueryString,
waitOnEventOrTimeout, waitOnEventOrTimeout,
WaitOnType, WaitOnType,
@ -864,44 +863,4 @@ describe("ui_utils", function () {
}); });
}); });
}); });
describe("moveToEndOfArray", function () {
it("works on empty arrays", function () {
const data = [];
moveToEndOfArray(data, function () {});
expect(data).toEqual([]);
});
it("works when moving everything", function () {
const data = [1, 2, 3, 4, 5];
moveToEndOfArray(data, function () {
return true;
});
expect(data).toEqual([1, 2, 3, 4, 5]);
});
it("works when moving some things", function () {
const data = [1, 2, 3, 4, 5];
moveToEndOfArray(data, function (x) {
return x % 2 === 0;
});
expect(data).toEqual([1, 3, 5, 2, 4]);
});
it("works when moving one thing", function () {
const data = [1, 2, 3, 4, 5];
moveToEndOfArray(data, function (x) {
return x === 1;
});
expect(data).toEqual([2, 3, 4, 5, 1]);
});
it("works when moving nothing", function () {
const data = [1, 2, 3, 4, 5];
moveToEndOfArray(data, function (x) {
return x === 0;
});
expect(data).toEqual([1, 2, 3, 4, 5]);
});
});
}); });

View File

@ -31,7 +31,6 @@ import {
MAX_AUTO_SCALE, MAX_AUTO_SCALE,
MAX_SCALE, MAX_SCALE,
MIN_SCALE, MIN_SCALE,
moveToEndOfArray,
PresentationModeState, PresentationModeState,
RendererType, RendererType,
SCROLLBAR_PADDING, SCROLLBAR_PADDING,
@ -92,18 +91,38 @@ const DEFAULT_CACHE_SIZE = 10;
* @property {IL10n} l10n - Localization service. * @property {IL10n} l10n - Localization service.
*/ */
function PDFPageViewBuffer(size) { class PDFPageViewBuffer {
const data = []; // Here we rely on the fact that `Set`s preserve the insertion order.
this.push = function (view) { #buf = new Set();
const i = data.indexOf(view);
if (i >= 0) { #size = 0;
data.splice(i, 1);
constructor(size) {
this.#size = size;
if (
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || TESTING")
) {
Object.defineProperty(this, "_buffer", {
get() {
return [...this.#buf];
},
});
} }
data.push(view); }
if (data.length > size) {
data.shift().destroy(); push(view) {
const buf = this.#buf;
if (buf.has(view)) {
buf.delete(view); // Move the view to the "end" of the buffer.
} }
}; buf.add(view);
if (buf.size > this.#size) {
this.#destroyFirstView();
}
}
/** /**
* After calling resize, the size of the buffer will be `newSize`. * After calling resize, the size of the buffer will be `newSize`.
@ -112,31 +131,38 @@ function PDFPageViewBuffer(size) {
* `idsToKeep` has no impact on the final size of the buffer; if `idsToKeep` * `idsToKeep` has no impact on the final size of the buffer; if `idsToKeep`
* is larger than `newSize`, some of those pages will be destroyed anyway. * is larger than `newSize`, some of those pages will be destroyed anyway.
*/ */
this.resize = function (newSize, idsToKeep = null) { resize(newSize, idsToKeep = null) {
size = newSize; this.#size = newSize;
const buf = this.#buf;
if (idsToKeep) { if (idsToKeep) {
moveToEndOfArray(data, function (page) { const ii = buf.size;
return idsToKeep.has(page.id); let i = 1;
}); for (const view of buf) {
if (idsToKeep.has(view.id)) {
buf.delete(view); // Move the view to the "end" of the buffer.
buf.add(view);
}
if (++i > ii) {
break;
}
}
} }
while (data.length > size) {
data.shift().destroy(); while (buf.size > this.#size) {
this.#destroyFirstView();
} }
}; }
this.has = function (view) { has(view) {
return data.includes(view); return this.#buf.has(view);
}; }
if ( #destroyFirstView() {
typeof PDFJSDev === "undefined" || const firstView = this.#buf.keys().next().value;
PDFJSDev.test("!PRODUCTION || TESTING")
) { firstView?.destroy();
Object.defineProperty(this, "_buffer", { this.#buf.delete(firstView);
get() {
return data.slice();
},
});
} }
} }
@ -156,6 +182,8 @@ function isSameScale(oldScale, newScale) {
* Simple viewer control to display PDF content/pages. * Simple viewer control to display PDF content/pages.
*/ */
class BaseViewer { class BaseViewer {
#buffer = null;
#scrollModePageState = null; #scrollModePageState = null;
/** /**
@ -518,7 +546,7 @@ class BaseViewer {
} }
// Add the page to the buffer at the start of drawing. That way it can be // Add the page to the buffer at the start of drawing. That way it can be
// evicted from the buffer and destroyed even if we pause its rendering. // evicted from the buffer and destroyed even if we pause its rendering.
this._buffer.push(pageView); this.#buffer.push(pageView);
}; };
this.eventBus._on("pagerender", this._onBeforeDraw); this.eventBus._on("pagerender", this._onBeforeDraw);
@ -684,7 +712,7 @@ class BaseViewer {
this._currentScale = UNKNOWN_SCALE; this._currentScale = UNKNOWN_SCALE;
this._currentScaleValue = null; this._currentScaleValue = null;
this._pageLabels = null; this._pageLabels = null;
this._buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE); this.#buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE);
this._location = null; this._location = null;
this._pagesRotation = 0; this._pagesRotation = 0;
this._optionalContentConfigPromise = null; this._optionalContentConfigPromise = null;
@ -1153,7 +1181,7 @@ class BaseViewer {
return; return;
} }
const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1); const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1);
this._buffer.resize(newCacheSize, visible.ids); this.#buffer.resize(newCacheSize, visible.ids);
this.renderingQueue.renderHighestPriority(visible); this.renderingQueue.renderHighestPriority(visible);
@ -1304,7 +1332,7 @@ class BaseViewer {
return false; return false;
} }
const pageView = this._pages[pageNumber - 1]; const pageView = this._pages[pageNumber - 1];
return this._buffer.has(pageView); return this.#buffer.has(pageView);
} }
cleanup() { cleanup() {

View File

@ -906,27 +906,6 @@ class ProgressBar {
} }
} }
/**
* Moves all elements of an array that satisfy condition to the end of the
* array, preserving the order of the rest.
*/
function moveToEndOfArray(arr, condition) {
const moved = [],
len = arr.length;
let write = 0;
for (let read = 0; read < len; ++read) {
if (condition(arr[read])) {
moved.push(arr[read]);
} else {
arr[write] = arr[read];
++write;
}
}
for (let read = 0; write < len; ++read, ++write) {
arr[write] = moved[read];
}
}
/** /**
* Get the active or focused element in current DOM. * Get the active or focused element in current DOM.
* *
@ -1031,7 +1010,6 @@ export {
MAX_AUTO_SCALE, MAX_AUTO_SCALE,
MAX_SCALE, MAX_SCALE,
MIN_SCALE, MIN_SCALE,
moveToEndOfArray,
noContextMenuHandler, noContextMenuHandler,
normalizeWheelEventDelta, normalizeWheelEventDelta,
normalizeWheelEventDirection, normalizeWheelEventDirection,