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