View Javadoc
1   /*
2    * Copyright (C) 2009-2010, Google Inc.
3    * Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de> and others
4    *
5    * This program and the accompanying materials are made available under the
6    * terms of the Eclipse Distribution License v. 1.0 which is available at
7    * https://www.eclipse.org/org/documents/edl-v10.php.
8    *
9    * SPDX-License-Identifier: BSD-3-Clause
10   */
11  
12  package org.eclipse.jgit.diff;
13  
14  import static org.eclipse.jgit.util.RawCharUtil.isWhitespace;
15  import static org.eclipse.jgit.util.RawCharUtil.trimLeadingWhitespace;
16  import static org.eclipse.jgit.util.RawCharUtil.trimTrailingWhitespace;
17  
18  import org.eclipse.jgit.util.IntList;
19  
20  /**
21   * Equivalence function for {@link org.eclipse.jgit.diff.RawText}.
22   */
23  public abstract class RawTextComparator extends SequenceComparator<RawText> {
24  	/** No special treatment. */
25  	public static final RawTextComparatorhtml#RawTextComparator">RawTextComparator DEFAULT = new RawTextComparator() {
26  		@Override
27  		public boolean equals(RawTextxt.html#RawText">RawText a, int ai, RawText b, int bi) {
28  			ai++;
29  			bi++;
30  
31  			int as = a.lines.get(ai);
32  			int bs = b.lines.get(bi);
33  			final int ae = a.lines.get(ai + 1);
34  			final int be = b.lines.get(bi + 1);
35  
36  			if (ae - as != be - bs)
37  				return false;
38  
39  			while (as < ae) {
40  				if (a.content[as++] != b.content[bs++])
41  					return false;
42  			}
43  			return true;
44  		}
45  
46  		@Override
47  		protected int hashRegion(byte[] raw, int ptr, int end) {
48  			int hash = 5381;
49  			for (; ptr < end; ptr++)
50  				hash = ((hash << 5) + hash) + (raw[ptr] & 0xff);
51  			return hash;
52  		}
53  	};
54  
55  	/** Ignores all whitespace. */
56  	public static final RawTextComparatorawTextComparator">RawTextComparator WS_IGNORE_ALL = new RawTextComparator() {
57  		@Override
58  		public boolean equals(RawTextxt.html#RawText">RawText a, int ai, RawText b, int bi) {
59  			ai++;
60  			bi++;
61  
62  			int as = a.lines.get(ai);
63  			int bs = b.lines.get(bi);
64  			int ae = a.lines.get(ai + 1);
65  			int be = b.lines.get(bi + 1);
66  
67  			ae = trimTrailingWhitespace(a.content, as, ae);
68  			be = trimTrailingWhitespace(b.content, bs, be);
69  
70  			while (as < ae && bs < be) {
71  				byte ac = a.content[as];
72  				byte bc = b.content[bs];
73  
74  				while (as < ae - 1 && isWhitespace(ac)) {
75  					as++;
76  					ac = a.content[as];
77  				}
78  
79  				while (bs < be - 1 && isWhitespace(bc)) {
80  					bs++;
81  					bc = b.content[bs];
82  				}
83  
84  				if (ac != bc)
85  					return false;
86  
87  				as++;
88  				bs++;
89  			}
90  
91  			return as == ae && bs == be;
92  		}
93  
94  		@Override
95  		protected int hashRegion(byte[] raw, int ptr, int end) {
96  			int hash = 5381;
97  			for (; ptr < end; ptr++) {
98  				byte c = raw[ptr];
99  				if (!isWhitespace(c))
100 					hash = ((hash << 5) + hash) + (c & 0xff);
101 			}
102 			return hash;
103 		}
104 	};
105 
106 	/**
107 	 * Ignore leading whitespace.
108 	 **/
109 	public static final RawTextComparatorxtComparator">RawTextComparator WS_IGNORE_LEADING = new RawTextComparator() {
110 		@Override
111 		public boolean equals(RawTextxt.html#RawText">RawText a, int ai, RawText b, int bi) {
112 			ai++;
113 			bi++;
114 
115 			int as = a.lines.get(ai);
116 			int bs = b.lines.get(bi);
117 			int ae = a.lines.get(ai + 1);
118 			int be = b.lines.get(bi + 1);
119 
120 			as = trimLeadingWhitespace(a.content, as, ae);
121 			bs = trimLeadingWhitespace(b.content, bs, be);
122 
123 			if (ae - as != be - bs)
124 				return false;
125 
126 			while (as < ae) {
127 				if (a.content[as++] != b.content[bs++])
128 					return false;
129 			}
130 			return true;
131 		}
132 
133 		@Override
134 		protected int hashRegion(byte[] raw, int ptr, int end) {
135 			int hash = 5381;
136 			ptr = trimLeadingWhitespace(raw, ptr, end);
137 			for (; ptr < end; ptr++)
138 				hash = ((hash << 5) + hash) + (raw[ptr] & 0xff);
139 			return hash;
140 		}
141 	};
142 
143 	/** Ignores trailing whitespace. */
144 	public static final RawTextComparatortComparator">RawTextComparator WS_IGNORE_TRAILING = new RawTextComparator() {
145 		@Override
146 		public boolean equals(RawTextxt.html#RawText">RawText a, int ai, RawText b, int bi) {
147 			ai++;
148 			bi++;
149 
150 			int as = a.lines.get(ai);
151 			int bs = b.lines.get(bi);
152 			int ae = a.lines.get(ai + 1);
153 			int be = b.lines.get(bi + 1);
154 
155 			ae = trimTrailingWhitespace(a.content, as, ae);
156 			be = trimTrailingWhitespace(b.content, bs, be);
157 
158 			if (ae - as != be - bs)
159 				return false;
160 
161 			while (as < ae) {
162 				if (a.content[as++] != b.content[bs++])
163 					return false;
164 			}
165 			return true;
166 		}
167 
168 		@Override
169 		protected int hashRegion(byte[] raw, int ptr, int end) {
170 			int hash = 5381;
171 			end = trimTrailingWhitespace(raw, ptr, end);
172 			for (; ptr < end; ptr++)
173 				hash = ((hash << 5) + hash) + (raw[ptr] & 0xff);
174 			return hash;
175 		}
176 	};
177 
178 	/** Ignores whitespace occurring between non-whitespace characters. */
179 	public static final RawTextComparatorextComparator">RawTextComparator WS_IGNORE_CHANGE = new RawTextComparator() {
180 		@Override
181 		public boolean equals(RawTextxt.html#RawText">RawText a, int ai, RawText b, int bi) {
182 			ai++;
183 			bi++;
184 
185 			int as = a.lines.get(ai);
186 			int bs = b.lines.get(bi);
187 			int ae = a.lines.get(ai + 1);
188 			int be = b.lines.get(bi + 1);
189 
190 			ae = trimTrailingWhitespace(a.content, as, ae);
191 			be = trimTrailingWhitespace(b.content, bs, be);
192 
193 			while (as < ae && bs < be) {
194 				byte ac = a.content[as];
195 				byte bc = b.content[bs];
196 
197 				if (ac != bc)
198 					return false;
199 
200 				if (isWhitespace(ac))
201 					as = trimLeadingWhitespace(a.content, as, ae);
202 				else
203 					as++;
204 
205 				if (isWhitespace(bc))
206 					bs = trimLeadingWhitespace(b.content, bs, be);
207 				else
208 					bs++;
209 			}
210 			return as == ae && bs == be;
211 		}
212 
213 		@Override
214 		protected int hashRegion(byte[] raw, int ptr, int end) {
215 			int hash = 5381;
216 			end = trimTrailingWhitespace(raw, ptr, end);
217 			while (ptr < end) {
218 				byte c = raw[ptr];
219 				hash = ((hash << 5) + hash) + (c & 0xff);
220 				if (isWhitespace(c))
221 					ptr = trimLeadingWhitespace(raw, ptr, end);
222 				else
223 					ptr++;
224 			}
225 			return hash;
226 		}
227 	};
228 
229 	@Override
230 	public int hash(RawText seq, int lno) {
231 		final int begin = seq.lines.get(lno + 1);
232 		final int end = seq.lines.get(lno + 2);
233 		return hashRegion(seq.content, begin, end);
234 	}
235 
236 	/** {@inheritDoc} */
237 	@Override
238 	public Editff/Edit.html#Edit">Edit reduceCommonStartEnd(RawTexthref="../../../../org/eclipse/jgit/diff/RawText.html#RawText">RawText a, RawText b, Edit e) {
239 		// This is a faster exact match based form that tries to improve
240 		// performance for the common case of the header and trailer of
241 		// a text file not changing at all. After this fast path we use
242 		// the slower path based on the super class' using equals() to
243 		// allow for whitespace ignore modes to still work.
244 
245 		if (e.beginA == e.endA || e.beginB == e.endB)
246 			return e;
247 
248 		byte[] aRaw = a.content;
249 		byte[] bRaw = b.content;
250 
251 		int aPtr = a.lines.get(e.beginA + 1);
252 		int bPtr = a.lines.get(e.beginB + 1);
253 
254 		int aEnd = a.lines.get(e.endA + 1);
255 		int bEnd = b.lines.get(e.endB + 1);
256 
257 		// This can never happen, but the JIT doesn't know that. If we
258 		// define this assertion before the tight while loops below it
259 		// should be able to skip the array bound checks on access.
260 		//
261 		if (aPtr < 0 || bPtr < 0 || aEnd > aRaw.length || bEnd > bRaw.length)
262 			throw new ArrayIndexOutOfBoundsException();
263 
264 		while (aPtr < aEnd && bPtr < bEnd && aRaw[aPtr] == bRaw[bPtr]) {
265 			aPtr++;
266 			bPtr++;
267 		}
268 
269 		while (aPtr < aEnd && bPtr < bEnd && aRaw[aEnd - 1] == bRaw[bEnd - 1]) {
270 			aEnd--;
271 			bEnd--;
272 		}
273 
274 		e.beginA = findForwardLine(a.lines, e.beginA, aPtr);
275 		e.beginB = findForwardLine(b.lines, e.beginB, bPtr);
276 
277 		e.endA = findReverseLine(a.lines, e.endA, aEnd);
278 
279 		final boolean partialA = aEnd < a.lines.get(e.endA + 1);
280 		if (partialA)
281 			bEnd += a.lines.get(e.endA + 1) - aEnd;
282 
283 		e.endB = findReverseLine(b.lines, e.endB, bEnd);
284 
285 		if (!partialA && bEnd < b.lines.get(e.endB + 1))
286 			e.endA++;
287 
288 		return super.reduceCommonStartEnd(a, b, e);
289 	}
290 
291 	private static int findForwardLine(IntList lines, int idx, int ptr) {
292 		final int end = lines.size() - 2;
293 		while (idx < end && lines.get(idx + 2) < ptr)
294 			idx++;
295 		return idx;
296 	}
297 
298 	private static int findReverseLine(IntList lines, int idx, int ptr) {
299 		while (0 < idx && ptr <= lines.get(idx))
300 			idx--;
301 		return idx;
302 	}
303 
304 	/**
305 	 * Compute a hash code for a region.
306 	 *
307 	 * @param raw
308 	 *            the raw file content.
309 	 * @param ptr
310 	 *            first byte of the region to hash.
311 	 * @param end
312 	 *            1 past the last byte of the region.
313 	 * @return hash code for the region <code>[ptr, end)</code> of raw.
314 	 */
315 	protected abstract int hashRegion(byte[] raw, int ptr, int end);
316 }