diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2015-07-17 16:22:56 +0200 |
---|---|---|
committer | Saša Živkov <sasa.zivkov@sap.com> | 2016-02-04 17:49:42 +0100 |
commit | 536db18cc62afab2d38aac5b2af73f575f40d8b6 (patch) | |
tree | 94dccd9b5a11010044efd27f126bd21a94d5a665 /org.eclipse.jgit.lfs/src/org/eclipse/jgit | |
parent | 2de33d7678ffe865e44fc5007ee5fc59afa03404 (diff) | |
download | jgit-536db18cc62afab2d38aac5b2af73f575f40d8b6.tar.gz jgit-536db18cc62afab2d38aac5b2af73f575f40d8b6.zip |
Implement SHA-256 abstraction
The Large File Storage extension specified by GitHub [1] uses SHA-256 to
compute the ID of large files stored by the extension. Hence implement a
SHA-256 abstraction similar to the SHA-1 abstraction used by JGit.
[1] https://git-lfs.github.com/
Bug: 470333
Change-Id: I3a95954543c8570d73929e55f4a884b55dbf1b7a
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.jgit.lfs/src/org/eclipse/jgit')
7 files changed, 1752 insertions, 0 deletions
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/InvalidLongObjectIdException.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/InvalidLongObjectIdException.java new file mode 100644 index 0000000000..1f6e2d174f --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/InvalidLongObjectIdException.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2009, Jonas Fonseca <fonseca@diku.dk> + * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.lfs.errors; + +import java.io.UnsupportedEncodingException; +import java.text.MessageFormat; + +import org.eclipse.jgit.lfs.internal.LfsText; + +/** + * Thrown when an invalid long object id is passed in as an argument. + * + * @since 4.3 + */ +public class InvalidLongObjectIdException extends IllegalArgumentException { + private static final long serialVersionUID = 1L; + + /** + * Create exception with bytes of the invalid object id. + * + * @param bytes containing the invalid id. + * @param offset in the byte array where the error occurred. + * @param length of the sequence of invalid bytes. + */ + public InvalidLongObjectIdException(byte[] bytes, int offset, int length) { + super(MessageFormat.format(LfsText.get().invalidLongId, + asAscii(bytes, offset, length))); + } + + /** + * @param idString + * String containing the invalid id + */ + public InvalidLongObjectIdException(String idString) { + super(MessageFormat.format(LfsText.get().invalidLongId, idString)); + } + + private static String asAscii(byte[] bytes, int offset, int length) { + try { + return ": " + new String(bytes, offset, length, "US-ASCII"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (UnsupportedEncodingException e2) { + return ""; //$NON-NLS-1$ + } catch (StringIndexOutOfBoundsException e2) { + return ""; //$NON-NLS-1$ + } + } +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java new file mode 100644 index 0000000000..eaffcc9047 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.internal; + +import org.eclipse.jgit.nls.NLS; +import org.eclipse.jgit.nls.TranslationBundle; + +/** + * Translation bundle for JGit LFS server + */ +public class LfsText extends TranslationBundle { + + /** + * @return an instance of this translation bundle + */ + public static LfsText get() { + return NLS.getBundleFor(LfsText.class); + } + + // @formatter:off + /***/ public String incorrectLONG_OBJECT_ID_LENGTH; + /***/ public String invalidLongId; + /***/ public String invalidLongIdLength; + /***/ public String requiredHashFunctionNotAvailable; +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java new file mode 100644 index 0000000000..e9eb0e3850 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.lfs.lib; + +import java.io.Serializable; +import java.text.MessageFormat; + +import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException; +import org.eclipse.jgit.lfs.internal.LfsText; +import org.eclipse.jgit.lib.AbbreviatedObjectId; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.util.NB; +import org.eclipse.jgit.util.RawParseUtils; + +/** + * A prefix abbreviation of an {@link LongObjectId}. + * <p> + * Enable abbreviating SHA-256 strings used by Git LFS, using sufficient leading + * digits from the LongObjectId name to still be unique within the repository + * the string was generated from. These ids are likely to be unique for a useful + * period of time, especially if they contain at least 6-10 hex digits. + * <p> + * This class converts the hex string into a binary form, to make it more + * efficient for matching against an object. + * + * Ported to SHA-256 from {@link AbbreviatedObjectId} + * + * @since 4.3 + */ +public final class AbbreviatedLongObjectId implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * Test a string of characters to verify it is a hex format. + * <p> + * If true the string can be parsed with {@link #fromString(String)}. + * + * @param id + * the string to test. + * @return true if the string can converted into an AbbreviatedObjectId. + */ + public static final boolean isId(final String id) { + if (id.length() < 2 + || Constants.LONG_OBJECT_ID_STRING_LENGTH < id.length()) + return false; + try { + for (int i = 0; i < id.length(); i++) + RawParseUtils.parseHexInt4((byte) id.charAt(i)); + return true; + } catch (ArrayIndexOutOfBoundsException e) { + return false; + } + } + + /** + * Convert an AbbreviatedObjectId from hex characters (US-ASCII). + * + * @param buf + * the US-ASCII buffer to read from. + * @param offset + * position to read the first character from. + * @param end + * one past the last position to read (<code>end-offset</code> is + * the length of the string). + * @return the converted object id. + */ + public static final AbbreviatedLongObjectId fromString(final byte[] buf, + final int offset, final int end) { + if (end - offset > Constants.LONG_OBJECT_ID_STRING_LENGTH) + throw new IllegalArgumentException(MessageFormat.format( + LfsText.get().invalidLongIdLength, + Integer.valueOf(end - offset), + Integer.valueOf(Constants.LONG_OBJECT_ID_STRING_LENGTH))); + return fromHexString(buf, offset, end); + } + + /** + * Convert an AbbreviatedObjectId from an {@link AnyObjectId}. + * <p> + * This method copies over all bits of the Id, and is therefore complete + * (see {@link #isComplete()}). + * + * @param id + * the {@link ObjectId} to convert from. + * @return the converted object id. + */ + public static final AbbreviatedLongObjectId fromLongObjectId( + AnyLongObjectId id) { + return new AbbreviatedLongObjectId( + Constants.LONG_OBJECT_ID_STRING_LENGTH, id.w1, id.w2, id.w3, + id.w4); + } + + /** + * Convert an AbbreviatedLongObjectId from hex characters. + * + * @param str + * the string to read from. Must be <= 64 characters. + * @return the converted object id. + */ + public static final AbbreviatedLongObjectId fromString(final String str) { + if (str.length() > Constants.LONG_OBJECT_ID_STRING_LENGTH) + throw new IllegalArgumentException( + MessageFormat.format(LfsText.get().invalidLongId, str)); + final byte[] b = org.eclipse.jgit.lib.Constants.encodeASCII(str); + return fromHexString(b, 0, b.length); + } + + private static final AbbreviatedLongObjectId fromHexString(final byte[] bs, + int ptr, final int end) { + try { + final long a = hexUInt64(bs, ptr, end); + final long b = hexUInt64(bs, ptr + 16, end); + final long c = hexUInt64(bs, ptr + 32, end); + final long d = hexUInt64(bs, ptr + 48, end); + return new AbbreviatedLongObjectId(end - ptr, a, b, c, d); + } catch (ArrayIndexOutOfBoundsException e1) { + throw new InvalidLongObjectIdException(bs, ptr, end - ptr); + } + } + + private static final long hexUInt64(final byte[] bs, int p, final int end) { + if (16 <= end - p) + return RawParseUtils.parseHexInt64(bs, p); + + long r = 0; + int n = 0; + while (n < 16 && p < end) { + r <<= 4; + r |= RawParseUtils.parseHexInt4(bs[p++]); + n++; + } + return r << (16 - n) * 4; + } + + static long mask(final int nibbles, final long word, final long v) { + final long b = (word - 1) * 16; + if (b + 16 <= nibbles) { + // We have all of the bits required for this word. + // + return v; + } + + if (nibbles <= b) { + // We have none of the bits required for this word. + // + return 0; + } + + final long s = 64 - (nibbles - b) * 4; + return (v >>> s) << s; + } + + /** Number of half-bytes used by this id. */ + final int nibbles; + + final long w1; + + final long w2; + + final long w3; + + final long w4; + + AbbreviatedLongObjectId(final int n, final long new_1, final long new_2, + final long new_3, final long new_4) { + nibbles = n; + w1 = new_1; + w2 = new_2; + w3 = new_3; + w4 = new_4; + } + + /** @return number of hex digits appearing in this id */ + public int length() { + return nibbles; + } + + /** @return true if this ObjectId is actually a complete id. */ + public boolean isComplete() { + return length() == Constants.LONG_OBJECT_ID_STRING_LENGTH; + } + + /** @return a complete ObjectId; null if {@link #isComplete()} is false */ + public LongObjectId toLongObjectId() { + return isComplete() ? new LongObjectId(w1, w2, w3, w4) : null; + } + + /** + * Compares this abbreviation to a full object id. + * + * @param other + * the other object id. + * @return <0 if this abbreviation names an object that is less than + * <code>other</code>; 0 if this abbreviation exactly matches the + * first {@link #length()} digits of <code>other.name()</code>; + * >0 if this abbreviation names an object that is after + * <code>other</code>. + */ + public final int prefixCompare(final AnyLongObjectId other) { + int cmp; + + cmp = NB.compareUInt64(w1, mask(1, other.w1)); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w2, mask(2, other.w2)); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w3, mask(3, other.w3)); + if (cmp != 0) + return cmp; + + return NB.compareUInt64(w4, mask(4, other.w4)); + } + + /** + * Compare this abbreviation 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 <0 if this abbreviation names an object that is less than + * <code>other</code>; 0 if this abbreviation exactly matches the + * first {@link #length()} digits of <code>other.name()</code>; + * >0 if this abbreviation names an object that is after + * <code>other</code>. + */ + public final int prefixCompare(final byte[] bs, final int p) { + int cmp; + + cmp = NB.compareUInt64(w1, mask(1, NB.decodeInt64(bs, p))); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w2, mask(2, NB.decodeInt64(bs, p + 8))); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w3, mask(3, NB.decodeInt64(bs, p + 16))); + if (cmp != 0) + return cmp; + + return NB.compareUInt64(w4, mask(4, NB.decodeInt64(bs, p + 24))); + } + + /** + * Compare this abbreviation 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 <0 if this abbreviation names an object that is less than + * <code>other</code>; 0 if this abbreviation exactly matches the + * first {@link #length()} digits of <code>other.name()</code>; + * >0 if this abbreviation names an object that is after + * <code>other</code>. + */ + public final int prefixCompare(final long[] bs, final int p) { + int cmp; + + cmp = NB.compareUInt64(w1, mask(1, bs[p])); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w2, mask(2, bs[p + 1])); + if (cmp != 0) + return cmp; + + cmp = NB.compareUInt64(w3, mask(3, bs[p + 2])); + if (cmp != 0) + return cmp; + + return NB.compareUInt64(w4, mask(4, bs[p + 3])); + } + + /** @return value for a fan-out style map, only valid of length >= 2. */ + public final int getFirstByte() { + return (int) (w1 >>> 56); + } + + private long mask(final long word, final long v) { + return mask(nibbles, word, v); + } + + @Override + public int hashCode() { + return (int) (w1 >> 32); + } + + @Override + public boolean equals(final Object o) { + if (o instanceof AbbreviatedLongObjectId) { + final AbbreviatedLongObjectId b = (AbbreviatedLongObjectId) o; + return nibbles == b.nibbles && w1 == b.w1 && w2 == b.w2 + && w3 == b.w3 && w4 == b.w4; + } + return false; + } + + /** + * @return string form of the abbreviation, in lower case hexadecimal. + */ + public final String name() { + final char[] b = new char[Constants.LONG_OBJECT_ID_STRING_LENGTH]; + + AnyLongObjectId.formatHexChar(b, 0, w1); + if (nibbles <= 16) + return new String(b, 0, nibbles); + + AnyLongObjectId.formatHexChar(b, 16, w2); + if (nibbles <= 32) + return new String(b, 0, nibbles); + + AnyLongObjectId.formatHexChar(b, 32, w3); + if (nibbles <= 48) + return new String(b, 0, nibbles); + + AnyLongObjectId.formatHexChar(b, 48, w4); + return new String(b, 0, nibbles); + } + + @SuppressWarnings("nls") + @Override + public String toString() { + return "AbbreviatedLongObjectId[" + name() + "]"; //$NON-NLS-1$ + } +} 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..1f0df882d2 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java @@ -0,0 +1,555 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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.lib.ObjectReader; +import org.eclipse.jgit.util.NB; + +/** + * A (possibly mutable) SHA-256 abstraction. + * <p> + * If this is an instance of {@link 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 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. + */ + public static boolean equals(final AnyLongObjectId firstObjectId, + final AnyLongObjectId secondObjectId) { + if (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 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 ArrayIndexOutOfBoundsException + * {@code index} is less than 0, equal to + * {@link Constants#LONG_OBJECT_ID_LENGTH}, or greater than + * {@link 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); + } + + /** + * Compare this LongObjectId to another and obtain a sort ordering. + * + * @param other + * the other id to compare to. Must not be null. + * @return < 0 if this id comes before other; 0 if this id is equal to + * other; > 0 if this id comes after other. + */ + public final int compareTo(final 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(final byte[] bs, final 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(final long[] bs, final 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(final AbbreviatedLongObjectId abbr) { + return abbr.prefixCompare(this) == 0; + } + + 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. + */ + public final boolean equals(final AnyLongObjectId other) { + return other != null ? equals(this, other) : false; + } + + public final boolean equals(final Object o) { + if (o instanceof AnyLongObjectId) + return equals((AnyLongObjectId) o); + else + 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(final 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(final byte[] b, final 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(final long[] b, final 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 IOException + * the stream writing failed. + */ + public void copyRawTo(final OutputStream w) throws IOException { + writeRawLong(w, w1); + writeRawLong(w, w2); + writeRawLong(w, w3); + writeRawLong(w, w4); + } + + private static void writeRawLong(final 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 IOException + * the stream writing failed. + */ + public void copyTo(final 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(final byte[] dst, final 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 IOException + * the stream writing failed. + */ + public void copyTo(final 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 IOException + * the stream writing failed. + */ + public void copyTo(final char[] tmp, final 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(final char[] tmp, final 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(final 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(final char[] dst, final 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() + "]"; + } + + /** + * @return string form of the SHA-256, in lower case hexadecimal. + */ + public final String name() { + return new String(toHexCharArray()); + } + + /** + * @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 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(final 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 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(); +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java new file mode 100644 index 0000000000..92a5c4250f --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.lib; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.MessageFormat; + +import org.eclipse.jgit.lfs.internal.LfsText; + +/** + * Misc. constants used throughout JGit LFS extension. + * + * @since 4.3 + **/ +@SuppressWarnings("nls") +public final class Constants { + /** Hash function used natively by Git LFS extension for large objects. */ + private static final String LONG_HASH_FUNCTION = "SHA-256"; + + /** + * A Git LFS large object hash is 256 bits, i.e. 32 bytes. + * <p> + * Changing this assumption is not going to be as easy as changing this + * declaration. + */ + public static final int LONG_OBJECT_ID_LENGTH = 32; + + /** + * A Git LFS large object can be expressed as a 64 character string of + * hexadecimal digits. + * + * @see #LONG_OBJECT_ID_LENGTH + */ + public static final int LONG_OBJECT_ID_STRING_LENGTH = LONG_OBJECT_ID_LENGTH + * 2; + + /** + * Create a new digest function for objects. + * + * @return a new digest object. + * @throws RuntimeException + * this Java virtual machine does not support the required hash + * function. Very unlikely given that JGit uses a hash function + * that is in the Java reference specification. + */ + public static MessageDigest newMessageDigest() { + try { + return MessageDigest.getInstance(LONG_HASH_FUNCTION); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException(MessageFormat.format( + LfsText.get().requiredHashFunctionNotAvailable, + LONG_HASH_FUNCTION), nsae); + } + } + + static { + if (LONG_OBJECT_ID_LENGTH != newMessageDigest().getDigestLength()) + throw new LinkageError( + LfsText.get().incorrectLONG_OBJECT_ID_LENGTH); + } +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LongObjectId.java new file mode 100644 index 0000000000..c4a4e43b12 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LongObjectId.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.lfs.lib; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.util.NB; +import org.eclipse.jgit.util.RawParseUtils; + +/** + * A SHA-256 abstraction. + * + * Ported to SHA-256 from {@link ObjectId} + * + * @since 4.3 + */ +public class LongObjectId extends AnyLongObjectId implements Serializable { + private static final long serialVersionUID = 1L; + + private static final LongObjectId ZEROID; + + private static final String ZEROID_STR; + + static { + ZEROID = new LongObjectId(0L, 0L, 0L, 0L); + ZEROID_STR = ZEROID.name(); + } + + /** + * Get the special all-zero LongObjectId. + * + * @return the all-zero LongObjectId, often used to stand-in for no object. + */ + public static final LongObjectId zeroId() { + return ZEROID; + } + + /** + * Test a string of characters to verify that it can be interpreted as + * LongObjectId. + * <p> + * If true the string can be parsed with {@link #fromString(String)}. + * + * @param id + * the string to test. + * @return true if the string can converted into an LongObjectId. + */ + public static final boolean isId(final String id) { + if (id.length() != Constants.LONG_OBJECT_ID_STRING_LENGTH) + return false; + try { + for (int i = 0; i < Constants.LONG_OBJECT_ID_STRING_LENGTH; i++) { + RawParseUtils.parseHexInt4((byte) id.charAt(i)); + } + return true; + } catch (ArrayIndexOutOfBoundsException e) { + return false; + } + } + + /** + * Convert a LongObjectId into a hex string representation. + * + * @param i + * the id to convert. May be null. + * @return the hex string conversion of this id's content. + */ + public static final String toString(final LongObjectId i) { + return i != null ? i.name() : ZEROID_STR; + } + + /** + * Compare two object identifier byte sequences for equality. + * + * @param firstBuffer + * the first buffer to compare against. Must have at least 32 + * bytes from position fi through the end of the buffer. + * @param fi + * first offset within firstBuffer to begin testing. + * @param secondBuffer + * the second buffer to compare against. Must have at least 32 + * bytes from position si through the end of the buffer. + * @param si + * first offset within secondBuffer to begin testing. + * @return true if the two identifiers are the same. + */ + public static boolean equals(final byte[] firstBuffer, final int fi, + final byte[] secondBuffer, final int si) { + return firstBuffer[fi] == secondBuffer[si] + && firstBuffer[fi + 1] == secondBuffer[si + 1] + && firstBuffer[fi + 2] == secondBuffer[si + 2] + && firstBuffer[fi + 3] == secondBuffer[si + 3] + && firstBuffer[fi + 4] == secondBuffer[si + 4] + && firstBuffer[fi + 5] == secondBuffer[si + 5] + && firstBuffer[fi + 6] == secondBuffer[si + 6] + && firstBuffer[fi + 7] == secondBuffer[si + 7] + && firstBuffer[fi + 8] == secondBuffer[si + 8] + && firstBuffer[fi + 9] == secondBuffer[si + 9] + && firstBuffer[fi + 10] == secondBuffer[si + 10] + && firstBuffer[fi + 11] == secondBuffer[si + 11] + && firstBuffer[fi + 12] == secondBuffer[si + 12] + && firstBuffer[fi + 13] == secondBuffer[si + 13] + && firstBuffer[fi + 14] == secondBuffer[si + 14] + && firstBuffer[fi + 15] == secondBuffer[si + 15] + && firstBuffer[fi + 16] == secondBuffer[si + 16] + && firstBuffer[fi + 17] == secondBuffer[si + 17] + && firstBuffer[fi + 18] == secondBuffer[si + 18] + && firstBuffer[fi + 19] == secondBuffer[si + 19] + && firstBuffer[fi + 20] == secondBuffer[si + 20] + && firstBuffer[fi + 21] == secondBuffer[si + 21] + && firstBuffer[fi + 22] == secondBuffer[si + 22] + && firstBuffer[fi + 23] == secondBuffer[si + 23] + && firstBuffer[fi + 24] == secondBuffer[si + 24] + && firstBuffer[fi + 25] == secondBuffer[si + 25] + && firstBuffer[fi + 26] == secondBuffer[si + 26] + && firstBuffer[fi + 27] == secondBuffer[si + 27] + && firstBuffer[fi + 28] == secondBuffer[si + 28] + && firstBuffer[fi + 29] == secondBuffer[si + 29] + && firstBuffer[fi + 30] == secondBuffer[si + 30] + && firstBuffer[fi + 31] == secondBuffer[si + 31]; + } + + /** + * Convert a LongObjectId from raw binary representation. + * + * @param bs + * the raw byte buffer to read from. At least 32 bytes must be + * available within this byte array. + * @return the converted object id. + */ + public static final LongObjectId fromRaw(final byte[] bs) { + return fromRaw(bs, 0); + } + + /** + * Convert a LongObjectId from raw binary representation. + * + * @param bs + * the raw byte buffer to read from. At least 32 bytes after p + * must be available within this byte array. + * @param p + * position to read the first byte of data from. + * @return the converted object id. + */ + public static final LongObjectId fromRaw(final byte[] bs, final int p) { + final long a = NB.decodeInt64(bs, p); + final long b = NB.decodeInt64(bs, p + 8); + final long c = NB.decodeInt64(bs, p + 16); + final long d = NB.decodeInt64(bs, p + 24); + return new LongObjectId(a, b, c, d); + } + + /** + * Convert a LongObjectId from raw binary representation. + * + * @param is + * the raw long buffer to read from. At least 4 longs must be + * available within this long array. + * @return the converted object id. + */ + public static final LongObjectId fromRaw(final long[] is) { + return fromRaw(is, 0); + } + + /** + * Convert a LongObjectId from raw binary representation. + * + * @param is + * the raw long buffer to read from. At least 4 longs after p + * must be available within this long array. + * @param p + * position to read the first long of data from. + * @return the converted object id. + */ + public static final LongObjectId fromRaw(final long[] is, final int p) { + return new LongObjectId(is[p], is[p + 1], is[p + 2], is[p + 3]); + } + + /** + * Convert a LongObjectId from hex characters (US-ASCII). + * + * @param buf + * the US-ASCII buffer to read from. At least 64 bytes after + * offset must be available within this byte array. + * @param offset + * position to read the first character from. + * @return the converted object id. + */ + public static final LongObjectId fromString(final byte[] buf, final int offset) { + return fromHexString(buf, offset); + } + + /** + * Convert a LongObjectId from hex characters. + * + * @param str + * the string to read from. Must be 64 characters long. + * @return the converted object id. + */ + public static LongObjectId fromString(final String str) { + if (str.length() != Constants.LONG_OBJECT_ID_STRING_LENGTH) + throw new InvalidLongObjectIdException(str); + return fromHexString(org.eclipse.jgit.lib.Constants.encodeASCII(str), + 0); + } + + private static final LongObjectId fromHexString(final byte[] bs, int p) { + try { + final long a = RawParseUtils.parseHexInt64(bs, p); + final long b = RawParseUtils.parseHexInt64(bs, p + 16); + final long c = RawParseUtils.parseHexInt64(bs, p + 32); + final long d = RawParseUtils.parseHexInt64(bs, p + 48); + return new LongObjectId(a, b, c, d); + } catch (ArrayIndexOutOfBoundsException e1) { + throw new InvalidLongObjectIdException(bs, p, + Constants.LONG_OBJECT_ID_STRING_LENGTH); + } + } + + LongObjectId(final long new_1, final long new_2, final long new_3, + final long new_4) { + w1 = new_1; + w2 = new_2; + w3 = new_3; + w4 = new_4; + } + + /** + * Initialize this instance by copying another existing LongObjectId. + * <p> + * This constructor is mostly useful for subclasses which want to extend a + * LongObjectId with more properties, but initialize from an existing + * LongObjectId instance acquired by other means. + * + * @param src + * another already parsed LongObjectId to copy the value out of. + */ + protected LongObjectId(final AnyLongObjectId src) { + w1 = src.w1; + w2 = src.w2; + w3 = src.w3; + w4 = src.w4; + } + + @Override + public LongObjectId toObjectId() { + return this; + } + + private void writeObject(ObjectOutputStream os) throws IOException { + os.writeLong(w1); + os.writeLong(w2); + os.writeLong(w3); + os.writeLong(w4); + } + + private void readObject(ObjectInputStream ois) throws IOException { + w1 = ois.readLong(); + w2 = ois.readLong(); + w3 = ois.readLong(); + w4 = ois.readLong(); + } +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/MutableLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/MutableLongObjectId.java new file mode 100644 index 0000000000..130e94ed0b --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/MutableLongObjectId.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.lfs.lib; + +import java.text.MessageFormat; + +import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException; +import org.eclipse.jgit.lfs.internal.LfsText; +import org.eclipse.jgit.lib.MutableObjectId; +import org.eclipse.jgit.util.NB; +import org.eclipse.jgit.util.RawParseUtils; + +/** + * A mutable SHA-256 abstraction. + * + * Ported to SHA-256 from {@link MutableObjectId} + * + * @since 4.3 + */ +public class MutableLongObjectId extends AnyLongObjectId { + /** + * Empty constructor. Initialize object with default (zeros) value. + */ + public MutableLongObjectId() { + super(); + } + + /** + * Copying constructor. + * + * @param src + * original entry, to copy id from + */ + MutableLongObjectId(MutableLongObjectId src) { + fromObjectId(src); + } + + /** + * Set any byte in the id. + * + * @param index + * index of the byte to set in the raw form of the ObjectId. Must + * be in range [0, {@link Constants#LONG_OBJECT_ID_LENGTH}). + * @param value + * the value of the specified byte at {@code index}. Values are + * unsigned and thus are in the range [0,255] rather than the + * signed byte range of [-128, 127]. + * @throws ArrayIndexOutOfBoundsException + * {@code index} is less than 0, equal to + * {@link Constants#LONG_OBJECT_ID_LENGTH}, or greater than + * {@link Constants#LONG_OBJECT_ID_LENGTH}. + */ + public void setByte(int index, int value) { + switch (index >> 3) { + case 0: + w1 = set(w1, index & 7, value); + break; + case 1: + w2 = set(w2, index & 7, value); + break; + case 2: + w3 = set(w3, index & 7, value); + break; + case 3: + w4 = set(w4, index & 7, value); + break; + default: + throw new ArrayIndexOutOfBoundsException(index); + } + } + + private static long set(long w, int index, long value) { + value &= 0xff; + + switch (index) { + case 0: + return (w & 0x00ffffffffffffffL) | (value << 56); + case 1: + return (w & 0xff00ffffffffffffL) | (value << 48); + case 2: + return (w & 0xffff00ffffffffffL) | (value << 40); + case 3: + return (w & 0xffffff00ffffffffL) | (value << 32); + case 4: + return (w & 0xffffffff00ffffffL) | (value << 24); + case 5: + return (w & 0xffffffffff00ffffL) | (value << 16); + case 6: + return (w & 0xffffffffffff00ffL) | (value << 8); + case 7: + return (w & 0xffffffffffffff00L) | value; + default: + throw new ArrayIndexOutOfBoundsException(); + } + } + + /** Make this id match {@link LongObjectId#zeroId()}. */ + public void clear() { + w1 = 0; + w2 = 0; + w3 = 0; + w4 = 0; + } + + /** + * Copy an LongObjectId into this mutable buffer. + * + * @param src + * the source id to copy from. + */ + public void fromObjectId(AnyLongObjectId src) { + this.w1 = src.w1; + this.w2 = src.w2; + this.w3 = src.w3; + this.w4 = src.w4; + } + + /** + * Convert an LongObjectId from raw binary representation. + * + * @param bs + * the raw byte buffer to read from. At least 32 bytes must be + * available within this byte array. + */ + public void fromRaw(final byte[] bs) { + fromRaw(bs, 0); + } + + /** + * Convert an LongObjectId from raw binary representation. + * + * @param bs + * the raw byte buffer to read from. At least 32 bytes after p + * must be available within this byte array. + * @param p + * position to read the first byte of data from. + */ + public void fromRaw(final byte[] bs, final int p) { + w1 = NB.decodeInt64(bs, p); + w2 = NB.decodeInt64(bs, p + 8); + w3 = NB.decodeInt64(bs, p + 16); + w4 = NB.decodeInt64(bs, p + 24); + } + + /** + * Convert an LongObjectId from binary representation expressed in integers. + * + * @param longs + * the raw long buffer to read from. At least 4 longs must be + * available within this longs array. + */ + public void fromRaw(final long[] longs) { + fromRaw(longs, 0); + } + + /** + * Convert an LongObjectId from binary representation expressed in longs. + * + * @param longs + * the raw int buffer to read from. At least 4 longs after p must + * be available within this longs array. + * @param p + * position to read the first integer of data from. + * + */ + public void fromRaw(final long[] longs, final int p) { + w1 = longs[p]; + w2 = longs[p + 1]; + w3 = longs[p + 2]; + w4 = longs[p + 3]; + } + + /** + * Convert an LongObjectId from hex characters (US-ASCII). + * + * @param buf + * the US-ASCII buffer to read from. At least 32 bytes after + * offset must be available within this byte array. + * @param offset + * position to read the first character from. + */ + public void fromString(final byte[] buf, final int offset) { + fromHexString(buf, offset); + } + + /** + * Convert an LongObjectId from hex characters. + * + * @param str + * the string to read from. Must be 64 characters long. + */ + public void fromString(final String str) { + if (str.length() != Constants.LONG_OBJECT_ID_STRING_LENGTH) + throw new IllegalArgumentException( + MessageFormat.format(LfsText.get().invalidLongId, str)); + fromHexString(org.eclipse.jgit.lib.Constants.encodeASCII(str), 0); + } + + private void fromHexString(final byte[] bs, int p) { + try { + w1 = RawParseUtils.parseHexInt64(bs, p); + w2 = RawParseUtils.parseHexInt64(bs, p + 16); + w3 = RawParseUtils.parseHexInt64(bs, p + 32); + w4 = RawParseUtils.parseHexInt64(bs, p + 48); + } catch (ArrayIndexOutOfBoundsException e1) { + throw new InvalidLongObjectIdException(bs, p, + Constants.LONG_OBJECT_ID_STRING_LENGTH); + } + } + + @Override + public LongObjectId toObjectId() { + return new LongObjectId(this); + } +} |