72 lines
3.2 KiB
JavaScript
72 lines
3.2 KiB
JavaScript
// PDF.js 6 uses Map upsert APIs absent from Chrome 143 / older WebViews.
|
|
// Run in both the document and its separate worker realm. Never replace natives.
|
|
(() => {
|
|
'use strict';
|
|
const { has, get, set } = Map.prototype;
|
|
// Accumulate finite doubles exactly in units of the smallest subnormal.
|
|
// Round only once, avoiding both cancellation loss and intermediate overflow.
|
|
if (typeof Math.sumPrecise !== 'function') {
|
|
Object.defineProperty(Math, 'sumPrecise', {
|
|
configurable: true, writable: true,
|
|
value: function sumPrecise(values) {
|
|
const bits = new DataView(new ArrayBuffer(8));
|
|
let total = 0n, negativeZero = true, positiveInfinity = false, negativeInfinity = false, nan = false;
|
|
for (const value of values) {
|
|
if (typeof value !== 'number') throw new TypeError('sumPrecise requires numbers');
|
|
if (!Object.is(value, -0)) negativeZero = false;
|
|
if (!Number.isFinite(value)) {
|
|
if (Number.isNaN(value)) nan = true;
|
|
else if (value > 0) positiveInfinity = true;
|
|
else negativeInfinity = true;
|
|
continue;
|
|
}
|
|
bits.setFloat64(0, value);
|
|
const high = bits.getUint32(0), low = bits.getUint32(4);
|
|
const exponent = (high >>> 20) & 0x7ff;
|
|
let mantissa = (BigInt(high & 0xfffff) << 32n) | BigInt(low);
|
|
if (exponent) mantissa = (mantissa | (1n << 52n)) << BigInt(exponent - 1);
|
|
total += high >>> 31 ? -mantissa : mantissa;
|
|
}
|
|
if (nan || (positiveInfinity && negativeInfinity)) return NaN;
|
|
if (positiveInfinity) return Infinity;
|
|
if (negativeInfinity) return -Infinity;
|
|
if (total === 0n) return negativeZero ? -0 : 0;
|
|
const sign = total < 0n ? -1 : 1;
|
|
if (total < 0n) total = -total;
|
|
const shift = Math.max(0, total.toString(2).length - 53);
|
|
let rounded = total >> BigInt(shift);
|
|
if (shift) {
|
|
const remainder = total - (rounded << BigInt(shift));
|
|
const half = 1n << BigInt(shift - 1);
|
|
if (remainder > half || (remainder === half && (rounded & 1n))) rounded++;
|
|
}
|
|
return sign * Number(rounded) * 2 ** (shift - 1074);
|
|
},
|
|
});
|
|
}
|
|
if (typeof Map.prototype.getOrInsert !== 'function') {
|
|
Object.defineProperty(Map.prototype, 'getOrInsert', {
|
|
configurable: true, writable: true,
|
|
value: function getOrInsert(key, value) {
|
|
if (has.call(this, key)) return get.call(this, key);
|
|
set.call(this, key, value);
|
|
return value;
|
|
},
|
|
});
|
|
}
|
|
if (typeof Map.prototype.getOrInsertComputed !== 'function') {
|
|
Object.defineProperty(Map.prototype, 'getOrInsertComputed', {
|
|
configurable: true, writable: true,
|
|
value: function getOrInsertComputed(key, callback) {
|
|
const present = has.call(this, key); // Also validates the Map receiver.
|
|
if (typeof callback !== 'function') throw new TypeError('callback must be callable');
|
|
if (present) return get.call(this, key);
|
|
if (key === 0) key = 0; // Canonicalize -0 before calling the callback.
|
|
const value = callback(key);
|
|
set.call(this, key, value); // Callback may have inserted the same key.
|
|
return value;
|
|
},
|
|
});
|
|
}
|
|
})();
|