dastal
Version:
Data Structures & Algorithms implementations
157 lines • 5.38 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMaxArrayLength = exports.getMaxArgumentsLength = exports.search = void 0;
const numberUtils_1 = require("src/math/numberUtils");
/**
* Find the intended value given a comparison function. If not found,
* finds the closest intended value.
*
* @param min - The minimum value
* @param max - The maximum value
* @param compareFn - The comparison function
*/
function search(min, max, compareFn) {
while (min < max) {
const mid = min + Math.ceil((max - min) / 2);
const cmp = compareFn(mid);
if (cmp < 0) {
max = mid - 1;
}
else if (cmp > 0) {
min = mid;
}
else {
return mid;
}
}
return min;
}
exports.search = search;
/**
* Get the maximum number of arguments that
* can be passed to a simple function.
*
* According to the [ECMA-262](https://tc39.es/ecma262/#sec-list-and-record-specification-type),
* there is no maximum. In practice, different environments impose their own
* limit. The limit closely depends on available stack memory.
*
* @param accuracy - A number from 0 - 1 to determine
* if calculation prioritizes speed vs accuracy, with 1
* being most accurate and 0 being speediest. Be default,
* speed is preferred.
*
* **Note 1**: The output depends on the available
* stack memory at the time this function is called.
* For that reason, any output should be used as soon
* as possible and in its original context.
*
* **Note 2**: When `accuracy = 1`, the output has
* an error margin of +- 0.85% from the actual maximum.
* When `accuracy = 0`, the output has an error margin of -25%.
*
* **Note 3**: Even with a simple function, it may
* still cause a stack overflow if `accuracy` is high.
* The margin of error should be taken into account
* before usage, such as multiplying the output
* by <= 1 - error_margin (for example: `0.99*output`).
*
* **Note 4**: The actual maximum depends on the
* amount of stack memory to be used by the intended function.
* For that reason, the actual maximum may be lower
* than this function's output. The output should only be
* used as an upper bound / starting point for
* an actual maximum.
*
* - The simplest way to do this
* is to decrease the multiple mentioned in note 3 (such as
* to 80%). This should work for most usage, as most functions
* are relatively flat (little to no nested function calls)
* and/or use a reasonable amount of variables.
*
* - For more accuracy, the intended function can be
* profiled to come up with a more exact estimate.
*/
function getMaxArgumentsLength(accuracy = 0) {
/**
* Let:
* - T = total stack memory
* - U = used stack memory
* - F = size of a function call in the stack
* - V = size of a variable in the stack
*
* Then:
* T ~= aF + bV + U
*
* If we set the number of variables `b`, we can find `a`
* by calling the function recursively until the stack overflows:
* 1: T ~= a_1F + b_1V + U
*
* If we change `b` to a different number:
* 2: T ~= a_2F + b_2V + U
*
* Putting them together:
* a_1F + b_1V + U ~= a_2F + b_2V + U
* a_1F + b_1V ~= a_2F + b_2V
* (a_1 - a_2)F ~= (b_2 - b_1)V
* F ~= ((b_2 - b_1) / (a_1 - a_2))V
*
* Replacing F in #1, we can find the maximum number of variables
* given the available stack memory:
* T ~= a_1F + b_1V + U
* T ~= a_1*((b_2 - b_1) / (a_1 - a_2))V + b_1V + U
* T - U ~= (a_1*((b_2 - b_1) / (a_1 - a_2)) + b_1)V
* (T - U) / V ~= (a_1*((b_2 - b_1) / (a_1 - a_2)) + b_1)
*
* Note: The stack will overflow on a whole number of functions. So
* the larger `b` is, the smaller and less accurate 'a' is. So
* the result is more accurate for lower number of variables.
* But less variables take longer to overflow the stack.
* The tradeoff between quick vs accuracy is thus decided
* via an input parameter.
*/
accuracy = 7 * (1 - numberUtils_1.clamp(accuracy, 0, 1));
let a1 = 0, a2 = 0, b1 = 0, b2 = 0;
function test(length) {
let i = 0;
try {
(function fn(...args) {
++i;
fn(...args);
})(...new Array(length).fill(0));
}
catch (err) { }
return i;
}
do {
b2 = 2 << accuracy;
b1 = b2 - 1;
a1 = test(b1);
a2 = test(b2);
} while (a1 === a2 && --accuracy >= 0);
if (a1 === a2) {
return 8;
}
return Math.floor(((a1 + 2) * (a2 * b2 - a1 * b1)) / (a1 - a2) + a1 * b1 + 10);
}
exports.getMaxArgumentsLength = getMaxArgumentsLength;
/**
* Get the maximum length of an array.
*
* At the time of implementation, the [ECMA-262](https://tc39.es/ecma262/#array-index)
* defines the maximum as: `0 <= array.length <= 2**32 - 1`
*
* That said, the actual maximum may change over time and/or unique environments.
*/
function getMaxArrayLength() {
return search(0, Number.MAX_SAFE_INTEGER, (i) => {
try {
new Array(i);
return 1;
}
catch (e) {
return -1;
}
});
}
exports.getMaxArrayLength = getMaxArrayLength;
//# sourceMappingURL=utils.js.map