1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.diff;
12
13 import org.eclipse.jgit.internal.JGitText;
14
15
16
17
18
19
20
21
22
23
24
25
26
27 final class HistogramDiffIndex<S extends Sequence> {
28 private static final int REC_NEXT_SHIFT = 28 + 8;
29
30 private static final int REC_PTR_SHIFT = 8;
31
32 private static final int REC_PTR_MASK = (1 << 28) - 1;
33
34 private static final int REC_CNT_MASK = (1 << 8) - 1;
35
36 private static final int MAX_PTR = REC_PTR_MASK;
37
38 private static final int MAX_CNT = (1 << 8) - 1;
39
40 private final int maxChainLength;
41
42 private final HashedSequenceComparator<S> cmp;
43
44 private final HashedSequence<S> a;
45
46 private final HashedSequence<S> b;
47
48 private final Edit region;
49
50
51 private final int[] table;
52
53
54 private final int keyShift;
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 private long[] recs;
71
72
73 private int recCnt;
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 private int[] next;
92
93
94
95
96
97
98
99
100
101
102
103 private int[] recIdx;
104
105
106 private int ptrShift;
107
108 private Edit lcs;
109
110 private int cnt;
111
112 private boolean hasCommon;
113
114 HistogramDiffIndex(int maxChainLength, HashedSequenceComparator<S> cmp,
115 HashedSequence<S> a, HashedSequence<S> b, Edit r) {
116 this.maxChainLength = maxChainLength;
117 this.cmp = cmp;
118 this.a = a;
119 this.b = b;
120 this.region = r;
121
122 if (region.endA >= MAX_PTR)
123 throw new IllegalArgumentException(
124 JGitText.get().sequenceTooLargeForDiffAlgorithm);
125
126 final int sz = r.getLengthA();
127 final int tableBits = tableBits(sz);
128 table = new int[1 << tableBits];
129 keyShift = 32 - tableBits;
130 ptrShift = r.beginA;
131
132 recs = new long[Math.max(4, sz >>> 3)];
133 next = new int[sz];
134 recIdx = new int[sz];
135 }
136
137 Edit findLongestCommonSequence() {
138 if (!scanA())
139 return null;
140
141 lcs = new Edit(0, 0);
142 cnt = maxChainLength + 1;
143
144 for (int bPtr = region.beginB; bPtr < region.endB;)
145 bPtr = tryLongestCommonSequence(bPtr);
146
147 return hasCommon && maxChainLength < cnt ? null : lcs;
148 }
149
150 private boolean scanA() {
151
152
153
154
155
156 SCAN: for (int ptr = region.endA - 1; region.beginA <= ptr; ptr--) {
157 final int tIdx = hash(a, ptr);
158
159 int chainLen = 0;
160 for (int rIdx = table[tIdx]; rIdx != 0;) {
161 final long rec = recs[rIdx];
162 if (cmp.equals(a, recPtr(rec), a, ptr)) {
163
164
165
166 int newCnt = recCnt(rec) + 1;
167 if (MAX_CNT < newCnt)
168 newCnt = MAX_CNT;
169 recs[rIdx] = recCreate(recNext(rec), ptr, newCnt);
170 next[ptr - ptrShift] = recPtr(rec);
171 recIdx[ptr - ptrShift] = rIdx;
172 continue SCAN;
173 }
174
175 rIdx = recNext(rec);
176 chainLen++;
177 }
178
179 if (chainLen == maxChainLength)
180 return false;
181
182
183
184
185 final int rIdx = ++recCnt;
186 if (rIdx == recs.length) {
187 int sz = Math.min(recs.length << 1, 1 + region.getLengthA());
188 long[] n = new long[sz];
189 System.arraycopy(recs, 0, n, 0, recs.length);
190 recs = n;
191 }
192
193 recs[rIdx] = recCreate(table[tIdx], ptr, 1);
194 recIdx[ptr - ptrShift] = rIdx;
195 table[tIdx] = rIdx;
196 }
197 return true;
198 }
199
200 private int tryLongestCommonSequence(int bPtr) {
201 int bNext = bPtr + 1;
202 int rIdx = table[hash(b, bPtr)];
203 for (long rec; rIdx != 0; rIdx = recNext(rec)) {
204 rec = recs[rIdx];
205
206
207 if (recCnt(rec) > cnt) {
208 if (!hasCommon)
209 hasCommon = cmp.equals(a, recPtr(rec), b, bPtr);
210 continue;
211 }
212
213 int as = recPtr(rec);
214 if (!cmp.equals(a, as, b, bPtr))
215 continue;
216
217 hasCommon = true;
218 TRY_LOCATIONS: for (;;) {
219 int np = next[as - ptrShift];
220 int bs = bPtr;
221 int ae = as + 1;
222 int be = bs + 1;
223 int rc = recCnt(rec);
224
225 while (region.beginA < as && region.beginB < bs
226 && cmp.equals(a, as - 1, b, bs - 1)) {
227 as--;
228 bs--;
229 if (1 < rc)
230 rc = Math.min(rc, recCnt(recs[recIdx[as - ptrShift]]));
231 }
232 while (ae < region.endA && be < region.endB
233 && cmp.equals(a, ae, b, be)) {
234 if (1 < rc)
235 rc = Math.min(rc, recCnt(recs[recIdx[ae - ptrShift]]));
236 ae++;
237 be++;
238 }
239
240 if (bNext < be)
241 bNext = be;
242 if (lcs.getLengthA() < ae - as || rc < cnt) {
243
244
245
246 lcs.beginA = as;
247 lcs.beginB = bs;
248 lcs.endA = ae;
249 lcs.endB = be;
250 cnt = rc;
251 }
252
253
254
255
256
257
258 if (np == 0)
259 break TRY_LOCATIONS;
260
261 while (np < ae) {
262
263
264
265 np = next[np - ptrShift];
266 if (np == 0)
267 break TRY_LOCATIONS;
268 }
269
270 as = np;
271 }
272 }
273 return bNext;
274 }
275
276 private int hash(HashedSequence<S> s, int idx) {
277 return (cmp.hash(s, idx) * 0x9e370001 ) >>> keyShift;
278 }
279
280 private static long recCreate(int next, int ptr, int cnt) {
281 return ((long) next << REC_NEXT_SHIFT)
282 | ((long) ptr << REC_PTR_SHIFT)
283 | cnt;
284 }
285
286 private static int recNext(long rec) {
287 return (int) (rec >>> REC_NEXT_SHIFT);
288 }
289
290 private static int recPtr(long rec) {
291 return ((int) (rec >>> REC_PTR_SHIFT)) & REC_PTR_MASK;
292 }
293
294 private static int recCnt(long rec) {
295 return ((int) rec) & REC_CNT_MASK;
296 }
297
298 private static int tableBits(int sz) {
299 int bits = 31 - Integer.numberOfLeadingZeros(sz);
300 if (bits == 0)
301 bits = 1;
302 if (1 << bits < sz)
303 bits++;
304 return bits;
305 }
306 }