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