From f719438a91589b307458e271a32d6f629fed09a9 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 18 Dec 2024 11:33:01 +0100 Subject: [PATCH] assert: make myers_diff function more performant --- lib/internal/assert/myers_diff.js | 62 +++++++++++++++++-------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/lib/internal/assert/myers_diff.js b/lib/internal/assert/myers_diff.js index 8893ff78d7c2a7..50507c3af9a13f 100644 --- a/lib/internal/assert/myers_diff.js +++ b/lib/internal/assert/myers_diff.js @@ -1,10 +1,9 @@ 'use strict'; const { - Array, - ArrayPrototypeFill, ArrayPrototypePush, ArrayPrototypeSlice, + Int32Array, StringPrototypeEndsWith, } = primordials; @@ -26,7 +25,7 @@ function myersDiff(actual, expected, checkCommaDisparity = false) { const actualLength = actual.length; const expectedLength = expected.length; const max = actualLength + expectedLength; - const v = ArrayPrototypeFill(Array(2 * max + 1), 0); + const v = new Int32Array(2 * max + 1); const trace = []; @@ -35,22 +34,24 @@ function myersDiff(actual, expected, checkCommaDisparity = false) { ArrayPrototypePush(trace, newTrace); for (let diagonalIndex = -diffLevel; diagonalIndex <= diffLevel; diagonalIndex += 2) { - let x; - if (diagonalIndex === -diffLevel || - (diagonalIndex !== diffLevel && v[diagonalIndex - 1 + max] < v[diagonalIndex + 1 + max])) { - x = v[diagonalIndex + 1 + max]; - } else { - x = v[diagonalIndex - 1 + max] + 1; - } - + const offset = diagonalIndex + max; + const previousOffset = v[offset - 1]; + const nextOffset = v[offset + 1]; + let x = diagonalIndex === -diffLevel || (diagonalIndex !== diffLevel && previousOffset < nextOffset) ? + nextOffset : + previousOffset + 1; let y = x - diagonalIndex; - while (x < actualLength && y < expectedLength && areLinesEqual(actual[x], expected[y], checkCommaDisparity)) { + while ( + x < actualLength && + y < expectedLength && + areLinesEqual(actual[x], expected[y], checkCommaDisparity) + ) { x++; y++; } - v[diagonalIndex + max] = x; + v[offset] = x; if (x >= actualLength && y >= expectedLength) { return backtrack(trace, actual, expected, checkCommaDisparity); @@ -71,10 +72,13 @@ function backtrack(trace, actual, expected, checkCommaDisparity) { for (let diffLevel = trace.length - 1; diffLevel >= 0; diffLevel--) { const v = trace[diffLevel]; const diagonalIndex = x - y; - let prevDiagonalIndex; + const offset = diagonalIndex + max; - if (diagonalIndex === -diffLevel || - (diagonalIndex !== diffLevel && v[diagonalIndex - 1 + max] < v[diagonalIndex + 1 + max])) { + let prevDiagonalIndex; + if ( + diagonalIndex === -diffLevel || + (diagonalIndex !== diffLevel && v[offset - 1] < v[offset + 1]) + ) { prevDiagonalIndex = diagonalIndex + 1; } else { prevDiagonalIndex = diagonalIndex - 1; @@ -84,8 +88,11 @@ function backtrack(trace, actual, expected, checkCommaDisparity) { const prevY = prevX - prevDiagonalIndex; while (x > prevX && y > prevY) { - const value = !checkCommaDisparity || - StringPrototypeEndsWith(actual[x - 1], ',') ? actual[x - 1] : expected[y - 1]; + const actualItem = actual[x - 1]; + const value = + !checkCommaDisparity || StringPrototypeEndsWith(actualItem, ',') ? + actualItem : + expected[y - 1]; ArrayPrototypePush(result, { __proto__: null, type: 'nop', value }); x--; y--; @@ -110,13 +117,15 @@ function printSimpleMyersDiff(diff) { for (let diffIdx = diff.length - 1; diffIdx >= 0; diffIdx--) { const { type, value } = diff[diffIdx]; + let color = colors.white; + if (type === 'insert') { - message += `${colors.green}${value}${colors.white}`; + color = colors.green; } else if (type === 'delete') { - message += `${colors.red}${value}${colors.white}`; - } else { - message += `${colors.white}${value}${colors.white}`; + color = colors.red; } + + message += `${color}${value}${colors.white}`; } return `\n${message}`; @@ -129,17 +138,16 @@ function printMyersDiff(diff, simple = false) { for (let diffIdx = diff.length - 1; diffIdx >= 0; diffIdx--) { const { type, value } = diff[diffIdx]; - const previousType = (diffIdx < (diff.length - 1)) ? diff[diffIdx + 1].type : null; - const typeChanged = previousType && (type !== previousType); + const previousType = diffIdx < diff.length - 1 ? diff[diffIdx + 1].type : null; - if (typeChanged && previousType === 'nop') { - // Avoid grouping if only one line would have been grouped otherwise + // Avoid grouping if only one line would have been grouped otherwise + if (previousType === 'nop' && type !== previousType) { if (nopCount === kNopLinesToCollapse + 1) { message += `${colors.white} ${diff[diffIdx + 1].value}\n`; } else if (nopCount === kNopLinesToCollapse + 2) { message += `${colors.white} ${diff[diffIdx + 2].value}\n`; message += `${colors.white} ${diff[diffIdx + 1].value}\n`; - } if (nopCount >= (kNopLinesToCollapse + 3)) { + } else if (nopCount >= kNopLinesToCollapse + 3) { message += `${colors.blue}...${colors.white}\n`; message += `${colors.white} ${diff[diffIdx + 1].value}\n`; skipped = true;