View Javadoc
1   /*
2    * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.lib;
12  
13  import java.io.IOException;
14  import java.io.OutputStream;
15  import java.io.Writer;
16  import java.nio.ByteBuffer;
17  
18  import org.eclipse.jgit.util.NB;
19  import org.eclipse.jgit.util.References;
20  
21  /**
22   * A (possibly mutable) SHA-1 abstraction.
23   * <p>
24   * If this is an instance of {@link org.eclipse.jgit.lib.MutableObjectId} the
25   * concept of equality with this instance can alter at any time, if this
26   * instance is modified to represent a different object name.
27   */
28  public abstract class AnyObjectId implements Comparable<AnyObjectId> {
29  
30  	/**
31  	 * Compare two object identifier byte sequences for equality.
32  	 *
33  	 * @param firstObjectId
34  	 *            the first identifier to compare. Must not be null.
35  	 * @param secondObjectId
36  	 *            the second identifier to compare. Must not be null.
37  	 * @return true if the two identifiers are the same.
38  	 * @deprecated use {@link #isEqual(AnyObjectId, AnyObjectId)} instead
39  	 */
40  	@Deprecated
41  	@SuppressWarnings("AmbiguousMethodReference")
42  	public static boolean equals(final AnyObjectId firstObjectId,
43  			final AnyObjectId secondObjectId) {
44  		return isEqual(firstObjectId, secondObjectId);
45  	}
46  
47  	/**
48  	 * Compare two object identifier byte sequences for equality.
49  	 *
50  	 * @param firstObjectId
51  	 *            the first identifier to compare. Must not be null.
52  	 * @param secondObjectId
53  	 *            the second identifier to compare. Must not be null.
54  	 * @return true if the two identifiers are the same.
55  	 * @since 5.4
56  	 */
57  	public static boolean isEqual(final AnyObjectId firstObjectId,
58  			final AnyObjectId secondObjectId) {
59  		if (References.isSameObject(firstObjectId, secondObjectId)) {
60  			return true;
61  		}
62  		// We test word 3 first since the git file-based ODB
63  		// uses the first byte of w1, and we use w2 as the
64  		// hash code, one of those probably came up with these
65  		// two instances which we are comparing for equality.
66  		// Therefore the first two words are very likely to be
67  		// identical. We want to break away from collisions as
68  		// quickly as possible.
69  		return firstObjectId.w3 == secondObjectId.w3
70  				&& firstObjectId.w4 == secondObjectId.w4
71  				&& firstObjectId.w5 == secondObjectId.w5
72  				&& firstObjectId.w1 == secondObjectId.w1
73  				&& firstObjectId.w2 == secondObjectId.w2;
74  	}
75  
76  	int w1;
77  
78  	int w2;
79  
80  	int w3;
81  
82  	int w4;
83  
84  	int w5;
85  
86  	/**
87  	 * Get the first 8 bits of the ObjectId.
88  	 *
89  	 * This is a faster version of {@code getByte(0)}.
90  	 *
91  	 * @return a discriminator usable for a fan-out style map. Returned values
92  	 *         are unsigned and thus are in the range [0,255] rather than the
93  	 *         signed byte range of [-128, 127].
94  	 */
95  	public final int getFirstByte() {
96  		return w1 >>> 24;
97  	}
98  
99  	/**
100 	 * Get any byte from the ObjectId.
101 	 *
102 	 * Callers hard-coding {@code getByte(0)} should instead use the much faster
103 	 * special case variant {@link #getFirstByte()}.
104 	 *
105 	 * @param index
106 	 *            index of the byte to obtain from the raw form of the ObjectId.
107 	 *            Must be in range [0,
108 	 *            {@link org.eclipse.jgit.lib.Constants#OBJECT_ID_LENGTH}).
109 	 * @return the value of the requested byte at {@code index}. Returned values
110 	 *         are unsigned and thus are in the range [0,255] rather than the
111 	 *         signed byte range of [-128, 127].
112 	 * @throws java.lang.ArrayIndexOutOfBoundsException
113 	 *             {@code index} is less than 0, equal to
114 	 *             {@link org.eclipse.jgit.lib.Constants#OBJECT_ID_LENGTH}, or
115 	 *             greater than
116 	 *             {@link org.eclipse.jgit.lib.Constants#OBJECT_ID_LENGTH}.
117 	 */
118 	public final int getByte(int index) {
119 		int w;
120 		switch (index >> 2) {
121 		case 0:
122 			w = w1;
123 			break;
124 		case 1:
125 			w = w2;
126 			break;
127 		case 2:
128 			w = w3;
129 			break;
130 		case 3:
131 			w = w4;
132 			break;
133 		case 4:
134 			w = w5;
135 			break;
136 		default:
137 			throw new ArrayIndexOutOfBoundsException(index);
138 		}
139 
140 		return (w >>> (8 * (3 - (index & 3)))) & 0xff;
141 	}
142 
143 	/**
144 	 * {@inheritDoc}
145 	 * <p>
146 	 * Compare this ObjectId to another and obtain a sort ordering.
147 	 */
148 	@Override
149 	public final int compareTo(AnyObjectId other) {
150 		if (this == other)
151 			return 0;
152 
153 		int cmp;
154 
155 		cmp = NB.compareUInt32(w1, other.w1);
156 		if (cmp != 0)
157 			return cmp;
158 
159 		cmp = NB.compareUInt32(w2, other.w2);
160 		if (cmp != 0)
161 			return cmp;
162 
163 		cmp = NB.compareUInt32(w3, other.w3);
164 		if (cmp != 0)
165 			return cmp;
166 
167 		cmp = NB.compareUInt32(w4, other.w4);
168 		if (cmp != 0)
169 			return cmp;
170 
171 		return NB.compareUInt32(w5, other.w5);
172 	}
173 
174 	/**
175 	 * Compare this ObjectId to a network-byte-order ObjectId.
176 	 *
177 	 * @param bs
178 	 *            array containing the other ObjectId in network byte order.
179 	 * @param p
180 	 *            position within {@code bs} to start the compare at. At least
181 	 *            20 bytes, starting at this position are required.
182 	 * @return a negative integer, zero, or a positive integer as this object is
183 	 *         less than, equal to, or greater than the specified object.
184 	 */
185 	public final int compareTo(byte[] bs, int p) {
186 		int cmp;
187 
188 		cmp = NB.compareUInt32(w1, NB.decodeInt32(bs, p));
189 		if (cmp != 0)
190 			return cmp;
191 
192 		cmp = NB.compareUInt32(w2, NB.decodeInt32(bs, p + 4));
193 		if (cmp != 0)
194 			return cmp;
195 
196 		cmp = NB.compareUInt32(w3, NB.decodeInt32(bs, p + 8));
197 		if (cmp != 0)
198 			return cmp;
199 
200 		cmp = NB.compareUInt32(w4, NB.decodeInt32(bs, p + 12));
201 		if (cmp != 0)
202 			return cmp;
203 
204 		return NB.compareUInt32(w5, NB.decodeInt32(bs, p + 16));
205 	}
206 
207 	/**
208 	 * Compare this ObjectId to a network-byte-order ObjectId.
209 	 *
210 	 * @param bs
211 	 *            array containing the other ObjectId in network byte order.
212 	 * @param p
213 	 *            position within {@code bs} to start the compare at. At least 5
214 	 *            integers, starting at this position are required.
215 	 * @return a negative integer, zero, or a positive integer as this object is
216 	 *         less than, equal to, or greater than the specified object.
217 	 */
218 	public final int compareTo(int[] bs, int p) {
219 		int cmp;
220 
221 		cmp = NB.compareUInt32(w1, bs[p]);
222 		if (cmp != 0)
223 			return cmp;
224 
225 		cmp = NB.compareUInt32(w2, bs[p + 1]);
226 		if (cmp != 0)
227 			return cmp;
228 
229 		cmp = NB.compareUInt32(w3, bs[p + 2]);
230 		if (cmp != 0)
231 			return cmp;
232 
233 		cmp = NB.compareUInt32(w4, bs[p + 3]);
234 		if (cmp != 0)
235 			return cmp;
236 
237 		return NB.compareUInt32(w5, bs[p + 4]);
238 	}
239 
240 	/**
241 	 * Tests if this ObjectId starts with the given abbreviation.
242 	 *
243 	 * @param abbr
244 	 *            the abbreviation.
245 	 * @return true if this ObjectId begins with the abbreviation; else false.
246 	 */
247 	public boolean startsWith(AbbreviatedObjectId abbr) {
248 		return abbr.prefixCompare(this) == 0;
249 	}
250 
251 	/** {@inheritDoc} */
252 	@Override
253 	public final int hashCode() {
254 		return w2;
255 	}
256 
257 	/**
258 	 * Determine if this ObjectId has exactly the same value as another.
259 	 *
260 	 * @param other
261 	 *            the other id to compare to. May be null.
262 	 * @return true only if both ObjectIds have identical bits.
263 	 */
264 	@SuppressWarnings({ "NonOverridingEquals", "AmbiguousMethodReference" })
265 	public final boolean equals(AnyObjectId other) {
266 		return other != null ? isEqual(this, other) : false;
267 	}
268 
269 	/** {@inheritDoc} */
270 	@Override
271 	public final boolean equals(Object o) {
272 		if (o instanceof AnyObjectId) {
273 			return equals((AnyObjectId) o);
274 		}
275 		return false;
276 	}
277 
278 	/**
279 	 * Copy this ObjectId to an output writer in raw binary.
280 	 *
281 	 * @param w
282 	 *            the buffer to copy to. Must be in big endian order.
283 	 */
284 	public void copyRawTo(ByteBuffer w) {
285 		w.putInt(w1);
286 		w.putInt(w2);
287 		w.putInt(w3);
288 		w.putInt(w4);
289 		w.putInt(w5);
290 	}
291 
292 	/**
293 	 * Copy this ObjectId to a byte array.
294 	 *
295 	 * @param b
296 	 *            the buffer to copy to.
297 	 * @param o
298 	 *            the offset within b to write at.
299 	 */
300 	public void copyRawTo(byte[] b, int o) {
301 		NB.encodeInt32(b, o, w1);
302 		NB.encodeInt32(b, o + 4, w2);
303 		NB.encodeInt32(b, o + 8, w3);
304 		NB.encodeInt32(b, o + 12, w4);
305 		NB.encodeInt32(b, o + 16, w5);
306 	}
307 
308 	/**
309 	 * Copy this ObjectId to an int array.
310 	 *
311 	 * @param b
312 	 *            the buffer to copy to.
313 	 * @param o
314 	 *            the offset within b to write at.
315 	 */
316 	public void copyRawTo(int[] b, int o) {
317 		b[o] = w1;
318 		b[o + 1] = w2;
319 		b[o + 2] = w3;
320 		b[o + 3] = w4;
321 		b[o + 4] = w5;
322 	}
323 
324 	/**
325 	 * Copy this ObjectId to an output writer in raw binary.
326 	 *
327 	 * @param w
328 	 *            the stream to write to.
329 	 * @throws java.io.IOException
330 	 *             the stream writing failed.
331 	 */
332 	public void copyRawTo(OutputStream w) throws IOException {
333 		writeRawInt(w, w1);
334 		writeRawInt(w, w2);
335 		writeRawInt(w, w3);
336 		writeRawInt(w, w4);
337 		writeRawInt(w, w5);
338 	}
339 
340 	private static void writeRawInt(OutputStream w, int v)
341 			throws IOException {
342 		w.write(v >>> 24);
343 		w.write(v >>> 16);
344 		w.write(v >>> 8);
345 		w.write(v);
346 	}
347 
348 	/**
349 	 * Copy this ObjectId to an output writer in hex format.
350 	 *
351 	 * @param w
352 	 *            the stream to copy to.
353 	 * @throws java.io.IOException
354 	 *             the stream writing failed.
355 	 */
356 	public void copyTo(OutputStream w) throws IOException {
357 		w.write(toHexByteArray());
358 	}
359 
360 	/**
361 	 * Copy this ObjectId to a byte array in hex format.
362 	 *
363 	 * @param b
364 	 *            the buffer to copy to.
365 	 * @param o
366 	 *            the offset within b to write at.
367 	 */
368 	public void copyTo(byte[] b, int o) {
369 		formatHexByte(b, o + 0, w1);
370 		formatHexByte(b, o + 8, w2);
371 		formatHexByte(b, o + 16, w3);
372 		formatHexByte(b, o + 24, w4);
373 		formatHexByte(b, o + 32, w5);
374 	}
375 
376 	/**
377 	 * Copy this ObjectId to a ByteBuffer in hex format.
378 	 *
379 	 * @param b
380 	 *            the buffer to copy to.
381 	 */
382 	public void copyTo(ByteBuffer b) {
383 		b.put(toHexByteArray());
384 	}
385 
386 	private byte[] toHexByteArray() {
387 		final byte[] dst = new byte[Constants.OBJECT_ID_STRING_LENGTH];
388 		formatHexByte(dst, 0, w1);
389 		formatHexByte(dst, 8, w2);
390 		formatHexByte(dst, 16, w3);
391 		formatHexByte(dst, 24, w4);
392 		formatHexByte(dst, 32, w5);
393 		return dst;
394 	}
395 
396 	private static final byte[] hexbyte = { '0', '1', '2', '3', '4', '5', '6',
397 			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
398 
399 	private static void formatHexByte(byte[] dst, int p, int w) {
400 		int o = p + 7;
401 		while (o >= p && w != 0) {
402 			dst[o--] = hexbyte[w & 0xf];
403 			w >>>= 4;
404 		}
405 		while (o >= p)
406 			dst[o--] = '0';
407 	}
408 
409 	/**
410 	 * Copy this ObjectId to an output writer in hex format.
411 	 *
412 	 * @param w
413 	 *            the stream to copy to.
414 	 * @throws java.io.IOException
415 	 *             the stream writing failed.
416 	 */
417 	public void copyTo(Writer w) throws IOException {
418 		w.write(toHexCharArray());
419 	}
420 
421 	/**
422 	 * Copy this ObjectId to an output writer in hex format.
423 	 *
424 	 * @param tmp
425 	 *            temporary char array to buffer construct into before writing.
426 	 *            Must be at least large enough to hold 2 digits for each byte
427 	 *            of object id (40 characters or larger).
428 	 * @param w
429 	 *            the stream to copy to.
430 	 * @throws java.io.IOException
431 	 *             the stream writing failed.
432 	 */
433 	public void copyTo(char[] tmp, Writer w) throws IOException {
434 		toHexCharArray(tmp);
435 		w.write(tmp, 0, Constants.OBJECT_ID_STRING_LENGTH);
436 	}
437 
438 	/**
439 	 * Copy this ObjectId to a StringBuilder in hex format.
440 	 *
441 	 * @param tmp
442 	 *            temporary char array to buffer construct into before writing.
443 	 *            Must be at least large enough to hold 2 digits for each byte
444 	 *            of object id (40 characters or larger).
445 	 * @param w
446 	 *            the string to append onto.
447 	 */
448 	public void copyTo(char[] tmp, StringBuilder w) {
449 		toHexCharArray(tmp);
450 		w.append(tmp, 0, Constants.OBJECT_ID_STRING_LENGTH);
451 	}
452 
453 	private char[] toHexCharArray() {
454 		final char[] dst = new char[Constants.OBJECT_ID_STRING_LENGTH];
455 		toHexCharArray(dst);
456 		return dst;
457 	}
458 
459 	private void toHexCharArray(char[] dst) {
460 		formatHexChar(dst, 0, w1);
461 		formatHexChar(dst, 8, w2);
462 		formatHexChar(dst, 16, w3);
463 		formatHexChar(dst, 24, w4);
464 		formatHexChar(dst, 32, w5);
465 	}
466 
467 	private static final char[] hexchar = { '0', '1', '2', '3', '4', '5', '6',
468 			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
469 
470 	static void formatHexChar(char[] dst, int p, int w) {
471 		int o = p + 7;
472 		while (o >= p && w != 0) {
473 			dst[o--] = hexchar[w & 0xf];
474 			w >>>= 4;
475 		}
476 		while (o >= p)
477 			dst[o--] = '0';
478 	}
479 
480 	/** {@inheritDoc} */
481 	@SuppressWarnings("nls")
482 	@Override
483 	public String toString() {
484 		return "AnyObjectId[" + name() + "]";
485 	}
486 
487 	/**
488 	 * <p>name.</p>
489 	 *
490 	 * @return string form of the SHA-1, in lower case hexadecimal.
491 	 */
492 	public final String name() {
493 		return new String(toHexCharArray());
494 	}
495 
496 	/**
497 	 * Get string form of the SHA-1, in lower case hexadecimal.
498 	 *
499 	 * @return string form of the SHA-1, in lower case hexadecimal.
500 	 */
501 	public final String getName() {
502 		return name();
503 	}
504 
505 	/**
506 	 * Return an abbreviation (prefix) of this object SHA-1.
507 	 * <p>
508 	 * This implementation does not guarantee uniqueness. Callers should instead
509 	 * use
510 	 * {@link org.eclipse.jgit.lib.ObjectReader#abbreviate(AnyObjectId, int)} to
511 	 * obtain a unique abbreviation within the scope of a particular object
512 	 * database.
513 	 *
514 	 * @param len
515 	 *            length of the abbreviated string.
516 	 * @return SHA-1 abbreviation.
517 	 */
518 	public AbbreviatedObjectId abbreviate(int len) {
519 		final int a = AbbreviatedObjectId.mask(len, 1, w1);
520 		final int b = AbbreviatedObjectId.mask(len, 2, w2);
521 		final int c = AbbreviatedObjectId.mask(len, 3, w3);
522 		final int d = AbbreviatedObjectId.mask(len, 4, w4);
523 		final int e = AbbreviatedObjectId.mask(len, 5, w5);
524 		return new AbbreviatedObjectId(len, a, b, c, d, e);
525 	}
526 
527 	/**
528 	 * Obtain an immutable copy of this current object name value.
529 	 * <p>
530 	 * Only returns <code>this</code> if this instance is an unsubclassed
531 	 * instance of {@link org.eclipse.jgit.lib.ObjectId}; otherwise a new
532 	 * instance is returned holding the same value.
533 	 * <p>
534 	 * This method is useful to shed any additional memory that may be tied to
535 	 * the subclass, yet retain the unique identity of the object id for future
536 	 * lookups within maps and repositories.
537 	 *
538 	 * @return an immutable copy, using the smallest memory footprint possible.
539 	 */
540 	public final ObjectId copy() {
541 		if (getClass() == ObjectId.class)
542 			return (ObjectId) this;
543 		return new ObjectId(this);
544 	}
545 
546 	/**
547 	 * Obtain an immutable copy of this current object name value.
548 	 * <p>
549 	 * See {@link #copy()} if <code>this</code> is a possibly subclassed (but
550 	 * immutable) identity and the application needs a lightweight identity
551 	 * <i>only</i> reference.
552 	 *
553 	 * @return an immutable copy. May be <code>this</code> if this is already
554 	 *         an immutable instance.
555 	 */
556 	public abstract ObjectId toObjectId();
557 }