为什么Diff-match-patch破线差异超过65K行

时间:2018-12-03 09:49:18

标签: javascript node.js google-diff-match-patch

我尝试将Google diff-match-path库用于行差异: https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs。总而言之,两个输入的行都超过65,536(2 ^ 16)行,我得到了错误的补丁。

是一个错误(在我的代码或diff-match-patch中),还是我遇到了javascript / nodejs的已知限制?我可以对较大的文件使用d-m-p吗?

使用node version v6.3.1, diff-match-patch 1.0.4

此脚本重现了问题

var diff_match_patch = require("diff-match-patch")

// function copied from google wiki 
// https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
function diff_lineMode(text1, text2) {
  var dmp = new diff_match_patch();
  var a = dmp.diff_linesToChars_(text1, text2);
  var lineText1 = a.chars1;
  var lineText2 = a.chars2;
  var lineArray = a.lineArray;
  var diffs = dmp.diff_main(lineText1, lineText2, false);
  dmp.diff_charsToLines_(diffs, lineArray);
  return diffs;
}

// reproduce problem by diffing string with many lines to "abcd"
for (let size = 65534; size < 65538; size += 1) {
  let text1 = "";
  for (let i = 0; i < size; i++) {
    text1 += i + "\n";
  }

  var patches = diff_lineMode(text1, "abcb")
  console.log("######## Size: " + size + ": patches " + patches.length)
  for (let i = 0; i < patches.length; i++) {
    // patch[0] is action, patch[1] is value
    var action = patches[i][0] < 0 ? "remove" : (patches[i][0] > 0 ? "add" : "keep")
    console.log("patch" + i + ": " + action + "\n" + patches[i][1].substring(0, 10))
  }
}

提供这些输出:

######## Size: 65534: patches 2
patch0: remove
0
1
2
3
4

patch1: add
abcb
######## Size: 65535: patches 2
patch0: remove
0
1
2
3
4

patch1: add

######## Size: 65536: patches 2
patch0: keep
0

patch1: remove
1
2
3
4
5

######## Size: 65537: patches 3
patch0: remove
0

patch1: keep
1

patch2: remove
2
3
4
5
6

1 个答案:

答案 0 :(得分:2)

这是ES5和算法映射行到16位unicode字符的限制。在ES6上,它可以扩展为2 ^ 21位,从而覆盖更长的文件。

为了加快行间差异,该算法不比较整个文本,而是用单个unicode字符替换每行。因此,替换中的每个字符都映射到哈希图中的唯一行。但是unicode字符的数量是有限的,并且当前的实现只是溢出。

这不会导致误报(相同的行仍被认为是相同的),但是对于自然差异,它可能会以每行1 / 65K的低概率错过某些行差异。

并且由于不同的行被映射到同一字符,因此它阻止了补丁可靠地映射回原始文本行,因此逆过程将所有此类字符映射到第一条映射行。

通过使用较大的符号目标空间(例如使用2或3个字符表示唯一的行),应该有可能将正确的差异缩放到更大的输入。