Skip to content

Commit 71a8f04

Browse files
authored
Merge pull request #447 from datafold/apply-args-by-chunks
Prevent `Maximum call stack exceeded` by apply args to `Math.min`, `Math.max` in chunks
2 parents 295b3e1 + fee1acf commit 71a8f04

File tree

5 files changed

+45
-14
lines changed

5 files changed

+45
-14
lines changed

lib/nesting-graph.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ module.exports = {
3131
function run(g) {
3232
let root = util.addDummyNode(g, "root", {}, "_root");
3333
let depths = treeDepths(g);
34-
let height = Math.max(...Object.values(depths)) - 1; // Note: depths is an Object not an array
34+
let depthsArr = Object.values(depths);
35+
let height = util.applyWithChunking(Math.max, depthsArr) - 1; // Note: depths is an Object not an array
3536
let nodeSep = 2 * height + 1;
3637

3738
g.graph().nestingRoot = root;

lib/order/init-order.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ module.exports = initOrder;
1818
function initOrder(g) {
1919
let visited = {};
2020
let simpleNodes = g.nodes().filter(v => !g.children(v).length);
21-
let maxRank = Math.max(...simpleNodes.map(v => g.node(v).rank));
21+
let simpleNodesRanks = simpleNodes.map(v => g.node(v).rank);
22+
let maxRank = util.applyWithChunking(Math.max, simpleNodesRanks);
2223
let layers = util.range(maxRank + 1).map(() => []);
2324

2425
function dfs(v) {

lib/position/bk.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,8 @@ function findSmallestWidthAlignment(g, xss) {
313313
*/
314314
function alignCoordinates(xss, alignTo) {
315315
let alignToVals = Object.values(alignTo),
316-
alignToMin = Math.min(...alignToVals),
317-
alignToMax = Math.max(...alignToVals);
316+
alignToMin = util.applyWithChunking(Math.min, alignToVals),
317+
alignToMax = util.applyWithChunking(Math.max, alignToVals);
318318

319319
["u", "d"].forEach(vert => {
320320
["l", "r"].forEach(horiz => {
@@ -324,9 +324,9 @@ function alignCoordinates(xss, alignTo) {
324324
if (xs === alignTo) return;
325325

326326
let xsVals = Object.values(xs);
327-
let delta = alignToMin - Math.min(...xsVals);
327+
let delta = alignToMin - util.applyWithChunking(Math.min, xsVals);
328328
if (horiz !== "l") {
329-
delta = alignToMax - Math.max(...xsVals);
329+
delta = alignToMax - util.applyWithChunking(Math.max,xsVals);
330330
}
331331

332332
if (delta) {

lib/rank/util.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"use strict";
22

3+
const { applyWithChunking } = require("../util");
4+
35
module.exports = {
46
longestPath: longestPath,
57
slack: slack
@@ -36,13 +38,15 @@ function longestPath(g) {
3638
}
3739
visited[v] = true;
3840

39-
var rank = Math.min(...g.outEdges(v).map(e => {
41+
let outEdgesMinLens = g.outEdges(v).map(e => {
4042
if (e == null) {
4143
return Number.POSITIVE_INFINITY;
4244
}
4345

4446
return dfs(e.w) - g.edge(e).minlen;
45-
}));
47+
});
48+
49+
var rank = applyWithChunking(Math.min, outEdgesMinLens);
4650

4751
if (rank === Number.POSITIVE_INFINITY) {
4852
rank = 0;

lib/util.js

+31-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ let Graph = require("@dagrejs/graphlib").Graph;
77
module.exports = {
88
addBorderNode,
99
addDummyNode,
10+
applyWithChunking,
1011
asNonCompoundGraph,
1112
buildLayerMatrix,
1213
intersectRect,
@@ -153,14 +154,15 @@ function buildLayerMatrix(g) {
153154
* rank(v) >= 0 and at least one node w has rank(w) = 0.
154155
*/
155156
function normalizeRanks(g) {
156-
let min = Math.min(...g.nodes().map(v => {
157+
let nodeRanks = g.nodes().map(v => {
157158
let rank = g.node(v).rank;
158159
if (rank === undefined) {
159160
return Number.MAX_VALUE;
160161
}
161162

162163
return rank;
163-
}));
164+
});
165+
let min = applyWithChunking(Math.min, nodeRanks);
164166
g.nodes().forEach(v => {
165167
let node = g.node(v);
166168
if (node.hasOwnProperty("rank")) {
@@ -171,7 +173,8 @@ function normalizeRanks(g) {
171173

172174
function removeEmptyRanks(g) {
173175
// Ranks may not start at 0, so we need to offset them
174-
let offset = Math.min(...g.nodes().map(v => g.node(v).rank));
176+
let nodeRanks = g.nodes().map(v => g.node(v).rank);
177+
let offset = applyWithChunking(Math.min, nodeRanks);
175178

176179
let layers = [];
177180
g.nodes().forEach(v => {
@@ -205,15 +208,37 @@ function addBorderNode(g, prefix, rank, order) {
205208
return addDummyNode(g, "border", node, prefix);
206209
}
207210

211+
function splitToChunks(array, chunkSize = CHUNKING_THRESHOLD) {
212+
const chunks = [];
213+
for (let i = 0; i < array.length; i += chunkSize) {
214+
const chunk = array.slice(i, i + chunkSize);
215+
chunks.push(chunk);
216+
}
217+
return chunks;
218+
}
219+
220+
const CHUNKING_THRESHOLD = 65535;
221+
222+
function applyWithChunking(fn, argsArray) {
223+
if(argsArray.length > CHUNKING_THRESHOLD) {
224+
const chunks = splitToChunks(argsArray);
225+
return fn.apply(null, chunks.map(chunk => fn.apply(null, chunk)));
226+
} else {
227+
return fn.apply(null, argsArray);
228+
}
229+
}
230+
208231
function maxRank(g) {
209-
return Math.max(...g.nodes().map(v => {
232+
const nodes = g.nodes();
233+
const nodeRanks = nodes.map(v => {
210234
let rank = g.node(v).rank;
211235
if (rank === undefined) {
212236
return Number.MIN_VALUE;
213237
}
214-
215238
return rank;
216-
}));
239+
});
240+
241+
return applyWithChunking(Math.max, nodeRanks);
217242
}
218243

219244
/*

0 commit comments

Comments
 (0)