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