1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | "use strict";
|
23 |
|
24 | Object.defineProperty(exports, "__esModule", {
|
25 | value: true
|
26 | });
|
27 | exports.ChunkedStreamManager = exports.ChunkedStream = void 0;
|
28 |
|
29 | var _util = require("../shared/util.js");
|
30 |
|
31 | var _core_utils = require("./core_utils.js");
|
32 |
|
33 | var _stream = require("./stream.js");
|
34 |
|
35 | class ChunkedStream extends _stream.Stream {
|
36 | constructor(length, chunkSize, manager) {
|
37 | super(new Uint8Array(length), 0, length, null);
|
38 | this.chunkSize = chunkSize;
|
39 | this._loadedChunks = new Set();
|
40 | this.numChunks = Math.ceil(length / chunkSize);
|
41 | this.manager = manager;
|
42 | this.progressiveDataLength = 0;
|
43 | this.lastSuccessfulEnsureByteChunk = -1;
|
44 | }
|
45 |
|
46 | getMissingChunks() {
|
47 | const chunks = [];
|
48 |
|
49 | for (let chunk = 0, n = this.numChunks; chunk < n; ++chunk) {
|
50 | if (!this._loadedChunks.has(chunk)) {
|
51 | chunks.push(chunk);
|
52 | }
|
53 | }
|
54 |
|
55 | return chunks;
|
56 | }
|
57 |
|
58 | get numChunksLoaded() {
|
59 | return this._loadedChunks.size;
|
60 | }
|
61 |
|
62 | get isDataLoaded() {
|
63 | return this.numChunksLoaded === this.numChunks;
|
64 | }
|
65 |
|
66 | onReceiveData(begin, chunk) {
|
67 | const chunkSize = this.chunkSize;
|
68 |
|
69 | if (begin % chunkSize !== 0) {
|
70 | throw new Error(`Bad begin offset: ${begin}`);
|
71 | }
|
72 |
|
73 | const end = begin + chunk.byteLength;
|
74 |
|
75 | if (end % chunkSize !== 0 && end !== this.bytes.length) {
|
76 | throw new Error(`Bad end offset: ${end}`);
|
77 | }
|
78 |
|
79 | this.bytes.set(new Uint8Array(chunk), begin);
|
80 | const beginChunk = Math.floor(begin / chunkSize);
|
81 | const endChunk = Math.floor((end - 1) / chunkSize) + 1;
|
82 |
|
83 | for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
|
84 | this._loadedChunks.add(curChunk);
|
85 | }
|
86 | }
|
87 |
|
88 | onReceiveProgressiveData(data) {
|
89 | let position = this.progressiveDataLength;
|
90 | const beginChunk = Math.floor(position / this.chunkSize);
|
91 | this.bytes.set(new Uint8Array(data), position);
|
92 | position += data.byteLength;
|
93 | this.progressiveDataLength = position;
|
94 | const endChunk = position >= this.end ? this.numChunks : Math.floor(position / this.chunkSize);
|
95 |
|
96 | for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
|
97 | this._loadedChunks.add(curChunk);
|
98 | }
|
99 | }
|
100 |
|
101 | ensureByte(pos) {
|
102 | if (pos < this.progressiveDataLength) {
|
103 | return;
|
104 | }
|
105 |
|
106 | const chunk = Math.floor(pos / this.chunkSize);
|
107 |
|
108 | if (chunk > this.numChunks) {
|
109 | return;
|
110 | }
|
111 |
|
112 | if (chunk === this.lastSuccessfulEnsureByteChunk) {
|
113 | return;
|
114 | }
|
115 |
|
116 | if (!this._loadedChunks.has(chunk)) {
|
117 | throw new _core_utils.MissingDataException(pos, pos + 1);
|
118 | }
|
119 |
|
120 | this.lastSuccessfulEnsureByteChunk = chunk;
|
121 | }
|
122 |
|
123 | ensureRange(begin, end) {
|
124 | if (begin >= end) {
|
125 | return;
|
126 | }
|
127 |
|
128 | if (end <= this.progressiveDataLength) {
|
129 | return;
|
130 | }
|
131 |
|
132 | const beginChunk = Math.floor(begin / this.chunkSize);
|
133 |
|
134 | if (beginChunk > this.numChunks) {
|
135 | return;
|
136 | }
|
137 |
|
138 | const endChunk = Math.min(Math.floor((end - 1) / this.chunkSize) + 1, this.numChunks);
|
139 |
|
140 | for (let chunk = beginChunk; chunk < endChunk; ++chunk) {
|
141 | if (!this._loadedChunks.has(chunk)) {
|
142 | throw new _core_utils.MissingDataException(begin, end);
|
143 | }
|
144 | }
|
145 | }
|
146 |
|
147 | nextEmptyChunk(beginChunk) {
|
148 | const numChunks = this.numChunks;
|
149 |
|
150 | for (let i = 0; i < numChunks; ++i) {
|
151 | const chunk = (beginChunk + i) % numChunks;
|
152 |
|
153 | if (!this._loadedChunks.has(chunk)) {
|
154 | return chunk;
|
155 | }
|
156 | }
|
157 |
|
158 | return null;
|
159 | }
|
160 |
|
161 | hasChunk(chunk) {
|
162 | return this._loadedChunks.has(chunk);
|
163 | }
|
164 |
|
165 | getByte() {
|
166 | const pos = this.pos;
|
167 |
|
168 | if (pos >= this.end) {
|
169 | return -1;
|
170 | }
|
171 |
|
172 | if (pos >= this.progressiveDataLength) {
|
173 | this.ensureByte(pos);
|
174 | }
|
175 |
|
176 | return this.bytes[this.pos++];
|
177 | }
|
178 |
|
179 | getBytes(length) {
|
180 | const bytes = this.bytes;
|
181 | const pos = this.pos;
|
182 | const strEnd = this.end;
|
183 |
|
184 | if (!length) {
|
185 | if (strEnd > this.progressiveDataLength) {
|
186 | this.ensureRange(pos, strEnd);
|
187 | }
|
188 |
|
189 | return bytes.subarray(pos, strEnd);
|
190 | }
|
191 |
|
192 | let end = pos + length;
|
193 |
|
194 | if (end > strEnd) {
|
195 | end = strEnd;
|
196 | }
|
197 |
|
198 | if (end > this.progressiveDataLength) {
|
199 | this.ensureRange(pos, end);
|
200 | }
|
201 |
|
202 | this.pos = end;
|
203 | return bytes.subarray(pos, end);
|
204 | }
|
205 |
|
206 | getByteRange(begin, end) {
|
207 | if (begin < 0) {
|
208 | begin = 0;
|
209 | }
|
210 |
|
211 | if (end > this.end) {
|
212 | end = this.end;
|
213 | }
|
214 |
|
215 | if (end > this.progressiveDataLength) {
|
216 | this.ensureRange(begin, end);
|
217 | }
|
218 |
|
219 | return this.bytes.subarray(begin, end);
|
220 | }
|
221 |
|
222 | makeSubStream(start, length, dict = null) {
|
223 | if (length) {
|
224 | if (start + length > this.progressiveDataLength) {
|
225 | this.ensureRange(start, start + length);
|
226 | }
|
227 | } else {
|
228 | if (start >= this.progressiveDataLength) {
|
229 | this.ensureByte(start);
|
230 | }
|
231 | }
|
232 |
|
233 | function ChunkedStreamSubstream() {}
|
234 |
|
235 | ChunkedStreamSubstream.prototype = Object.create(this);
|
236 |
|
237 | ChunkedStreamSubstream.prototype.getMissingChunks = function () {
|
238 | const chunkSize = this.chunkSize;
|
239 | const beginChunk = Math.floor(this.start / chunkSize);
|
240 | const endChunk = Math.floor((this.end - 1) / chunkSize) + 1;
|
241 | const missingChunks = [];
|
242 |
|
243 | for (let chunk = beginChunk; chunk < endChunk; ++chunk) {
|
244 | if (!this._loadedChunks.has(chunk)) {
|
245 | missingChunks.push(chunk);
|
246 | }
|
247 | }
|
248 |
|
249 | return missingChunks;
|
250 | };
|
251 |
|
252 | Object.defineProperty(ChunkedStreamSubstream.prototype, "isDataLoaded", {
|
253 | get() {
|
254 | if (this.numChunksLoaded === this.numChunks) {
|
255 | return true;
|
256 | }
|
257 |
|
258 | return this.getMissingChunks().length === 0;
|
259 | },
|
260 |
|
261 | configurable: true
|
262 | });
|
263 | const subStream = new ChunkedStreamSubstream();
|
264 | subStream.pos = subStream.start = start;
|
265 | subStream.end = start + length || this.end;
|
266 | subStream.dict = dict;
|
267 | return subStream;
|
268 | }
|
269 |
|
270 | getBaseStreams() {
|
271 | return [this];
|
272 | }
|
273 |
|
274 | }
|
275 |
|
276 | exports.ChunkedStream = ChunkedStream;
|
277 |
|
278 | class ChunkedStreamManager {
|
279 | constructor(pdfNetworkStream, args) {
|
280 | this.length = args.length;
|
281 | this.chunkSize = args.rangeChunkSize;
|
282 | this.stream = new ChunkedStream(this.length, this.chunkSize, this);
|
283 | this.pdfNetworkStream = pdfNetworkStream;
|
284 | this.disableAutoFetch = args.disableAutoFetch;
|
285 | this.msgHandler = args.msgHandler;
|
286 | this.currRequestId = 0;
|
287 | this._chunksNeededByRequest = new Map();
|
288 | this._requestsByChunk = new Map();
|
289 | this._promisesByRequest = new Map();
|
290 | this.progressiveDataLength = 0;
|
291 | this.aborted = false;
|
292 | this._loadedStreamCapability = (0, _util.createPromiseCapability)();
|
293 | }
|
294 |
|
295 | onLoadedStream() {
|
296 | return this._loadedStreamCapability.promise;
|
297 | }
|
298 |
|
299 | sendRequest(begin, end) {
|
300 | const rangeReader = this.pdfNetworkStream.getRangeReader(begin, end);
|
301 |
|
302 | if (!rangeReader.isStreamingSupported) {
|
303 | rangeReader.onProgress = this.onProgress.bind(this);
|
304 | }
|
305 |
|
306 | let chunks = [],
|
307 | loaded = 0;
|
308 | return new Promise((resolve, reject) => {
|
309 | const readChunk = chunk => {
|
310 | try {
|
311 | if (!chunk.done) {
|
312 | const data = chunk.value;
|
313 | chunks.push(data);
|
314 | loaded += (0, _util.arrayByteLength)(data);
|
315 |
|
316 | if (rangeReader.isStreamingSupported) {
|
317 | this.onProgress({
|
318 | loaded
|
319 | });
|
320 | }
|
321 |
|
322 | rangeReader.read().then(readChunk, reject);
|
323 | return;
|
324 | }
|
325 |
|
326 | const chunkData = (0, _util.arraysToBytes)(chunks);
|
327 | chunks = null;
|
328 | resolve(chunkData);
|
329 | } catch (e) {
|
330 | reject(e);
|
331 | }
|
332 | };
|
333 |
|
334 | rangeReader.read().then(readChunk, reject);
|
335 | }).then(data => {
|
336 | if (this.aborted) {
|
337 | return;
|
338 | }
|
339 |
|
340 | this.onReceiveData({
|
341 | chunk: data,
|
342 | begin
|
343 | });
|
344 | });
|
345 | }
|
346 |
|
347 | requestAllChunks() {
|
348 | const missingChunks = this.stream.getMissingChunks();
|
349 |
|
350 | this._requestChunks(missingChunks);
|
351 |
|
352 | return this._loadedStreamCapability.promise;
|
353 | }
|
354 |
|
355 | _requestChunks(chunks) {
|
356 | const requestId = this.currRequestId++;
|
357 | const chunksNeeded = new Set();
|
358 |
|
359 | this._chunksNeededByRequest.set(requestId, chunksNeeded);
|
360 |
|
361 | for (const chunk of chunks) {
|
362 | if (!this.stream.hasChunk(chunk)) {
|
363 | chunksNeeded.add(chunk);
|
364 | }
|
365 | }
|
366 |
|
367 | if (chunksNeeded.size === 0) {
|
368 | return Promise.resolve();
|
369 | }
|
370 |
|
371 | const capability = (0, _util.createPromiseCapability)();
|
372 |
|
373 | this._promisesByRequest.set(requestId, capability);
|
374 |
|
375 | const chunksToRequest = [];
|
376 |
|
377 | for (const chunk of chunksNeeded) {
|
378 | let requestIds = this._requestsByChunk.get(chunk);
|
379 |
|
380 | if (!requestIds) {
|
381 | requestIds = [];
|
382 |
|
383 | this._requestsByChunk.set(chunk, requestIds);
|
384 |
|
385 | chunksToRequest.push(chunk);
|
386 | }
|
387 |
|
388 | requestIds.push(requestId);
|
389 | }
|
390 |
|
391 | if (chunksToRequest.length > 0) {
|
392 | const groupedChunksToRequest = this.groupChunks(chunksToRequest);
|
393 |
|
394 | for (const groupedChunk of groupedChunksToRequest) {
|
395 | const begin = groupedChunk.beginChunk * this.chunkSize;
|
396 | const end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length);
|
397 | this.sendRequest(begin, end).catch(capability.reject);
|
398 | }
|
399 | }
|
400 |
|
401 | return capability.promise.catch(reason => {
|
402 | if (this.aborted) {
|
403 | return;
|
404 | }
|
405 |
|
406 | throw reason;
|
407 | });
|
408 | }
|
409 |
|
410 | getStream() {
|
411 | return this.stream;
|
412 | }
|
413 |
|
414 | requestRange(begin, end) {
|
415 | end = Math.min(end, this.length);
|
416 | const beginChunk = this.getBeginChunk(begin);
|
417 | const endChunk = this.getEndChunk(end);
|
418 | const chunks = [];
|
419 |
|
420 | for (let chunk = beginChunk; chunk < endChunk; ++chunk) {
|
421 | chunks.push(chunk);
|
422 | }
|
423 |
|
424 | return this._requestChunks(chunks);
|
425 | }
|
426 |
|
427 | requestRanges(ranges = []) {
|
428 | const chunksToRequest = [];
|
429 |
|
430 | for (const range of ranges) {
|
431 | const beginChunk = this.getBeginChunk(range.begin);
|
432 | const endChunk = this.getEndChunk(range.end);
|
433 |
|
434 | for (let chunk = beginChunk; chunk < endChunk; ++chunk) {
|
435 | if (!chunksToRequest.includes(chunk)) {
|
436 | chunksToRequest.push(chunk);
|
437 | }
|
438 | }
|
439 | }
|
440 |
|
441 | chunksToRequest.sort(function (a, b) {
|
442 | return a - b;
|
443 | });
|
444 | return this._requestChunks(chunksToRequest);
|
445 | }
|
446 |
|
447 | groupChunks(chunks) {
|
448 | const groupedChunks = [];
|
449 | let beginChunk = -1;
|
450 | let prevChunk = -1;
|
451 |
|
452 | for (let i = 0, ii = chunks.length; i < ii; ++i) {
|
453 | const chunk = chunks[i];
|
454 |
|
455 | if (beginChunk < 0) {
|
456 | beginChunk = chunk;
|
457 | }
|
458 |
|
459 | if (prevChunk >= 0 && prevChunk + 1 !== chunk) {
|
460 | groupedChunks.push({
|
461 | beginChunk,
|
462 | endChunk: prevChunk + 1
|
463 | });
|
464 | beginChunk = chunk;
|
465 | }
|
466 |
|
467 | if (i + 1 === chunks.length) {
|
468 | groupedChunks.push({
|
469 | beginChunk,
|
470 | endChunk: chunk + 1
|
471 | });
|
472 | }
|
473 |
|
474 | prevChunk = chunk;
|
475 | }
|
476 |
|
477 | return groupedChunks;
|
478 | }
|
479 |
|
480 | onProgress(args) {
|
481 | this.msgHandler.send("DocProgress", {
|
482 | loaded: this.stream.numChunksLoaded * this.chunkSize + args.loaded,
|
483 | total: this.length
|
484 | });
|
485 | }
|
486 |
|
487 | onReceiveData(args) {
|
488 | const chunk = args.chunk;
|
489 | const isProgressive = args.begin === undefined;
|
490 | const begin = isProgressive ? this.progressiveDataLength : args.begin;
|
491 | const end = begin + chunk.byteLength;
|
492 | const beginChunk = Math.floor(begin / this.chunkSize);
|
493 | const endChunk = end < this.length ? Math.floor(end / this.chunkSize) : Math.ceil(end / this.chunkSize);
|
494 |
|
495 | if (isProgressive) {
|
496 | this.stream.onReceiveProgressiveData(chunk);
|
497 | this.progressiveDataLength = end;
|
498 | } else {
|
499 | this.stream.onReceiveData(begin, chunk);
|
500 | }
|
501 |
|
502 | if (this.stream.isDataLoaded) {
|
503 | this._loadedStreamCapability.resolve(this.stream);
|
504 | }
|
505 |
|
506 | const loadedRequests = [];
|
507 |
|
508 | for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
|
509 | const requestIds = this._requestsByChunk.get(curChunk);
|
510 |
|
511 | if (!requestIds) {
|
512 | continue;
|
513 | }
|
514 |
|
515 | this._requestsByChunk.delete(curChunk);
|
516 |
|
517 | for (const requestId of requestIds) {
|
518 | const chunksNeeded = this._chunksNeededByRequest.get(requestId);
|
519 |
|
520 | if (chunksNeeded.has(curChunk)) {
|
521 | chunksNeeded.delete(curChunk);
|
522 | }
|
523 |
|
524 | if (chunksNeeded.size > 0) {
|
525 | continue;
|
526 | }
|
527 |
|
528 | loadedRequests.push(requestId);
|
529 | }
|
530 | }
|
531 |
|
532 | if (!this.disableAutoFetch && this._requestsByChunk.size === 0) {
|
533 | let nextEmptyChunk;
|
534 |
|
535 | if (this.stream.numChunksLoaded === 1) {
|
536 | const lastChunk = this.stream.numChunks - 1;
|
537 |
|
538 | if (!this.stream.hasChunk(lastChunk)) {
|
539 | nextEmptyChunk = lastChunk;
|
540 | }
|
541 | } else {
|
542 | nextEmptyChunk = this.stream.nextEmptyChunk(endChunk);
|
543 | }
|
544 |
|
545 | if (Number.isInteger(nextEmptyChunk)) {
|
546 | this._requestChunks([nextEmptyChunk]);
|
547 | }
|
548 | }
|
549 |
|
550 | for (const requestId of loadedRequests) {
|
551 | const capability = this._promisesByRequest.get(requestId);
|
552 |
|
553 | this._promisesByRequest.delete(requestId);
|
554 |
|
555 | capability.resolve();
|
556 | }
|
557 |
|
558 | this.msgHandler.send("DocProgress", {
|
559 | loaded: this.stream.numChunksLoaded * this.chunkSize,
|
560 | total: this.length
|
561 | });
|
562 | }
|
563 |
|
564 | onError(err) {
|
565 | this._loadedStreamCapability.reject(err);
|
566 | }
|
567 |
|
568 | getBeginChunk(begin) {
|
569 | return Math.floor(begin / this.chunkSize);
|
570 | }
|
571 |
|
572 | getEndChunk(end) {
|
573 | return Math.floor((end - 1) / this.chunkSize) + 1;
|
574 | }
|
575 |
|
576 | abort(reason) {
|
577 | this.aborted = true;
|
578 |
|
579 | if (this.pdfNetworkStream) {
|
580 | this.pdfNetworkStream.cancelAllRequests(reason);
|
581 | }
|
582 |
|
583 | for (const capability of this._promisesByRequest.values()) {
|
584 | capability.reject(reason);
|
585 | }
|
586 | }
|
587 |
|
588 | }
|
589 |
|
590 | exports.ChunkedStreamManager = ChunkedStreamManager; |
\ | No newline at end of file |