1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 package org.eclipse.jgit.diff;
46
47 import java.text.MessageFormat;
48
49 import org.eclipse.jgit.errors.DiffInterruptedException;
50 import org.eclipse.jgit.internal.JGitText;
51 import org.eclipse.jgit.util.IntList;
52 import org.eclipse.jgit.util.LongList;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 @SuppressWarnings("hiding")
114 public class MyersDiff<S extends Sequence> {
115
116 public static final DiffAlgorithm INSTANCE = new LowLevelDiffAlgorithm() {
117 @SuppressWarnings("unused")
118 @Override
119 public <S extends Sequence> void diffNonCommon(EditList edits,
120 HashedSequenceComparator<S> cmp, HashedSequence<S> a,
121 HashedSequence<S> b, Edit region) {
122 new MyersDiff<>(edits, cmp, a, b, region);
123 }
124 };
125
126
127
128
129
130 protected EditList edits;
131
132
133 protected HashedSequenceComparator<S> cmp;
134
135
136
137
138 protected HashedSequence<S> a;
139
140
141
142
143 protected HashedSequence<S> b;
144
145 private MyersDiff(EditList edits, HashedSequenceComparator<S> cmp,
146 HashedSequence<S> a, HashedSequence<S> b, Edit region) {
147 this.edits = edits;
148 this.cmp = cmp;
149 this.a = a;
150 this.b = b;
151 calculateEdits(region);
152 }
153
154
155 MiddleEdit middle = new MiddleEdit();
156
157
158
159
160
161
162 private void calculateEdits(Edit r) {
163 middle.initialize(r.beginA, r.endA, r.beginB, r.endB);
164 if (middle.beginA >= middle.endA &&
165 middle.beginB >= middle.endB)
166 return;
167
168 calculateEdits(middle.beginA, middle.endA,
169 middle.beginB, middle.endB);
170 }
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 protected void calculateEdits(int beginA, int endA,
190 int beginB, int endB) {
191 Edit edit = middle.calculate(beginA, endA, beginB, endB);
192
193 if (beginA < edit.beginA || beginB < edit.beginB) {
194 int k = edit.beginB - edit.beginA;
195 int x = middle.backward.snake(k, edit.beginA);
196 calculateEdits(beginA, x, beginB, k + x);
197 }
198
199 if (edit.getType() != Edit.Type.EMPTY)
200 edits.add(edits.size(), edit);
201
202
203 if (endA > edit.endA || endB > edit.endB) {
204 int k = edit.endB - edit.endA;
205 int x = middle.forward.snake(k, edit.endA);
206 calculateEdits(x, endA, k + x, endB);
207 }
208 }
209
210
211
212
213
214
215
216
217
218
219 class MiddleEdit {
220 void initialize(int beginA, int endA, int beginB, int endB) {
221 this.beginA = beginA; this.endA = endA;
222 this.beginB = beginB; this.endB = endB;
223
224
225 int k = beginB - beginA;
226 this.beginA = forward.snake(k, beginA);
227 this.beginB = k + this.beginA;
228
229 k = endB - endA;
230 this.endA = backward.snake(k, endA);
231 this.endB = k + this.endA;
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245 Edit calculate(int beginA, int endA, int beginB, int endB) {
246 if (beginA == endA || beginB == endB)
247 return new Edit(beginA, endA, beginB, endB);
248 this.beginA = beginA; this.endA = endA;
249 this.beginB = beginB; this.endB = endB;
250
251
252
253
254
255
256 int minK = beginB - endA;
257 int maxK = endB - beginA;
258
259 forward.initialize(beginB - beginA, beginA, minK, maxK);
260 backward.initialize(endB - endA, endA, minK, maxK);
261
262 for (int d = 1; ; d++)
263 if (forward.calculate(d) ||
264 backward.calculate(d))
265 return edit;
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303 EditPaths forward = new ForwardEditPaths();
304 EditPaths backward = new BackwardEditPaths();
305
306
307 protected int beginA, endA, beginB, endB;
308 protected Edit edit;
309
310 abstract class EditPaths {
311 private IntListtList.html#IntList">IntList x = new IntList();
312 private LongListt.html#LongList">LongList snake = new LongList();
313 int beginK, endK, middleK;
314 int prevBeginK, prevEndK;
315
316 int minK, maxK;
317
318 final int getIndex(int d, int k) {
319
320 if (((d + k - middleK) % 2) != 0)
321 throw new RuntimeException(MessageFormat.format(JGitText.get().unexpectedOddResult, Integer.valueOf(d), Integer.valueOf(k), Integer.valueOf(middleK)));
322 return (d + k - middleK) / 2;
323 }
324
325 final int getX(int d, int k) {
326
327 if (k < beginK || k > endK)
328 throw new RuntimeException(MessageFormat.format(JGitText.get().kNotInRange, Integer.valueOf(k), Integer.valueOf(beginK), Integer.valueOf(endK)));
329 return x.get(getIndex(d, k));
330 }
331
332 final long getSnake(int d, int k) {
333
334 if (k < beginK || k > endK)
335 throw new RuntimeException(MessageFormat.format(JGitText.get().kNotInRange, Integer.valueOf(k), Integer.valueOf(beginK), Integer.valueOf(endK)));
336 return snake.get(getIndex(d, k));
337 }
338
339 private int forceKIntoRange(int k) {
340
341 if (k < minK)
342 return minK + ((k ^ minK) & 1);
343 else if (k > maxK)
344 return maxK - ((k ^ maxK) & 1);
345 return k;
346 }
347
348 void initialize(int k, int x, int minK, int maxK) {
349 this.minK = minK;
350 this.maxK = maxK;
351 beginK = endK = middleK = k;
352 this.x.clear();
353 this.x.add(x);
354 snake.clear();
355 snake.add(newSnake(k, x));
356 }
357
358 abstract int snake(int k, int x);
359 abstract int getLeft(int x);
360 abstract int getRight(int x);
361 abstract boolean isBetter(int left, int right);
362 abstract void adjustMinMaxK(int k, int x);
363 abstract boolean meets(int d, int k, int x, long snake);
364
365 final long newSnake(int k, int x) {
366 long y = k + x;
367 long ret = ((long) x) << 32;
368 return ret | y;
369 }
370
371 final int snake2x(long snake) {
372 return (int) (snake >>> 32);
373 }
374
375 final int snake2y(long snake) {
376 return (int) snake;
377 }
378
379 final boolean makeEdit(long snake1, long snake2) {
380 int x1 = snake2x(snake1), x2 = snake2x(snake2);
381 int y1 = snake2y(snake1), y2 = snake2y(snake2);
382
383
384
385
386
387
388
389
390
391
392
393 if (x1 > x2 || y1 > y2) {
394 x1 = x2;
395 y1 = y2;
396 }
397 edit = new Edit(x1, x2, y1, y2);
398 return true;
399 }
400
401 boolean calculate(int d) {
402 prevBeginK = beginK;
403 prevEndK = endK;
404 beginK = forceKIntoRange(middleK - d);
405 endK = forceKIntoRange(middleK + d);
406
407
408
409
410 for (int k = endK; k >= beginK; k -= 2) {
411 if (Thread.interrupted()) {
412 throw new DiffInterruptedException();
413 }
414 int left = -1, right = -1;
415 long leftSnake = -1L, rightSnake = -1L;
416
417 if (k > prevBeginK) {
418 int i = getIndex(d - 1, k - 1);
419 left = x.get(i);
420 int end = snake(k - 1, left);
421 leftSnake = left != end ?
422 newSnake(k - 1, end) :
423 snake.get(i);
424 if (meets(d, k - 1, end, leftSnake))
425 return true;
426 left = getLeft(end);
427 }
428 if (k < prevEndK) {
429 int i = getIndex(d - 1, k + 1);
430 right = x.get(i);
431 int end = snake(k + 1, right);
432 rightSnake = right != end ?
433 newSnake(k + 1, end) :
434 snake.get(i);
435 if (meets(d, k + 1, end, rightSnake))
436 return true;
437 right = getRight(end);
438 }
439 int newX;
440 long newSnake;
441 if (k >= prevEndK ||
442 (k > prevBeginK &&
443 isBetter(left, right))) {
444 newX = left;
445 newSnake = leftSnake;
446 }
447 else {
448 newX = right;
449 newSnake = rightSnake;
450 }
451 if (meets(d, k, newX, newSnake))
452 return true;
453 adjustMinMaxK(k, newX);
454 int i = getIndex(d, k);
455 x.set(i, newX);
456 snake.set(i, newSnake);
457 }
458 return false;
459 }
460 }
461
462 class ForwardEditPaths extends EditPaths {
463 @Override
464 final int snake(int k, int x) {
465 for (; x < endA && k + x < endB; x++)
466 if (!cmp.equals(a, x, b, k + x))
467 break;
468 return x;
469 }
470
471 @Override
472 final int getLeft(int x) {
473 return x;
474 }
475
476 @Override
477 final int getRight(int x) {
478 return x + 1;
479 }
480
481 @Override
482 final boolean isBetter(int left, int right) {
483 return left > right;
484 }
485
486 @Override
487 final void adjustMinMaxK(int k, int x) {
488 if (x >= endA || k + x >= endB) {
489 if (k > backward.middleK)
490 maxK = k;
491 else
492 minK = k;
493 }
494 }
495
496 @Override
497 final boolean meets(int d, int k, int x, long snake) {
498 if (k < backward.beginK || k > backward.endK)
499 return false;
500
501 if (((d - 1 + k - backward.middleK) % 2) != 0)
502 return false;
503 if (x < backward.getX(d - 1, k))
504 return false;
505 makeEdit(snake, backward.getSnake(d - 1, k));
506 return true;
507 }
508 }
509
510 class BackwardEditPaths extends EditPaths {
511 @Override
512 final int snake(int k, int x) {
513 for (; x > beginA && k + x > beginB; x--)
514 if (!cmp.equals(a, x - 1, b, k + x - 1))
515 break;
516 return x;
517 }
518
519 @Override
520 final int getLeft(int x) {
521 return x - 1;
522 }
523
524 @Override
525 final int getRight(int x) {
526 return x;
527 }
528
529 @Override
530 final boolean isBetter(int left, int right) {
531 return left < right;
532 }
533
534 @Override
535 final void adjustMinMaxK(int k, int x) {
536 if (x <= beginA || k + x <= beginB) {
537 if (k > forward.middleK)
538 maxK = k;
539 else
540 minK = k;
541 }
542 }
543
544 @Override
545 final boolean meets(int d, int k, int x, long snake) {
546 if (k < forward.beginK || k > forward.endK)
547 return false;
548
549 if (((d + k - forward.middleK) % 2) != 0)
550 return false;
551 if (x > forward.getX(d, k))
552 return false;
553 makeEdit(forward.getSnake(d, k), snake);
554 return true;
555 }
556 }
557 }
558
559
560
561
562
563
564
565 public static void main(String[] args) {
566 if (args.length != 2) {
567 System.err.println(JGitText.get().need2Arguments);
568 System.exit(1);
569 }
570 try {
571 RawText a = new RawText(new java.io.File(args[0]));
572 RawText b = new RawText(new java.io.File(args[1]));
573 EditList r = INSTANCE.diff(RawTextComparator.DEFAULT, a, b);
574 System.out.println(r.toString());
575 } catch (Exception e) {
576 e.printStackTrace();
577 }
578 }
579 }