diff options
Diffstat (limited to 'org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java')
-rw-r--r-- | org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java new file mode 100644 index 0000000000..a13a60c2b8 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.lfs.lib; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.ByteBuffer; + +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.util.NB; +import org.eclipse.jgit.util.References; + +/** + * A (possibly mutable) SHA-256 abstraction. + * <p> + * If this is an instance of + * {@link org.eclipse.jgit.lfs.lib.MutableLongObjectId} the concept of equality + * with this instance can alter at any time, if this instance is modified to + * represent a different object name. + * + * Ported to SHA-256 from {@link org.eclipse.jgit.lib.AnyObjectId} + * + * @since 4.3 + */ +public abstract class AnyLongObjectId implements Comparable<AnyLongObjectId> { + + /** + * Compare two object identifier byte sequences for equality. + * + * @param firstObjectId + * the first identifier to compare. Must not be null. + * @param secondObjectId + * the second identifier to compare. Must not be null. + * @return true if the two identifiers are the same. + * @since 5.4 + */ + public static boolean isEqual(final AnyLongObjectId firstObjectId, + final AnyLongObjectId secondObjectId) { + if (References.isSameObject(firstObjectId, secondObjectId)) { + return true; + } + + // We test word 2 first as odds are someone already used our + // word 1 as a hash code, and applying that came up with these + // two instances we are comparing for equality. Therefore the + // first two words are very likely to be identical. We want to + // break away from collisions as quickly as possible. + // + return firstObjectId.w2 == secondObjectId.w2 + && firstObjectId.w3 == secondObjectId.w3 + && firstObjectId.w4 == secondObjectId.w4 + && firstObjectId.w1 == secondObjectId.w1; + } + + long w1; + + long w2; + + long w3; + + long w4; + + /** + * Get the first 8 bits of the LongObjectId. + * + * This is a faster version of {@code getByte(0)}. + * + * @return a discriminator usable for a fan-out style map. Returned values + * are unsigned and thus are in the range [0,255] rather than the + * signed byte range of [-128, 127]. + */ + public final int getFirstByte() { + return (int) (w1 >>> 56); + } + + /** + * Get the second 8 bits of the LongObjectId. + * + * @return a discriminator usable for a fan-out style map. Returned values + * are unsigned and thus are in the range [0,255] rather than the + * signed byte range of [-128, 127]. + */ + public final int getSecondByte() { + return (int) ((w1 >>> 48) & 0xff); + } + + /** + * Get any byte from the LongObjectId. + * + * Callers hard-coding {@code getByte(0)} should instead use the much faster + * special case variant {@link #getFirstByte()}. + * + * @param index + * index of the byte to obtain from the raw form of the + * LongObjectId. Must be in range [0, + * {@link org.eclipse.jgit.lfs.lib.Constants#LONG_OBJECT_ID_LENGTH}). + * @return the value of the requested byte at {@code index}. Returned values + * are unsigned and thus are in the range [0,255] rather than the + * signed byte range of [-128, 127]. + * @throws java.lang.ArrayIndexOutOfBoundsException + * {@code index} is less than 0, equal to + * {@link org.eclipse.jgit.lfs.lib.Constants#LONG_OBJECT_ID_LENGTH}, + * or greater than + * {@link org.eclipse.jgit.lfs.lib.Constants#LONG_OBJECT_ID_LENGTH}. + */ + public final int getByte(int index) { + long w; + switch (index >> 3) { + case 0: + w = w1; + break; + case 1: + w = w2; + break; + case 2: + w = w3; + break; + case 3: + w = w4; + break; + default: + throw new ArrayIndexOutOfBoundsException(index); + } + + return (int) ((w >>> (8 * (15 - (index & 15)))) & 0xff); + } + + /** + * {@inheritDoc} + * + * Compare this LongObjectId to another and obtain a sort ordering. + */ + @Override + public final int compareTo(AnyLongObjectId other) { + if (this == other) + return 0; + + int cmp; + + cmp = NB.compareUInt64(w1, other.w1); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w2, other.w2); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w3, other.w3); + if (cmp != 0) + return cmp; + + return NB.compareUInt64(w4, other.w4); + } + + /** + * Compare this LongObjectId to a network-byte-order LongObjectId. + * + * @param bs + * array containing the other LongObjectId in network byte order. + * @param p + * position within {@code bs} to start the compare at. At least + * 32 bytes, starting at this position are required. + * @return a negative integer, zero, or a positive integer as this object is + * less than, equal to, or greater than the specified object. + */ + public final int compareTo(byte[] bs, int p) { + int cmp; + + cmp = NB.compareUInt64(w1, NB.decodeInt64(bs, p)); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w2, NB.decodeInt64(bs, p + 8)); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w3, NB.decodeInt64(bs, p + 16)); + if (cmp != 0) + return cmp; + + return NB.compareUInt64(w4, NB.decodeInt64(bs, p + 24)); + } + + /** + * Compare this LongObjectId to a network-byte-order LongObjectId. + * + * @param bs + * array containing the other LongObjectId in network byte order. + * @param p + * position within {@code bs} to start the compare at. At least 4 + * longs, starting at this position are required. + * @return a negative integer, zero, or a positive integer as this object is + * less than, equal to, or greater than the specified object. + */ + public final int compareTo(long[] bs, int p) { + int cmp; + + cmp = NB.compareUInt64(w1, bs[p]); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w2, bs[p + 1]); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w3, bs[p + 2]); + if (cmp != 0) + return cmp; + + return NB.compareUInt64(w4, bs[p + 3]); + } + + /** + * Tests if this LongObjectId starts with the given abbreviation. + * + * @param abbr + * the abbreviation. + * @return true if this LongObjectId begins with the abbreviation; else + * false. + */ + public boolean startsWith(AbbreviatedLongObjectId abbr) { + return abbr.prefixCompare(this) == 0; + } + + @Override + public final int hashCode() { + return (int) (w1 >> 32); + } + + /** + * Determine if this LongObjectId has exactly the same value as another. + * + * @param other + * the other id to compare to. May be null. + * @return true only if both LongObjectIds have identical bits. + */ + @SuppressWarnings({ "NonOverridingEquals", "AmbiguousMethodReference" }) + public final boolean equals(AnyLongObjectId other) { + return other != null ? isEqual(this, other) : false; + } + + @Override + public final boolean equals(Object o) { + if (o instanceof AnyLongObjectId) { + return equals((AnyLongObjectId) o); + } + return false; + } + + /** + * Copy this LongObjectId to an output writer in raw binary. + * + * @param w + * the buffer to copy to. Must be in big endian order. + */ + public void copyRawTo(ByteBuffer w) { + w.putLong(w1); + w.putLong(w2); + w.putLong(w3); + w.putLong(w4); + } + + /** + * Copy this LongObjectId to a byte array. + * + * @param b + * the buffer to copy to. + * @param o + * the offset within b to write at. + */ + public void copyRawTo(byte[] b, int o) { + NB.encodeInt64(b, o, w1); + NB.encodeInt64(b, o + 8, w2); + NB.encodeInt64(b, o + 16, w3); + NB.encodeInt64(b, o + 24, w4); + } + + /** + * Copy this LongObjectId to an long array. + * + * @param b + * the buffer to copy to. + * @param o + * the offset within b to write at. + */ + public void copyRawTo(long[] b, int o) { + b[o] = w1; + b[o + 1] = w2; + b[o + 2] = w3; + b[o + 3] = w4; + } + + /** + * Copy this LongObjectId to an output writer in raw binary. + * + * @param w + * the stream to write to. + * @throws java.io.IOException + * the stream writing failed. + */ + public void copyRawTo(OutputStream w) throws IOException { + writeRawLong(w, w1); + writeRawLong(w, w2); + writeRawLong(w, w3); + writeRawLong(w, w4); + } + + private static void writeRawLong(OutputStream w, long v) + throws IOException { + w.write((int) (v >>> 56)); + w.write((int) (v >>> 48)); + w.write((int) (v >>> 40)); + w.write((int) (v >>> 32)); + w.write((int) (v >>> 24)); + w.write((int) (v >>> 16)); + w.write((int) (v >>> 8)); + w.write((int) v); + } + + /** + * Copy this LongObjectId to an output writer in hex format. + * + * @param w + * the stream to copy to. + * @throws java.io.IOException + * the stream writing failed. + */ + public void copyTo(OutputStream w) throws IOException { + w.write(toHexByteArray()); + } + + /** + * Copy this LongObjectId to a byte array in hex format. + * + * @param b + * the buffer to copy to. + * @param o + * the offset within b to write at. + */ + public void copyTo(byte[] b, int o) { + formatHexByte(b, o + 0, w1); + formatHexByte(b, o + 16, w2); + formatHexByte(b, o + 32, w3); + formatHexByte(b, o + 48, w4); + } + + /** + * Copy this LongObjectId to a ByteBuffer in hex format. + * + * @param b + * the buffer to copy to. + */ + public void copyTo(ByteBuffer b) { + b.put(toHexByteArray()); + } + + private byte[] toHexByteArray() { + final byte[] dst = new byte[Constants.LONG_OBJECT_ID_STRING_LENGTH]; + formatHexByte(dst, 0, w1); + formatHexByte(dst, 16, w2); + formatHexByte(dst, 32, w3); + formatHexByte(dst, 48, w4); + return dst; + } + + private static final byte[] hexbyte = { '0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + private static void formatHexByte(byte[] dst, int p, long w) { + int o = p + 15; + while (o >= p && w != 0) { + dst[o--] = hexbyte[(int) (w & 0xf)]; + w >>>= 4; + } + while (o >= p) + dst[o--] = '0'; + } + + /** + * Copy this LongObjectId to an output writer in hex format. + * + * @param w + * the stream to copy to. + * @throws java.io.IOException + * the stream writing failed. + */ + public void copyTo(Writer w) throws IOException { + w.write(toHexCharArray()); + } + + /** + * Copy this LongObjectId to an output writer in hex format. + * + * @param tmp + * temporary char array to buffer construct into before writing. + * Must be at least large enough to hold 2 digits for each byte + * of object id (64 characters or larger). + * @param w + * the stream to copy to. + * @throws java.io.IOException + * the stream writing failed. + */ + public void copyTo(char[] tmp, Writer w) throws IOException { + toHexCharArray(tmp); + w.write(tmp, 0, Constants.LONG_OBJECT_ID_STRING_LENGTH); + } + + /** + * Copy this LongObjectId to a StringBuilder in hex format. + * + * @param tmp + * temporary char array to buffer construct into before writing. + * Must be at least large enough to hold 2 digits for each byte + * of object id (64 characters or larger). + * @param w + * the string to append onto. + */ + public void copyTo(char[] tmp, StringBuilder w) { + toHexCharArray(tmp); + w.append(tmp, 0, Constants.LONG_OBJECT_ID_STRING_LENGTH); + } + + char[] toHexCharArray() { + final char[] dst = new char[Constants.LONG_OBJECT_ID_STRING_LENGTH]; + toHexCharArray(dst); + return dst; + } + + private void toHexCharArray(char[] dst) { + formatHexChar(dst, 0, w1); + formatHexChar(dst, 16, w2); + formatHexChar(dst, 32, w3); + formatHexChar(dst, 48, w4); + } + + private static final char[] hexchar = { '0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + static void formatHexChar(char[] dst, int p, long w) { + int o = p + 15; + while (o >= p && w != 0) { + dst[o--] = hexchar[(int) (w & 0xf)]; + w >>>= 4; + } + while (o >= p) + dst[o--] = '0'; + } + + @SuppressWarnings("nls") + @Override + public String toString() { + return "AnyLongObjectId[" + name() + "]"; + } + + /** + * Get string form of the SHA-256 + * + * @return string form of the SHA-256, in lower case hexadecimal. + */ + public final String name() { + return new String(toHexCharArray()); + } + + /** + * Get string form of the SHA-256 + * + * @return string form of the SHA-256, in lower case hexadecimal. + */ + public final String getName() { + return name(); + } + + /** + * Return an abbreviation (prefix) of this object SHA-256. + * <p> + * This implementation does not guarantee uniqueness. Callers should instead + * use + * {@link org.eclipse.jgit.lib.ObjectReader#abbreviate(AnyObjectId, int)} to + * obtain a unique abbreviation within the scope of a particular object + * database. + * + * @param len + * length of the abbreviated string. + * @return SHA-256 abbreviation. + */ + public AbbreviatedLongObjectId abbreviate(int len) { + final long a = AbbreviatedLongObjectId.mask(len, 1, w1); + final long b = AbbreviatedLongObjectId.mask(len, 2, w2); + final long c = AbbreviatedLongObjectId.mask(len, 3, w3); + final long d = AbbreviatedLongObjectId.mask(len, 4, w4); + return new AbbreviatedLongObjectId(len, a, b, c, d); + } + + /** + * Obtain an immutable copy of this current object. + * <p> + * Only returns <code>this</code> if this instance is an unsubclassed + * instance of {@link org.eclipse.jgit.lfs.lib.LongObjectId}; otherwise a + * new instance is returned holding the same value. + * <p> + * This method is useful to shed any additional memory that may be tied to + * the subclass, yet retain the unique identity of the object id for future + * lookups within maps and repositories. + * + * @return an immutable copy, using the smallest memory footprint possible. + */ + public final LongObjectId copy() { + if (getClass() == LongObjectId.class) + return (LongObjectId) this; + return new LongObjectId(this); + } + + /** + * Obtain an immutable copy of this current object. + * <p> + * See {@link #copy()} if <code>this</code> is a possibly subclassed (but + * immutable) identity and the application needs a lightweight identity + * <i>only</i> reference. + * + * @return an immutable copy. May be <code>this</code> if this is already an + * immutable instance. + */ + public abstract LongObjectId toObjectId(); +} |