The AbbreviatedObjectId class parses an abbreviated SHA-1 string into a binary format, permitting it to be more efficiently matched against existing binary ObjectId fields. Signed-off-by: Shawn O. Pearce <spearce@xxxxxxxxxxx> --- .../spearce/jgit/lib/AbbreviatedObjectIdTest.java | 227 ++++++++++++++++++++ .../org/spearce/jgit/lib/AbbreviatedObjectId.java | 205 ++++++++++++++++++ .../src/org/spearce/jgit/lib/AnyObjectId.java | 2 +- 3 files changed, 433 insertions(+), 1 deletions(-) create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/AbbreviatedObjectIdTest.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/AbbreviatedObjectId.java diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/AbbreviatedObjectIdTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/AbbreviatedObjectIdTest.java new file mode 100644 index 0000000..f540f49 --- /dev/null +++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/AbbreviatedObjectIdTest.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2008, Google Inc. + * + * 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 Git Development Community 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.spearce.jgit.lib; + +import junit.framework.TestCase; + +public class AbbreviatedObjectIdTest extends TestCase { + public void testEmpty_FromByteArray() { + final AbbreviatedObjectId i; + i = AbbreviatedObjectId.fromString(new byte[] {}, 0, 0); + assertNotNull(i); + assertEquals(0, i.length()); + assertFalse(i.isComplete()); + assertEquals("", i.name()); + } + + public void testEmpty_FromString() { + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(""); + assertNotNull(i); + assertEquals(0, i.length()); + assertFalse(i.isComplete()); + assertEquals("", i.name()); + } + + public void testFull_FromByteArray() { + final String s = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final byte[] b = Constants.encodeASCII(s); + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(b, 0, + b.length); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertTrue(i.isComplete()); + assertEquals(s, i.name()); + + final ObjectId f = i.toObjectId(); + assertNotNull(f); + assertEquals(ObjectId.fromString(s), f); + assertEquals(f.hashCode(), i.hashCode()); + } + + public void testFull_FromString() { + final String s = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertTrue(i.isComplete()); + assertEquals(s, i.name()); + + final ObjectId f = i.toObjectId(); + assertNotNull(f); + assertEquals(ObjectId.fromString(s), f); + assertEquals(f.hashCode(), i.hashCode()); + } + + public void test1_FromString() { + final String s = "7"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test2_FromString() { + final String s = "7b"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test3_FromString() { + final String s = "7b6"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test4_FromString() { + final String s = "7b6e"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test5_FromString() { + final String s = "7b6e8"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test6_FromString() { + final String s = "7b6e80"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test7_FromString() { + final String s = "7b6e806"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test8_FromString() { + final String s = "7b6e8067"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test9_FromString() { + final String s = "7b6e8067e"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test17_FromString() { + final String s = "7b6e8067ec96acef9"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void testEquals_Short() { + final String s = "7b6e8067"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(s); + final AbbreviatedObjectId b = AbbreviatedObjectId.fromString(s); + assertNotSame(a, b); + assertTrue(a.hashCode() == b.hashCode()); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + } + + public void testEquals_Full() { + final String s = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(s); + final AbbreviatedObjectId b = AbbreviatedObjectId.fromString(s); + assertNotSame(a, b); + assertTrue(a.hashCode() == b.hashCode()); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + } + + public void testNotEquals_SameLength() { + final String sa = "7b6e8067"; + final String sb = "7b6e806e"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + final AbbreviatedObjectId b = AbbreviatedObjectId.fromString(sb); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + } + + public void testNotEquals_DiffLength() { + final String sa = "7b6e8067abcd"; + final String sb = "7b6e8067"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + final AbbreviatedObjectId b = AbbreviatedObjectId.fromString(sb); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/AbbreviatedObjectId.java b/org.spearce.jgit/src/org/spearce/jgit/lib/AbbreviatedObjectId.java new file mode 100644 index 0000000..1a8d296 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/AbbreviatedObjectId.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2008, Google Inc. + * + * 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 Git Development Community 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.spearce.jgit.lib; + +import java.io.UnsupportedEncodingException; + +/** + * A prefix abbreviation of an {@link ObjectId}. + * <p> + * Sometimes Git produces abbreviated SHA-1 strings, using sufficient leading + * digits from the ObjectId 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. + */ +public class AbbreviatedObjectId { + /** + * 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 AbbreviatedObjectId fromString(final byte[] buf, + final int offset, final int end) { + if (end - offset > AnyObjectId.STR_LEN) + throw new IllegalArgumentException("Invalid id"); + return fromHexString(buf, offset, end); + } + + /** + * Convert an AbbreviatedObjectId from hex characters. + * + * @param str + * the string to read from. Must be <= 40 characters. + * @return the converted object id. + */ + public static final AbbreviatedObjectId fromString(final String str) { + if (str.length() > AnyObjectId.STR_LEN) + throw new IllegalArgumentException("Invalid id: " + str); + final byte[] b = Constants.encodeASCII(str); + return fromHexString(b, 0, b.length); + } + + private static final AbbreviatedObjectId fromHexString(final byte[] bs, + int ptr, final int end) { + try { + final int a = hexUInt32(bs, ptr, end); + final int b = hexUInt32(bs, ptr + 8, end); + final int c = hexUInt32(bs, ptr + 16, end); + final int d = hexUInt32(bs, ptr + 24, end); + final int e = hexUInt32(bs, ptr + 32, end); + return new AbbreviatedObjectId(end - ptr, a, b, c, d, e); + } catch (ArrayIndexOutOfBoundsException e1) { + try { + final String str = new String(bs, ptr, end - ptr, "US-ASCII"); + throw new IllegalArgumentException("Invalid id: " + str); + } catch (UnsupportedEncodingException e2) { + throw new IllegalArgumentException("Invalid id"); + } + } + } + + private static final int hexUInt32(final byte[] bs, int p, final int end) { + if (8 <= end - p) + return AnyObjectId.hexUInt32(bs, p); + + int r = 0, n = 0; + while (n < 8 && p < end) { + final int v = AnyObjectId.fromhex[bs[p++]]; + if (v < 0) + throw new ArrayIndexOutOfBoundsException(); + r <<= 4; + r |= v; + n++; + } + return r << (8 - n) * 4; + } + + /** Number of half-bytes used by this id. */ + final int nibbles; + + final int w1; + + final int w2; + + final int w3; + + final int w4; + + final int w5; + + private AbbreviatedObjectId(final int n, final int new_1, final int new_2, + final int new_3, final int new_4, final int new_5) { + nibbles = n; + w1 = new_1; + w2 = new_2; + w3 = new_3; + w4 = new_4; + w5 = new_5; + } + + /** @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() == AnyObjectId.RAW_LEN * 2; + } + + /** @return a complete ObjectId; null if {@link #isComplete()} is false */ + public ObjectId toObjectId() { + return isComplete() ? new ObjectId(w1, w2, w3, w4, w5) : null; + } + + @Override + public int hashCode() { + return w2; + } + + @Override + public boolean equals(final Object o) { + if (o instanceof AbbreviatedObjectId) { + final AbbreviatedObjectId b = (AbbreviatedObjectId) o; + return nibbles == b.nibbles && w1 == b.w1 && w2 == b.w2 + && w3 == b.w3 && w4 == b.w4 && w5 == b.w5; + } + return false; + } + + /** + * @return string form of the abbreviation, in lower case hexadecimal. + */ + public final String name() { + final char[] b = new char[AnyObjectId.STR_LEN]; + + AnyObjectId.formatHexChar(b, 0, w1); + if (nibbles <= 8) + return new String(b, 0, nibbles); + + AnyObjectId.formatHexChar(b, 8, w2); + if (nibbles <= 16) + return new String(b, 0, nibbles); + + AnyObjectId.formatHexChar(b, 16, w3); + if (nibbles <= 24) + return new String(b, 0, nibbles); + + AnyObjectId.formatHexChar(b, 24, w4); + if (nibbles <= 32) + return new String(b, 0, nibbles); + + AnyObjectId.formatHexChar(b, 32, w5); + return new String(b, 0, nibbles); + } + + @Override + public String toString() { + return "AbbreviatedObjectId[" + name() + "]"; + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/AnyObjectId.java b/org.spearce.jgit/src/org/spearce/jgit/lib/AnyObjectId.java index a534202..e88e09d 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/AnyObjectId.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/AnyObjectId.java @@ -396,7 +396,7 @@ private void toHexCharArray(final char[] dst) { private static final char[] hexchar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - private static void formatHexChar(final char[] dst, final int p, int w) { + static void formatHexChar(final char[] dst, final int p, int w) { int o = p + 7; while (o >= p && w != 0) { dst[o--] = hexchar[w & 0xf]; -- 1.6.1.rc2.299.gead4c -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html