pnodediff.h 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. //-----------------------------------------------------------------------------
  7. enum EditKind
  8. {
  9. // No change.
  10. None = 0,
  11. // Node value was updated.
  12. Update,
  13. // Node was inserted.
  14. Insert,
  15. // Node was deleted.
  16. Delete,
  17. // Node changed parent.
  18. Move,
  19. // Node changed position within its parent. The parent nodes of the old node and the new node are matching.
  20. Reorder,
  21. };
  22. //-----------------------------------------------------------------------------
  23. // Calculates Longest Common Subsequence.
  24. // This uses the basic version in
  25. //
  26. // EUGENE W. MYERS: An O(ND) Difference Algorithm and Its Variations
  27. //
  28. // The idea is that LCS is a dual problem of shortest path in an edit graph. The edit graph is a grid of lengthA
  29. // columns and lengthB rows. A path starts from (0,0) and moves toward (lenghA, lengthB).
  30. // - A horizontal move (i,j) -> (i+1,j) represents deleting A[i].
  31. // - A vertical move (i,j) -> (i,j+1) represents inserting B[j].
  32. // - A diagonal move (i,j) -> (i+1,j+1) represents a match, A[i] == B[j].
  33. // Each diagonal move represents a match. We want more diagnonal moves. Let diagonal move cost 0, horizontal or
  34. // vertical move each costs 1. The basic algorithm is a greedy algorthm to find a shortest path from (0,0) to
  35. // (lengthA, lengthB).
  36. //
  37. // Terms:
  38. // diagonal k: The diagnonal where x-y==k.
  39. // d-path: A path starting from (0,0) with d number of horizontal or vertical moves. Or, its length is d (note
  40. // that each horizontal/vertical move costs 1, diagnonal move costs 0).
  41. //
  42. // 0-path can only move along and end on diagonal 0.
  43. // 1-path can only end on diagonal -1 or 1.
  44. // d-path can end on diagonal [-d, -d+2, ..., d-2, d].
  45. //
  46. // The basic algorithm tries to find the smallest d, where there is a d-path reaches (lengthA, lengthB).
  47. //-----------------------------------------------------------------------------
  48. template <class Allocator>
  49. class LongestCommonSubsequence
  50. {
  51. private:
  52. // Stores d-path furthest reaching endpoints. They can be on diagonal [-d, -d+2, ..., d-2, d].
  53. class EndPoints
  54. {
  55. private:
  56. int d;
  57. int x[]; // Stores x for endpoints on the (d+1) diagonals. y == x - k.
  58. EndPoints(int d) : d(d)
  59. {
  60. }
  61. public:
  62. int getd() const
  63. {
  64. return d;
  65. }
  66. // Get x of furthest reaching endpoint on diagonal k.
  67. int operator[](int k) const
  68. {
  69. Assert(k >= -d && k <= d && (d - k) % 2 == 0); // k must be in [-d, -d+2, ..., d-2, d]
  70. int i = (k + d) / 2;
  71. return x[i];
  72. }
  73. // Get x reference of furthest reaching endpoint on diagonal k.
  74. int& operator[](int k)
  75. {
  76. Assert(k >= -d && k <= d && (d - k) % 2 == 0); // k must be in [-d, -d+2, ..., d-2, d]
  77. int i = (k + d) / 2;
  78. return x[i];
  79. }
  80. static EndPoints* New(Allocator* alloc, int d)
  81. {
  82. Assert(d >= 0);
  83. return AllocatorNewPlusLeaf(Allocator, alloc, sizeof(int) * (d + 1), EndPoints, d);
  84. }
  85. void Destroy(Allocator* alloc)
  86. {
  87. AllocatorDeletePlusLeaf(Allocator, alloc, sizeof(int) * (d + 1), this);
  88. }
  89. };
  90. // Represents an EditGraph for finding LCS
  91. class EditGraph
  92. {
  93. private:
  94. typedef JsUtil::List<EndPoints*, Allocator> EndPointsList;
  95. EndPointsList m_endPoints; // Stores endPoints for paths: -1, 0, 1, ..., d
  96. int m_diagonal; // The final diagonal found on d-path that reaches destination
  97. // Add EndPoints storage for d-path
  98. EndPoints* AddEndPoints(int d)
  99. {
  100. int i = m_endPoints.Add(nullptr);
  101. EndPoints* e = EndPoints::New(m_endPoints.GetAllocator(), d);
  102. m_endPoints.Item(i, e);
  103. return e;
  104. }
  105. public:
  106. EditGraph(Allocator* alloc) : m_endPoints(alloc) {}
  107. ~EditGraph()
  108. {
  109. Allocator* alloc = m_endPoints.GetAllocator();
  110. m_endPoints.Map([=](int, EndPoints* e)
  111. {
  112. if (e)
  113. {
  114. e->Destroy(alloc);
  115. }
  116. });
  117. }
  118. //
  119. // This is the basic algorithm to find a shortest path in the edit graph from (0,0) to (lengthA, lengthB).
  120. // We iterate through d=0,1,2,... to find smallest d, where one d-path reaches (lengthA, lengthB).
  121. // - d-path must end on diagonal [-d, -d+2, ..., d-2, d]
  122. // - A furthest reaching d-path on diagonal k is composed of a furthest reaching d-1 path on diagonal k-1 or k+1,
  123. // followed by a vertical or horizontal move, followed by moving along diagonal k.
  124. //
  125. template <class ItemEquals>
  126. void FindPath(int lengthA, int lengthB, const ItemEquals& equals)
  127. {
  128. Assert(m_endPoints.Empty()); // Only support one FindPath
  129. int maxD;
  130. if (Int32Math::Add(lengthA, lengthB, &maxD) || maxD > INT_MAX / 2) // Limits maxD to simplify overflow handling
  131. {
  132. Math::DefaultOverflowPolicy();
  133. }
  134. // Pre-add virtual path -1
  135. {
  136. EndPoints& pre = *AddEndPoints(1);
  137. pre[1] = 0;
  138. }
  139. bool found = false;
  140. for (int d = 0; d <= maxD && !found; d++)
  141. {
  142. const EndPoints& v = *m_endPoints.Item(d); // d-1 path
  143. EndPoints& cur = *AddEndPoints(d); // d path
  144. for (int k = -d; k <= d; k += 2)
  145. {
  146. const bool verticalMove = (k == -d || (k != d && v[k - 1] < v[k + 1]));
  147. int x = verticalMove ? v[k + 1] : v[k - 1] + 1;
  148. int y = x - k;
  149. while (x < lengthA && y < lengthB && equals(x, y))
  150. {
  151. x++;
  152. y++;
  153. }
  154. cur[k] = x; // furthest reaching end point
  155. if (x == lengthA && y == lengthB)
  156. {
  157. m_diagonal = k;
  158. found = true;
  159. break;
  160. }
  161. }
  162. }
  163. Assert(found);
  164. }
  165. template <class Func>
  166. void MapEdits(const Func& map) const
  167. {
  168. // m_endPoints contains endPoints for paths: -1, 0, 1, ..., d
  169. int d = m_endPoints.Count() - 2;
  170. int k = m_diagonal;
  171. for (; d >= 0; d--)
  172. {
  173. const EndPoints& v = *m_endPoints.Item(d); // d-1 path
  174. const EndPoints& cur = *m_endPoints.Item(d + 1); // d path
  175. Assert(cur.getd() == d);
  176. const bool verticalMove = (k == -d || (k != d && v[k - 1] < v[k + 1]));
  177. int x0 = verticalMove ? v[k + 1] : v[k - 1] + 1;
  178. int x = cur[k];
  179. int y = x - k;
  180. while (x > x0)
  181. {
  182. map(EditKind::Update, --x, --y);
  183. }
  184. if (verticalMove)
  185. {
  186. if (d > 0) // Don't emit virtual initial move from path -1 to 0
  187. {
  188. map(EditKind::Insert, -1, --y);
  189. }
  190. k++;
  191. }
  192. else
  193. {
  194. map(EditKind::Delete, --x, -1);
  195. k--;
  196. }
  197. }
  198. }
  199. };
  200. struct Edit
  201. {
  202. EditKind kind;
  203. int indexA;
  204. int indexB;
  205. Edit() {}
  206. Edit(EditKind kind, int indexA, int indexB) :
  207. kind(kind), indexA(indexA), indexB(indexB)
  208. {
  209. Assert((kind == EditKind::Insert && indexA == -1 && indexB >= 0)
  210. || (kind == EditKind::Delete && indexA >= 0 && indexB == -1)
  211. || (kind == EditKind::Update && indexA >= 0 && indexB >= 0));
  212. }
  213. };
  214. typedef JsUtil::List<Edit, Allocator, /*isLeaf*/true> EditList;
  215. EditList m_edits;
  216. public:
  217. template <class ItemEquals>
  218. LongestCommonSubsequence(Allocator* alloc, int lengthA, int lengthB, const ItemEquals& equals) :
  219. m_edits(alloc)
  220. {
  221. EditGraph graph(alloc);
  222. graph.FindPath(lengthA, lengthB, equals);
  223. graph.MapEdits([this](EditKind kind, int indexA, int indexB)
  224. {
  225. m_edits.Add(Edit(kind, indexA, indexB));
  226. });
  227. }
  228. template <class Func>
  229. void MapEdits(const Func& map) const
  230. {
  231. for (int i = m_edits.Count() - 1; i >= 0; i--)
  232. {
  233. const Edit& e = m_edits.Item(i);
  234. map(e.kind, e.indexA, e.indexB);
  235. }
  236. }
  237. template <class Func>
  238. void MapMatches(const Func& map) const
  239. {
  240. MapEdits([&](EditKind kind, int indexA, int indexB)
  241. {
  242. if (kind == EditKind::Update)
  243. {
  244. map(indexA, indexB);
  245. }
  246. });
  247. }
  248. };
  249. //
  250. // Returns a distance [0..1] of the specified sequences. The smaller distance the more of their elements match.
  251. //
  252. template <class Allocator, class ItemEquals>
  253. double ComputeLongestCommonSubsequenceDistance(Allocator* alloc, int lengthA, int lengthB, const ItemEquals& equals)
  254. {
  255. Assert(lengthA >= 0 && lengthB >= 0);
  256. if (lengthA == 0 || lengthB == 0)
  257. {
  258. return (lengthA == lengthB) ? 0.0 : 1.0;
  259. }
  260. int lcsLength = 0;
  261. LongestCommonSubsequence<Allocator> lcs(alloc, lengthA, lengthB, equals);
  262. lcs.MapMatches([&](int, int)
  263. {
  264. ++lcsLength;
  265. });
  266. return 1.0 - (double)lcsLength / (double)max(lengthA, lengthB);
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Base class for TreeComparers, used with TreeMatch. TreeComparers specify parse node details.
  270. //-----------------------------------------------------------------------------
  271. template <class SubClass, class Node>
  272. struct TreeComparerBase
  273. {
  274. typedef Node Node;
  275. typedef Node* PNode;
  276. static const double ExactMatchDistance;
  277. static const double EpsilonDistance;
  278. const SubClass* pThis() const { return static_cast<const SubClass*>(this); }
  279. SubClass* pThis() { return static_cast<SubClass*>(this); }
  280. // The number of distinct labels used in the tree.
  281. int LabelCount() const { return 0; }
  282. // Returns an integer label corresponding to the given node.
  283. // Returned value must be within [0, LabelCount).
  284. int GetLabel(PNode x) const { return 0; }
  285. // Returns N > 0 if the node with specified label can't change its N-th ancestor node, zero otherwise.
  286. // 1st ancestor is the node's parent node.
  287. // 2nd ancestor is the node's grandparent node.
  288. // etc.
  289. int TiedToAncestor(int label) { return 0; }
  290. // Calculates the distance [0..1] of two nodes.
  291. // The more similar the nodes the smaller the distance.
  292. //
  293. // Used to determine whether two nodes of the same label match.
  294. // Even if 0 is returned the nodes might be slightly different.
  295. double GetDistance(PNode x, PNode y) const { return 0; }
  296. // Returns true if the specified nodes have equal values.
  297. // Called with matching nodes (oldNode, newNode).
  298. // Return true if the values of the nodes are the same, or their difference is not important.
  299. bool ValuesEqual(PNode oldNode, PNode newNode) const { return true; }
  300. PNode GetParent(PNode x) const { return nullptr; }
  301. bool TryGetParent(PNode x, _Out_ PNode* p) const
  302. {
  303. *p = pThis()->GetParent(x);
  304. return *p != nullptr;
  305. }
  306. PNode GetAncestor(PNode node, int level) const
  307. {
  308. while (level > 0)
  309. {
  310. node = pThis()->GetParent(node);
  311. level--;
  312. }
  313. return node;
  314. }
  315. // Map children nodes of x
  316. template <class Func>
  317. void MapChildren(PNode x, const Func& func) const {}
  318. // Map all descendant nodes of x (not including x itself)
  319. template <class Func>
  320. void MapDescendants(PNode x, const Func& func) const
  321. {
  322. pThis()->MapChildren(x, [&](PNode child)
  323. {
  324. func(child);
  325. MapDescendants(child, func);
  326. });
  327. }
  328. // Map every node in the (sub)tree x.
  329. template <class Func>
  330. void MapTree(PNode x, const Func& func) const
  331. {
  332. func(x);
  333. pThis()->MapDescendants(x, func);
  334. }
  335. // Return true if specified nodes belong to the same tree. For debug only.
  336. bool TreesEqual(PNode left, PNode right) const { return true; }
  337. };
  338. template <class SubClass, class Node> const double TreeComparerBase<SubClass, Node>::ExactMatchDistance = 0.0;
  339. template <class SubClass, class Node> const double TreeComparerBase<SubClass, Node>::EpsilonDistance = 0.00001;
  340. //-----------------------------------------------------------------------------
  341. // Tree match algorithm, based on general algorithm described in
  342. // Change Detection in Hierarchically Structured Information
  343. // by Sudarshan S. Chawathe, Anand Rajaraman, Hector Garcia-Molina, and Jennifer Widom
  344. //
  345. // Derived from Roslyn implementation.
  346. //-----------------------------------------------------------------------------
  347. template <class TreeComparer, class Allocator>
  348. class TreeMatch
  349. {
  350. public:
  351. // ParseNodes are owned by Parser arena. Considered leaf here.
  352. typedef typename TreeComparer::PNode PNode;
  353. typedef JsUtil::List<PNode, Allocator, /*isLeaf*/true> NodeList;
  354. typedef JsUtil::BaseDictionary<PNode, PNode, typename ForceLeafAllocator<Allocator>::AllocatorType> NodeMap;
  355. private:
  356. static const double ExactMatchDistance;
  357. static const double EpsilonDistance;
  358. static const double MatchingDistance1;
  359. static const double MatchingDistance2;
  360. static const double MatchingDistance3;
  361. static const double MaxDistance;
  362. Allocator* alloc;
  363. const PNode root1;
  364. const PNode root2;
  365. TreeComparer comparer;
  366. NodeMap* oneToTwo;
  367. NodeMap* twoToOne;
  368. public:
  369. TreeMatch(Allocator* alloc, PNode root1, PNode root2, const TreeComparer& comparer = TreeComparer()) :
  370. alloc(alloc), root1(root1), root2(root2), comparer(comparer)
  371. {
  372. const int labelCount = comparer.LabelCount();
  373. // calculate chains (not including root node)
  374. AutoAllocatorObjectArrayPtr<NodeList, Allocator> nodes1(AllocatorNewArrayZ(Allocator, alloc, NodeList*, labelCount), labelCount, alloc);
  375. AutoAllocatorObjectArrayPtr<NodeList, Allocator> nodes2(AllocatorNewArrayZ(Allocator, alloc, NodeList*, labelCount), labelCount, alloc);
  376. int count1 = CategorizeNodesByLabels(root1, labelCount, nodes1);
  377. int count2 = CategorizeNodesByLabels(root2, labelCount, nodes2);
  378. AutoAllocatorObjectPtr<NodeMap, Allocator> map1(AllocatorNew(Allocator, alloc, NodeMap, alloc, count1), alloc);
  379. AutoAllocatorObjectPtr<NodeMap, Allocator> map2(AllocatorNew(Allocator, alloc, NodeMap, alloc, count2), alloc);
  380. this->oneToTwo = map1;
  381. this->twoToOne = map2;
  382. ComputeMatch(nodes1, nodes2, labelCount);
  383. // Succeeded. Detach local objects that are now owned by this instance.
  384. map1.Detach();
  385. map2.Detach();
  386. }
  387. ~TreeMatch()
  388. {
  389. DeleteObject<Allocator>(alloc, oneToTwo);
  390. DeleteObject<Allocator>(alloc, twoToOne);
  391. }
  392. const TreeComparer& Comparer() const { return comparer; }
  393. PNode OldRoot() const { return root1; }
  394. PNode NewRoot() const { return root2; }
  395. bool HasPartnerInTree1(PNode node2) const
  396. {
  397. Assert(comparer.TreesEqual(node2, root2));
  398. return twoToOne->ContainsKey(node2);
  399. }
  400. bool HasPartnerInTree2(PNode node1) const
  401. {
  402. Assert(comparer.TreesEqual(node1, root1));
  403. return oneToTwo->ContainsKey(node1);
  404. }
  405. bool TryGetPartnerInTree1(PNode node2, PNode* partner1) const
  406. {
  407. Assert(comparer.TreesEqual(node2, root2));
  408. return twoToOne->TryGetValue(node2, partner1);
  409. }
  410. bool TryGetPartnerInTree2(PNode node1, PNode* partner2) const
  411. {
  412. Assert(comparer.TreesEqual(node1, root1));
  413. return oneToTwo->TryGetValue(node1, partner2);
  414. }
  415. bool Contains(PNode node1, PNode node2) const
  416. {
  417. Assert(comparer.TreesEqual(node2, root2));
  418. PNode partner2;
  419. return TryGetPartnerInTree2(node1, &partner2) && node2 == partner2;
  420. }
  421. private:
  422. int CategorizeNodesByLabels(PNode root, int labelCount, _Out_writes_(labelCount) NodeList* nodes[])
  423. {
  424. int count = 0;
  425. comparer.MapDescendants(root, [&](PNode node)
  426. {
  427. int label = comparer.GetLabel(node);
  428. Assert(label >= 0 && label < labelCount);
  429. NodeList* list = nodes[label];
  430. if (!list)
  431. {
  432. list = NodeList::New(alloc);
  433. nodes[label] = list;
  434. }
  435. list->Add(node);
  436. count++;
  437. });
  438. return count;
  439. }
  440. void ComputeMatch(_In_reads_(labelCount) NodeList* nodes1[], _In_reads_(labelCount) NodeList* nodes2[], int labelCount)
  441. {
  442. // Root nodes always match but they might have been added as knownMatches
  443. if (!HasPartnerInTree2(root1))
  444. {
  445. Add(root1, root2);
  446. }
  447. // --- The original FastMatch algorithm ---
  448. //
  449. // For each leaf label l, and then for each internal node label l do:
  450. // a) S1 := chain T1(l)
  451. // b) S2 := chain T2(l)
  452. // c) lcs := LCS(S1, S2, Equal)
  453. // d) For each pair of nodes (x,y) in lcs add (x,y) to M.
  454. // e) Pair unmatched nodes with label l as in Algorithm Match, adding matches to M:
  455. // For each unmatched node x in T1, if there is an unmatched node y in T2 such that equal(x,y)
  456. // then add (x,y) to M.
  457. //
  458. // equal(x,y) is defined as follows:
  459. // x, y are leafs => equal(x,y) := label(x) == label(y) && compare(value(x), value(y)) <= f
  460. // x, y are nodes => equal(x,y) := label(x) == label(y) && |common(x,y)| / max(|x|, |y|) > t
  461. // where f, t are constants.
  462. //
  463. // --- Actual implementation ---
  464. //
  465. // We also categorize nodes by their labels, but then we proceed differently:
  466. //
  467. // 1) A label may be marked "tied to parent". Let x, y have both label l and l is "tied to parent".
  468. // Then (x,y) can be in M only if (parent(x), parent(y)) in M.
  469. // Thus we require labels of children tied to a parent to be preceeded by all their possible parent labels.
  470. //
  471. // 2) Rather than defining function equal in terms of constants f and t, which are hard to get right,
  472. // we try to match multiple times with different threashold for node distance.
  473. // The comparer defines the distance [0..1] between two nodes and it can do so by analyzing
  474. // the node structure and value. The comparer can tune the distance specifically for each node kind.
  475. // We first try to match nodes of the same labels to the exactly matching or almost matching counterpars.
  476. // The we keep increasing the threashold and keep adding matches.
  477. for (int label = 0; label < labelCount; label++)
  478. {
  479. if (nodes1[label] && nodes2[label])
  480. {
  481. ComputeMatchForLabel(label, *nodes1[label], *nodes2[label]);
  482. }
  483. }
  484. }
  485. void ComputeMatchForLabel(int label, NodeList& s1, NodeList& s2)
  486. {
  487. int tiedToAncestor = comparer.TiedToAncestor(label);
  488. ComputeMatchForLabel(s1, s2, tiedToAncestor, EpsilonDistance); // almost exact match
  489. ComputeMatchForLabel(s1, s2, tiedToAncestor, MatchingDistance1); // ok match
  490. ComputeMatchForLabel(s1, s2, tiedToAncestor, MatchingDistance2); // ok match
  491. ComputeMatchForLabel(s1, s2, tiedToAncestor, MatchingDistance3); // ok match
  492. ComputeMatchForLabel(s1, s2, tiedToAncestor, MaxDistance); // any match
  493. }
  494. void ComputeMatchForLabel(NodeList& s1, NodeList& s2, int tiedToAncestor, double maxAcceptableDistance)
  495. {
  496. // Obviously, the algorithm below is O(n^2). However, in the common case, the 2 lists will
  497. // be sequences that exactly match. The purpose of "firstNonMatch2" is to reduce the complexity
  498. // to O(n) in this case. Basically, the pointer is the 1st non-matched node in the list of nodes of tree2
  499. // with the given label.
  500. // Whenever we match to firstNonMatch2 we set firstNonMatch2 to the subsequent node.
  501. // So in the case of totally matching sequences, we process them in O(n) -
  502. // both node1 and firstNonMatch2 will be advanced simultaneously.
  503. UnmatchedIterator i1(s1);
  504. for (;;)
  505. {
  506. PNode node1 = i1.GetNextUnmatched();
  507. if (!node1) break;
  508. Assert(!HasPartnerInTree2(node1));
  509. // Find node2 that matches node1 the best, i.e. has minimal distance.
  510. double bestDistance = MaxDistance;
  511. PNode bestMatch = nullptr;
  512. int bestMatchIndex = -1; // node1's best match index in list2
  513. bool matched = false;
  514. UnmatchedIterator i2(s2);
  515. for (;;)
  516. {
  517. PNode node2 = i2.GetNextUnmatched();
  518. if (!node2) break;
  519. Assert(!HasPartnerInTree1(node2));
  520. // this requires parents to be processed before their children:
  521. if (tiedToAncestor > 0)
  522. {
  523. // TODO: For nodes tied to their parents,
  524. // consider avoding matching them to all other nodes of the same label.
  525. // Rather we should only match them with their siblings that share the same parent.
  526. PNode ancestor1 = comparer.GetAncestor(node1, tiedToAncestor);
  527. PNode ancestor2 = comparer.GetAncestor(node2, tiedToAncestor);
  528. Assert(comparer.GetLabel(ancestor1) < comparer.GetLabel(node1));
  529. if (!Contains(ancestor1, ancestor2))
  530. {
  531. continue;
  532. }
  533. }
  534. // We know that
  535. // 1. (node1, node2) not in M
  536. // 2. Both of their parents are matched to the same parent (or are not matched)
  537. //
  538. // Now, we have no other choice than comparing the node "values"
  539. // and looking for the one with the smaller distance.
  540. //
  541. double distance = comparer.GetDistance(node1, node2);
  542. if (distance < bestDistance)
  543. {
  544. matched = true;
  545. bestMatch = node2;
  546. bestMatchIndex = i2.CurIndex();
  547. bestDistance = distance;
  548. // We only stop if we've got an exact match. This is to resolve the problem
  549. // of entities with identical names(name is often used as the "value" of a
  550. // node) but with different "sub-values" (e.g. two locals may have the same name
  551. // but different types. Since the type is not part of the value, we don't want
  552. // to stop looking for the best match if we don't have an exact match).
  553. if (distance == ExactMatchDistance)
  554. {
  555. break;
  556. }
  557. }
  558. }
  559. if (matched && bestDistance <= maxAcceptableDistance)
  560. {
  561. Add(node1, bestMatch);
  562. i1.MarkCurrentMatched(); // i1's match is current node1
  563. i2.MarkMatched(bestMatchIndex); // i2's match is one of the nodes examined in the above for(;;) pass
  564. }
  565. }
  566. }
  567. void Add(PNode node1, PNode node2)
  568. {
  569. Assert(comparer.TreesEqual(node1, root1));
  570. Assert(comparer.TreesEqual(node2, root2));
  571. oneToTwo->Add(node1, node2);
  572. twoToOne->Add(node2, node1);
  573. }
  574. // The customized Match algorithm iterates over the 2 node lists, compares every unmatched node pair to match nodes.
  575. // To find the next unmatched node, original algorithm iterates over every node in each list, use a dictionary lookup
  576. // to test if the node has been matched or not, until it sees next unmatched node. This could be very expensive if the
  577. // lists are huge. E.g., assume the only diff is inserting a new node at the beginning of list2. Then for each node in
  578. // list1, it checks every node starting from the beginning new node in list2 for next unmatched node. This results in
  579. // O(N^2) dictionary lookups. And we do 5 passes of these.
  580. //
  581. // To improve on this, we can try to record every match span and directly jump to next unmatched position. Note that
  582. // in both lists once a node is matched, the list entry is no longer used. We can reuse that space to record extra info.
  583. // * Original PNode pointer value must be at even address. The list item must have 0 at bit0 (lowest bit).
  584. // * Once a node is matched, mark 1 at bit0. With this we can get rid of dictionary lookup.
  585. // * Next, for each matched entry, use the upper bits to record "next" unmatched index. Try to maintain match span,
  586. // so that from a matched node we can directly jump to next unmatched index.
  587. //
  588. // This class is for above purpose. Expected call pattern:
  589. // * GetNextUnmatched, [MarkCurrentMatched], GetNextUnmatched, [MarkCurrentMatched], ...
  590. // -- (A) With first MarkCurrentMatched we know the start of a match span.
  591. // -- (B) Subsequent MarkCurrentMatched indicates continuous match span.
  592. // -- (C) When MarkCurrentMatched is not called for an entry, we know the end of a match span. Record the whole
  593. // span (A)->(C). If walked again we would directly jump from (A) to (C).
  594. // * Random MarkMatched(i)
  595. // -- We don't know the exact match span. Just mark this entry "i" as matched, but set its "next" (upper bits) to 0.
  596. // -- During next pass, we can merge all adjacent match spans and individual matched entries to bigger match spans.
  597. // This would help next pass (we have 5).
  598. //
  599. class UnmatchedIterator
  600. {
  601. private:
  602. NodeList& list;
  603. int lastMatched; // last matched node index. -1 means no known last matched index.
  604. int index; // current examining index. Only moved by GetNextUnmatched().
  605. public:
  606. UnmatchedIterator(NodeList& list) :
  607. list(list),
  608. lastMatched(-1),
  609. index(-1)
  610. {
  611. VerifySize(list);
  612. }
  613. ~UnmatchedIterator()
  614. {
  615. // If we have lastMatched, we could have one of following:
  616. // * index is matched by MarkCurrentMatched(). Link lastMatched -> index (== lastMatched). GetNextUnmatched() can handle it.
  617. // * index remains unmatched (ends a matched sequence). Link lastMatched -> index.
  618. // * index is out of range. That means [lastMatched, ...end) are all matched. Link lastMatched -> index (out of range).
  619. //
  620. if (lastMatched >= 0)
  621. {
  622. SetNext(lastMatched, index);
  623. }
  624. }
  625. PNode GetNextUnmatched()
  626. {
  627. // If current ends a matched sequence, make a link [lastMatched -> current).
  628. if (lastMatched >= 0 && !IsMatched(index))
  629. {
  630. SetNext(lastMatched, index);
  631. lastMatched = -1;
  632. }
  633. ++index;
  634. if (index < list.Count())
  635. {
  636. if (IsMatched(index))
  637. {
  638. if (lastMatched < 0) // Check if current starts a matched sequence
  639. {
  640. lastMatched = index;
  641. }
  642. // Jumps all matched span, until sees an unmatched entry or the end.
  643. int next;
  644. while (index < list.Count() && IsNext(list.Item(index), &next))
  645. {
  646. index = max(next, index + 1); // Ensure moves forward (next could be 0, from individual MarkMatched() call).
  647. }
  648. }
  649. if (index < list.Count())
  650. {
  651. return list.Item(index);
  652. }
  653. }
  654. return nullptr;
  655. }
  656. int CurIndex() const { return index; }
  657. void MarkMatched(int i)
  658. {
  659. if (i == index)
  660. {
  661. MarkCurrentMatched();
  662. }
  663. else
  664. {
  665. SetMatched(i);
  666. }
  667. }
  668. void MarkCurrentMatched()
  669. {
  670. Assert(!IsMatched(index));
  671. SetMatched(index);
  672. if (lastMatched < 0) // If current starts a matched sequence
  673. {
  674. lastMatched = index;
  675. }
  676. }
  677. private:
  678. static void VerifySize(const NodeList& list)
  679. {
  680. if (list.Count() > INT_MAX / 2) // Limit max size as we used bit0
  681. {
  682. Math::DefaultOverflowPolicy();
  683. }
  684. }
  685. static void SetMatched(PNode& node)
  686. {
  687. SetNext(node, 0);
  688. }
  689. static bool IsMatched(PNode node)
  690. {
  691. return !!(reinterpret_cast<UINT_PTR>(node) & 1);
  692. }
  693. static void SetNext(PNode& node, int next)
  694. {
  695. UINT_PTR value = (static_cast<UINT_PTR>(next) << 1) | 1;
  696. node = reinterpret_cast<PNode>(value);
  697. }
  698. static bool IsNext(PNode node, _Out_ int* next)
  699. {
  700. UINT_PTR value = reinterpret_cast<UINT_PTR>(node);
  701. if (value & 1)
  702. {
  703. *next = static_cast<int>(value >> 1);
  704. return true;
  705. }
  706. return false;
  707. }
  708. void SetMatched(int i) { SetMatched(list.Item(i)); }
  709. bool IsMatched(int i) const { return IsMatched(list.Item(i)); }
  710. void SetNext(int i, int next) { SetNext(list.Item(i), next); }
  711. bool IsNext(int i, _Out_ int* next) const { return IsNext(list.Item(i), next); }
  712. };
  713. };
  714. template <class TreeComparer, class Allocator> const double TreeMatch<TreeComparer, Allocator>::ExactMatchDistance = TreeComparer::ExactMatchDistance;
  715. template <class TreeComparer, class Allocator> const double TreeMatch<TreeComparer, Allocator>::EpsilonDistance = TreeComparer::EpsilonDistance;
  716. template <class TreeComparer, class Allocator> const double TreeMatch<TreeComparer, Allocator>::MatchingDistance1 = 0.5;
  717. template <class TreeComparer, class Allocator> const double TreeMatch<TreeComparer, Allocator>::MatchingDistance2 = 1.0;
  718. template <class TreeComparer, class Allocator> const double TreeMatch<TreeComparer, Allocator>::MatchingDistance3 = 1.5;
  719. template <class TreeComparer, class Allocator> const double TreeMatch<TreeComparer, Allocator>::MaxDistance = 2.0;
  720. //-----------------------------------------------------------------------------
  721. // Represents an edit operation on a tree or a sequence of nodes.
  722. //-----------------------------------------------------------------------------
  723. template <class PNode>
  724. class Edit
  725. {
  726. private:
  727. EditKind kind;
  728. PNode node1;
  729. PNode node2;
  730. public:
  731. Edit() {}
  732. //
  733. // Insert nullptr NewNode
  734. // Delete OldNode nullptr
  735. // Move/Update OldNode NewNode
  736. //
  737. Edit(EditKind kind, PNode node1, PNode node2) :
  738. kind(kind), node1(node1), node2(node2)
  739. {
  740. Assert((node1 == nullptr) == (kind == EditKind::Insert));
  741. Assert((node2 == nullptr) == (kind == EditKind::Delete));
  742. }
  743. EditKind Kind() const { return kind; }
  744. PNode OldNode() const { return node1; }
  745. PNode NewNode() const { return node2; }
  746. };
  747. //-----------------------------------------------------------------------------
  748. // Represents a sequence of tree edits.
  749. //-----------------------------------------------------------------------------
  750. template <class TreeComparer, class Allocator>
  751. class EditScript
  752. {
  753. public:
  754. typedef TreeMatch<TreeComparer, Allocator> TreeMatch;
  755. typedef typename TreeMatch::PNode PNode;
  756. typedef typename TreeMatch::NodeList NodeList;
  757. typedef typename TreeMatch::NodeMap NodeMap;
  758. typedef JsUtil::List<Edit<PNode>, Allocator, /*isLeaf*/true> EditList;
  759. private:
  760. const TreeMatch& match;
  761. TreeComparer comparer;
  762. EditList edits;
  763. public:
  764. EditScript(Allocator* alloc, const TreeMatch& match) :
  765. match(match), comparer(match.Comparer()), edits(alloc)
  766. {
  767. AddUpdatesInsertsMoves();
  768. AddDeletes();
  769. }
  770. const EditList& Edits() const { return edits; }
  771. private:
  772. PNode Root1() const { return match.OldRoot(); }
  773. PNode Root2() const { return match.NewRoot(); }
  774. void AddUpdatesInsertsMoves()
  775. {
  776. // Breadth-first traversal.
  777. ProcessNode(Root2());
  778. JsUtil::Queue<PNode, Allocator> queue(edits.GetAllocator());
  779. queue.Enqueue(Root2());
  780. while (!queue.Empty())
  781. {
  782. PNode head = queue.Dequeue();
  783. comparer.MapChildren(head, [&](PNode child)
  784. {
  785. ProcessNode(child);
  786. queue.Enqueue(child);
  787. });
  788. }
  789. }
  790. void ProcessNode(PNode x)
  791. {
  792. Assert(comparer.TreesEqual(x, Root2()));
  793. // NOTE:
  794. // Our implementation differs from the algorithm described in the paper in following:
  795. // - We don't update M' and T1 since we don't need the final matching and the transformed tree.
  796. // - Insert and Move edits don't need to store the offset of the nodes relative to their parents,
  797. // so we don't calculate those. Thus we don't need to implement FindPos.
  798. // - We don't mark nodes "in order" since the marks are only needed by FindPos.
  799. // a)
  800. // Let x be the current node in the breadth-first search of T2.
  801. // Let y = parent(x).
  802. // Let z be the partner of parent(x) in M'. (note: we don't need z for insert)
  803. //
  804. // NOTE:
  805. // If we needed z then we would need to be updating M' as we encounter insertions.
  806. PNode w;
  807. bool hasPartner = match.TryGetPartnerInTree1(x, &w);
  808. PNode y;
  809. bool hasParent = comparer.TryGetParent(x, &y);
  810. if (!hasPartner)
  811. {
  812. // b) If x has no partner in M'.
  813. // i. k := FindPos(x)
  814. // ii. Append INS((w, a, value(x)), z, k) to E for a new identifier w.
  815. // iii. Add (w, x) to M' and apply INS((w, a, value(x)), z, k) to T1.
  816. edits.Add(Edit<PNode>(EditKind::Insert, /*node1*/nullptr, /*node2*/x));
  817. // NOTE:
  818. // We don't update M' here.
  819. }
  820. else if (hasParent)
  821. {
  822. // c) else if x is not a root
  823. // i. Let w be the partner of x in M', and let v = parent(w) in T1.
  824. PNode v = comparer.GetParent(w);
  825. // ii. if value(w) != value(x)
  826. // A. Append UPD(w, value(x)) to E
  827. // B. Apply UPD(w, value(x) to T1
  828. // Let the Comparer decide whether an update should be added to the edit list.
  829. // The Comparer defines what changes in node values it cares about.
  830. if (!comparer.ValuesEqual(w, x))
  831. {
  832. edits.Add(Edit<PNode>(EditKind::Update, /*node1*/w, /*node2*/x));
  833. }
  834. // If parents of w and x don't match, it's a move.
  835. // iii. if not (v, y) in M'
  836. // NOTE: The paper says (y, v) but that seems wrong since M': T1 -> T2 and w,v in T1 and x,y in T2.
  837. if (!match.Contains(v, y))
  838. {
  839. // A. Let z be the partner of y in M'. (NOTE: z not needed)
  840. // B. k := FindPos(x)
  841. // C. Append MOV(w, z, k)
  842. // D. Apply MOV(w, z, k) to T1
  843. edits.Add(Edit<PNode>(EditKind::Move, /*node1*/w, /*node2*/x));
  844. }
  845. }
  846. // d) AlignChildren(w, x)
  847. // NOTE: If we just applied an INS((w, a, value(x)), z, k) operation on tree T1
  848. // the newly created node w would have no children. So there is nothing to align.
  849. if (hasPartner)
  850. {
  851. AlignChildren(w, x);
  852. }
  853. }
  854. void AddDeletes()
  855. {
  856. // 3. Do a post-order traversal of T1.
  857. // a) Let w be the current node in the post-order traversal of T1.
  858. // b) If w has no partner in M' then append DEL(w) to E and apply DEL(w) to T1.
  859. //
  860. // NOTE: The fact that we haven't updated M' during the Insert phase
  861. // doesn't affect Delete phase. The original algorithm inserted new node n1 into T1
  862. // when an insertion INS(n1, n2) was detected. It also added (n1, n2) to M'.
  863. // Then in Delete phase n1 is visited but nothing is done since it has a partner n2 in M'.
  864. // Since we don't add n1 into T1, not adding (n1, n2) to M' doesn't affect the Delete phase.
  865. comparer.MapDescendants(Root1(), [&](PNode w)
  866. {
  867. if (!match.HasPartnerInTree2(w))
  868. {
  869. edits.Add(Edit<PNode>(EditKind::Delete, /*node1*/w, /*node2*/nullptr));
  870. }
  871. });
  872. }
  873. void AlignChildren(PNode w, PNode x)
  874. {
  875. Assert(comparer.TreesEqual(w, Root1()));
  876. Assert(comparer.TreesEqual(x, Root2()));
  877. Allocator* alloc = edits.GetAllocator();
  878. // Step 1
  879. // Make all children of w and and all children x "out of order"
  880. // NOTE: We don't need to mark nodes "in order".
  881. // Step 2
  882. // Let S1 be the sequence of children of w whose partner are children
  883. // of x and let S2 be the sequence of children of x whose partner are
  884. // children of w.
  885. NodeList s1(alloc), s2(alloc);
  886. if (!TryGetMatchedChildren(s1, w, x, [&](PNode e, PNode* partner) { return match.TryGetPartnerInTree2(e, partner); }) ||
  887. !TryGetMatchedChildren(s2, x, w, [&](PNode e, PNode* partner) { return match.TryGetPartnerInTree1(e, partner); }))
  888. {
  889. return;
  890. }
  891. // Step 3, 4
  892. // Define the function Equal(a,b) to be true if and only if (a,b) in M'
  893. // Let S <- LCS(S1, S2, Equal)
  894. NodeMap s(alloc);
  895. {
  896. LongestCommonSubsequence<Allocator> lcs(alloc, s1.Count(), s2.Count(), [&](int indexA, int indexB)
  897. {
  898. return match.Contains(s1.Item(indexA), s2.Item(indexB));
  899. });
  900. lcs.MapMatches([&](int indexA, int indexB)
  901. {
  902. s.AddNew(s1.Item(indexA), s2.Item(indexB));
  903. });
  904. }
  905. // Step 5
  906. // For each (a,b) in S, mark nodes a and b "in order"
  907. // NOTE: We don't need to mark nodes "in order".
  908. // Step 6
  909. // For each a in S1, b in S2 such that (a,b) in M but (a,b) not in S
  910. // (a) k <- FindPos(b)
  911. // (b) Append MOV(a,w,k) to E and apply MOV(a,w,k) to T1
  912. // (c) Mark a and b "in order"
  913. // NOTE: We don't mark nodes "in order".
  914. s1.Map([&](int index, PNode a)
  915. {
  916. PNode b;
  917. if (match.TryGetPartnerInTree2(a, &b) // (a,b) in M
  918. && comparer.GetParent(b) == x // => b in S2 since S2 == { b | parent(b) == x && parent(partner(b)) == w }
  919. && !ContainsPair(s, a, b)) // (a,b) not in S
  920. {
  921. Assert(comparer.TreesEqual(a, Root1()));
  922. Assert(comparer.TreesEqual(b, Root2()));
  923. edits.Add(Edit<PNode>(EditKind::Reorder, /*node1*/a, /*node2*/b));
  924. }
  925. });
  926. }
  927. // Helper: Get the sequence of children of x whose partner are children of y.
  928. template <class TryGetPartnerFunc>
  929. bool TryGetMatchedChildren(NodeList& nodes, PNode x, PNode y, const TryGetPartnerFunc& tryGetPartner)
  930. {
  931. Assert(nodes.Empty());
  932. comparer.MapChildren(x, [&](PNode e)
  933. {
  934. PNode partner;
  935. if (tryGetPartner(e, &partner) && comparer.GetParent(partner) == y)
  936. {
  937. nodes.Add(e);
  938. }
  939. });
  940. return !nodes.Empty();
  941. }
  942. static bool ContainsPair(const NodeMap& dict, PNode a, PNode b)
  943. {
  944. PNode value;
  945. return dict.TryGetValue(a, &value) && value == b;
  946. }
  947. };