1 /////////////////////////////////////////////////////////////////////////////
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 2 of the License, or (at
6 // your option) any later version.
8 // This program is distributed in the hope that it will be useful, but
9 // WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 /////////////////////////////////////////////////////////////////////////////
18 * @file MovedBlocks.cpp
20 * @brief Moved block detection code.
30 void Add(int val) { m_map[val] = 1; }
31 void Remove(int val) { m_map.erase(val); }
32 size_t count() const { return m_map.size(); }
33 bool isPresent(int val) const { return m_map.find(val) != m_map.end(); }
34 int getSingle() const { return m_map.begin()->first; }
36 std::map<int, int> m_map;
40 * @brief Set of equivalent lines
41 * This uses diffutils line numbers, which are counted from the prefix
45 IntSet m_lines0; // equivalent lines on side#0
46 IntSet m_lines1; // equivalent lines on side#1
48 bool isPerfectMatch() const { return m_lines0.count()==1 && m_lines1.count()==1; }
52 /** @brief Maps equivalency code to equivalency group */
56 std::map<int, EqGroup *> m_map;
57 /** @brief Add a line to the appropriate equivalency group */
58 void Add(int lineno, int eqcode, int nside)
60 EqGroup *pgroup = find(eqcode);
64 m_map[eqcode] = pgroup;
67 pgroup->m_lines1.Add(lineno);
69 pgroup->m_lines0.Add(lineno);
72 /** @brief Return the appropriate equivalency group */
73 EqGroup * find(int eqcode)
75 std::map<int, EqGroup *>::const_iterator it = m_map.find(eqcode);
76 return it != m_map.end() ? it->second : NULL;
81 std::map<int, EqGroup *>::const_iterator pos = m_map.begin();
82 while (pos != m_map.end())
88 WinMerge moved block code
89 This is called by diffutils code, by diff_2_files routine (in ANALYZE.C)
90 read_files earlier computed the hash chains ("equivs" file variable) and freed them,
91 but the equivs numerics are still available in each line
93 match1 set by scan from line0 to deleted
94 match0 set by scan from line1 to inserted
97 extern "C" void moved_block_analysis(struct change ** pscript, struct file_data fd[])
99 // Hash all altered lines
102 struct change * script = *pscript;
104 for (e = script; e; e = p)
108 for (i=e->line0; i-(e->line0) < (e->deleted); ++i)
109 map.Add(i, fd[0].equivs[i], 0);
110 for (i=e->line1; i-(e->line1) < (e->inserted); ++i)
111 map.Add(i, fd[1].equivs[i], 1);
115 // Scan through diff blocks, finding moved sections from left side
116 // and splitting them out
117 // That is, we actually fragment diff blocks as we find moved sections
118 for (e = script; e; e = p)
120 // scan down block for a match
122 EqGroup * pgroup = 0;
124 for (i=e->line0; i-(e->line0) < (e->deleted); ++i)
126 EqGroup * tempgroup = map.find(fd[0].equivs[i]);
127 if (tempgroup->isPerfectMatch())
134 // if no match, go to next diff block
139 int j = pgroup->m_lines1.getSingle();
140 // Ok, now our moved block is the single line i,j
142 // extend moved block upward as far as possible
145 for ( ; i1>=e->line0; --i1, --j1)
147 EqGroup * pgroup0 = map.find(fd[0].equivs[i1]);
148 EqGroup * pgroup1 = map.find(fd[1].equivs[j1]);
149 if (pgroup0 != pgroup1)
151 // 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
152 // pgroup1->m_lines1.Remove(j1);
156 // Ok, now our moved block is i1->i, j1->j
158 // extend moved block downward as far as possible
161 for ( ; i2-(e->line0) < (e->deleted); ++i2,++j2)
163 EqGroup * pgroup0 = map.find(fd[0].equivs[i2]);
164 EqGroup * pgroup1 = map.find(fd[1].equivs[j2]);
165 if (pgroup0 != pgroup1)
167 // 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
168 // pgroup1->m_lines1.Remove(j2);
172 // Ok, now our moved block is i1->i2,j1->j2
175 assert(i2-i1 == j2-j1);
177 int prefix = i1 - (e->line0);
180 // break e (current change) into two pieces
181 // first part is the prefix, before the moved part
183 // second part is the moved part & anything after it
184 // that goes in newob
185 // leave the right side (e->inserted) on e
186 // so no right side on newob
187 // newob will be the moved part only, later after we split off any suffix from it
188 struct change *newob = (struct change *) xmalloc (sizeof (struct change));
189 memset(newob, 0, sizeof(*newob));
192 newob->line1 = e->line1 + e->inserted;
194 newob->deleted = e->deleted - prefix;
195 newob->link = e->link;
202 // now make e point to the moved part (& any suffix)
205 // now e points to a moved diff chunk with no prefix, but maybe a suffix
209 int suffix = (e->deleted) - (i2-(e->line0)) - 1;
212 // break off any suffix from e
213 // newob will be the suffix, and will get all the right side
214 struct change *newob = (struct change *) xmalloc (sizeof (struct change));
215 memset(newob, 0, sizeof(*newob));
218 newob->line1 = e->line1;
219 newob->inserted = e->inserted;
220 newob->deleted = suffix;
221 newob->link = e->link;
226 e->deleted -= suffix;
229 p = newob; // next block to scan
233 // Scan through diff blocks, finding moved sections from right side
234 // and splitting them out
235 // That is, we actually fragment diff blocks as we find moved sections
236 for (e = script; e; e = p)
238 // scan down block for a match
240 EqGroup * pgroup = 0;
242 for (j=e->line1; j-(e->line1) < (e->inserted); ++j)
244 EqGroup * tempgroup = map.find(fd[1].equivs[j]);
245 if (tempgroup->isPerfectMatch())
252 // if no match, go to next diff block
257 int i = pgroup->m_lines0.getSingle();
258 // Ok, now our moved block is the single line i,j
260 // extend moved block upward as far as possible
263 for ( ; j1>=e->line1; --i1, --j1)
265 EqGroup * pgroup0 = map.find(fd[0].equivs[i1]);
266 EqGroup * pgroup1 = map.find(fd[1].equivs[j1]);
267 if (pgroup0 != pgroup1)
269 // 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
270 // pgroup1->m_lines1.Remove(j1);
274 // Ok, now our moved block is i1->i, j1->j
276 // extend moved block downward as far as possible
279 for ( ; j2-(e->line1) < (e->inserted); ++i2,++j2)
281 EqGroup * pgroup0 = map.find(fd[0].equivs[i2]);
282 EqGroup * pgroup1 = map.find(fd[1].equivs[j2]);
283 if (pgroup0 != pgroup1)
285 // 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
286 // pgroup1->m_lines1.Remove(j2);
290 // Ok, now our moved block is i1->i2,j1->j2
293 assert(i2-i1 == j2-j1);
295 int prefix = j1 - (e->line1);
298 // break e (current change) into two pieces
299 // first part is the prefix, before the moved part
301 // second part is the moved part & anything after it
302 // that goes in newob
303 // leave the left side (e->deleted) on e
304 // so no right side on newob
305 // newob will be the moved part only, later after we split off any suffix from it
306 struct change *newob = (struct change *) xmalloc (sizeof (struct change));
307 memset(newob, 0, sizeof(*newob));
309 newob->line0 = e->line0 + e->deleted;
311 newob->inserted = e->inserted - prefix;
313 newob->link = e->link;
317 e->inserted = prefix;
320 // now make e point to the moved part (& any suffix)
323 // now e points to a moved diff chunk with no prefix, but maybe a suffix
327 int suffix = (e->inserted) - (j2-(e->line1)) - 1;
330 // break off any suffix from e
331 // newob will be the suffix, and will get all the left side
332 struct change *newob = (struct change *) xmalloc (sizeof (struct change));
333 memset(newob, 0, sizeof(*newob));
335 newob->line0 = e->line0;
337 newob->inserted = suffix;
338 newob->deleted = e->deleted;
339 newob->link = e->link;
341 newob->match1 = e->match1;
343 e->inserted -= suffix;
348 p = newob; // next block to scan