summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.lfs/src/org
diff options
context:
space:
mode:
authorMatthias Sohn <matthias.sohn@sap.com>2015-07-17 16:22:56 +0200
committerSaša Živkov <sasa.zivkov@sap.com>2016-02-04 17:49:42 +0100
commit536db18cc62afab2d38aac5b2af73f575f40d8b6 (patch)
tree94dccd9b5a11010044efd27f126bd21a94d5a665 /org.eclipse.jgit.lfs/src/org
parent2de33d7678ffe865e44fc5007ee5fc59afa03404 (diff)
downloadjgit-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')
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/InvalidLongObjectIdException.java90
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java65
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java375
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java555
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java102
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LongObjectId.java309
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/MutableLongObjectId.java256
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 &lt;= 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 &lt;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>;
+ * &gt;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 &lt;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>;
+ * &gt;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 &lt;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>;
+ * &gt;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 &gt;= 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 &lt; 0 if this id comes before other; 0 if this id is equal to
+ * other; &gt; 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);
+ }
+}