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