1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * @file MovedBlocks.cpp
5 * @brief Moved block detection code.
16 void Add(int val) { m_map[val] = 1; }
17 void Remove(int val) { m_map.erase(val); }
18 size_t count() const { return m_map.size(); }
19 bool isPresent(int val) const { return m_map.find(val) != m_map.end(); }
20 int getSingle() const { return m_map.begin()->first; }
22 std::map<int, int> m_map;
26 * @brief Set of equivalent lines
27 * This uses diffutils line numbers, which are counted from the prefix
31 IntSet m_lines0; // equivalent lines on side#0
32 IntSet m_lines1; // equivalent lines on side#1
34 bool isPerfectMatch() const { return m_lines0.count()==1 && m_lines1.count()==1; }
38 /** @brief Maps equivalency code to equivalency group */
42 std::map<int, EqGroup *> m_map;
43 /** @brief Add a line to the appropriate equivalency group */
44 void Add(int lineno, int eqcode, int nside)
46 EqGroup *pgroup = find(eqcode);
47 if (pgroup == nullptr)
50 m_map[eqcode] = pgroup;
53 pgroup->m_lines1.Add(lineno);
55 pgroup->m_lines0.Add(lineno);
58 /** @brief Return the appropriate equivalency group */
59 EqGroup * find(int eqcode)
61 std::map<int, EqGroup *>::const_iterator it = m_map.find(eqcode);
62 return it != m_map.end() ? it->second : nullptr;
67 std::map<int, EqGroup *>::const_iterator pos = m_map.begin();
68 while (pos != m_map.end())
73 static bool isLineInDiffBlock(int nside, int lineno, change *script)
76 for (change *e = script; e; e = p)
81 if (e->line0 <= lineno && lineno < e->line0 + e->deleted)
86 if (e->line1 <= lineno && lineno < e->line1 + e->inserted)
94 WinMerge moved block code
95 This is called by diffutils code, by diff_2_files routine (in ANALYZE.C)
96 read_files earlier computed the hash chains ("equivs" file variable) and freed them,
97 but the equivs numerics are still available in each line
99 match1 set by scan from line0 to deleted
100 match0 set by scan from line1 to inserted
103 extern "C" void moved_block_analysis(struct change ** pscript, struct file_data fd[])
105 // Hash all altered lines
108 struct change * script = *pscript;
110 for (e = script; e; e = p)
114 for (i = e->line0; i - (e->line0) < (e->deleted); ++i)
115 map.Add(i, fd[0].equivs[i], 0);
116 for (i = e->line1; i - (e->line1) < (e->inserted); ++i)
117 map.Add(i, fd[1].equivs[i], 1);
121 // Scan through diff blocks, finding moved sections from left side
122 // and splitting them out
123 // That is, we actually fragment diff blocks as we find moved sections
124 for (e = script; e; e = p)
126 // scan down block for a match
128 EqGroup * pgroup = nullptr;
130 for (i=e->line0; i-(e->line0) < (e->deleted); ++i)
132 EqGroup * tempgroup = map.find(fd[0].equivs[i]);
133 if (tempgroup->isPerfectMatch())
140 // if no match, go to next diff block
141 if (pgroup == nullptr)
145 int j = pgroup->m_lines1.getSingle();
146 // Ok, now our moved block is the single line i,j
148 // extend moved block upward as far as possible
151 for ( ; i1>=e->line0; --i1, --j1)
153 EqGroup * pgroup0 = map.find(fd[0].equivs[i1]);
154 EqGroup * pgroup1 = map.find(fd[1].equivs[j1]);
155 if (pgroup0 != pgroup1 || !isLineInDiffBlock(1, j1, script))
157 // pgroup0->m_lines0.Remove(i1); // commented out this line although I'm not sure what this line means because this line causes the bug sf.net#2174
158 // pgroup1->m_lines1.Remove(j1);
162 // Ok, now our moved block is i1->i, j1->j
164 // extend moved block downward as far as possible
167 for ( ; i2-(e->line0) < (e->deleted); ++i2,++j2)
169 EqGroup * pgroup0 = map.find(fd[0].equivs[i2]);
170 EqGroup * pgroup1 = map.find(fd[1].equivs[j2]);
171 if (pgroup0 != pgroup1 || !isLineInDiffBlock(1, j2, script))
173 // pgroup0->m_lines0.Remove(i2); // commented out this line although I'm not sure what this line means because this line causes the bug sf.net#2174
174 // pgroup1->m_lines1.Remove(j2);
178 // Ok, now our moved block is i1->i2,j1->j2
181 assert(i2-i1 == j2-j1);
183 int prefix = i1 - (e->line0);
186 // break e (current change) into two pieces
187 // first part is the prefix, before the moved part
189 // second part is the moved part & anything after it
190 // that goes in newob
191 // leave the right side (e->inserted) on e
192 // so no right side on newob
193 // newob will be the moved part only, later after we split off any suffix from it
194 struct change *newob = (struct change *) xmalloc (sizeof (struct change));
198 newob->line1 = e->line1 + e->inserted;
200 newob->deleted = e->deleted - prefix;
201 newob->link = e->link;
208 // now make e point to the moved part (& any suffix)
211 // now e points to a moved diff chunk with no prefix, but maybe a suffix
215 int suffix = (e->deleted) - (i2-(e->line0)) - 1;
218 // break off any suffix from e
219 // newob will be the suffix, and will get all the right side
220 struct change *newob = (struct change *) xmalloc (sizeof (struct change));
224 newob->line1 = e->line1;
225 newob->inserted = e->inserted;
226 newob->deleted = suffix;
227 newob->link = e->link;
232 e->deleted -= suffix;
235 p = newob; // next block to scan
239 // Scan through diff blocks, finding moved sections from right side
240 // and splitting them out
241 // That is, we actually fragment diff blocks as we find moved sections
242 for (e = script; e; e = p)
244 // scan down block for a match
246 EqGroup * pgroup = nullptr;
248 for (j=e->line1; j-(e->line1) < (e->inserted); ++j)
250 EqGroup * tempgroup = map.find(fd[1].equivs[j]);
251 if (tempgroup->isPerfectMatch())
258 // if no match, go to next diff block
259 if (pgroup == nullptr)
263 int i = pgroup->m_lines0.getSingle();
264 // Ok, now our moved block is the single line i,j
266 // extend moved block upward as far as possible
269 for ( ; j1>=e->line1; --i1, --j1)
271 EqGroup * pgroup0 = map.find(fd[0].equivs[i1]);
272 EqGroup * pgroup1 = map.find(fd[1].equivs[j1]);
273 if (pgroup0 != pgroup1 || !isLineInDiffBlock(0, i1, script))
275 // pgroup0->m_lines0.Remove(i1); // commented out this line although I'm not sure what this line means because this line causes the bug sf.net#2174
276 // pgroup1->m_lines1.Remove(j1);
280 // Ok, now our moved block is i1->i, j1->j
282 // extend moved block downward as far as possible
285 for ( ; j2-(e->line1) < (e->inserted); ++i2,++j2)
287 EqGroup * pgroup0 = map.find(fd[0].equivs[i2]);
288 EqGroup * pgroup1 = map.find(fd[1].equivs[j2]);
289 if (pgroup0 != pgroup1 || !isLineInDiffBlock(0, i2, script))
291 // pgroup0->m_lines0.Remove(i2); // commented out this line although I'm not sure what this line means because this line causes the bug sf.net#2174
292 // pgroup1->m_lines1.Remove(j2);
296 // Ok, now our moved block is i1->i2,j1->j2
299 assert(i2-i1 == j2-j1);
301 int prefix = j1 - (e->line1);
304 // break e (current change) into two pieces
305 // first part is the prefix, before the moved part
307 // second part is the moved part & anything after it
308 // that goes in newob
309 // leave the left side (e->deleted) on e
310 // so no right side on newob
311 // newob will be the moved part only, later after we split off any suffix from it
312 struct change *newob = (struct change *) xmalloc (sizeof (struct change));
315 newob->line0 = e->line0 + e->deleted;
317 newob->inserted = e->inserted - prefix;
319 newob->link = e->link;
323 e->inserted = prefix;
326 // now make e point to the moved part (& any suffix)
329 // now e points to a moved diff chunk with no prefix, but maybe a suffix
333 int suffix = (e->inserted) - (j2-(e->line1)) - 1;
336 // break off any suffix from e
337 // newob will be the suffix, and will get all the left side
338 struct change *newob = (struct change *) xmalloc (sizeof (struct change));
341 newob->line0 = e->line0;
343 newob->inserted = suffix;
344 newob->deleted = e->deleted;
345 newob->link = e->link;
347 newob->match1 = e->match1;
349 e->inserted -= suffix;
354 p = newob; // next block to scan