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 < 0 if this id comes before other; 0 if this id is equal to
172 * other; > 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 }