NB: encode and decode 24-bit ints

Change-Id: Ie036dc46e5a88a4e87dc52e880505bbe34601ca7
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
index 7e11a61..d2d44ff 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
@@ -90,6 +90,24 @@
 	}
 
 	@Test
+	public void testDecodeUInt24() {
+		assertEquals(0, NB.decodeUInt24(b(0, 0, 0), 0));
+		assertEquals(0, NB.decodeUInt24(padb(3, 0, 0, 0), 3));
+
+		assertEquals(3, NB.decodeUInt24(b(0, 0, 3), 0));
+		assertEquals(3, NB.decodeUInt24(padb(3, 0, 0, 3), 3));
+
+		assertEquals(0xcede03, NB.decodeUInt24(b(0xce, 0xde, 3), 0));
+		assertEquals(0xbade03, NB.decodeUInt24(padb(3, 0xba, 0xde, 3), 3));
+
+		assertEquals(0x03bade, NB.decodeUInt24(b(3, 0xba, 0xde), 0));
+		assertEquals(0x03bade, NB.decodeUInt24(padb(3, 3, 0xba, 0xde), 3));
+
+		assertEquals(0xffffff, NB.decodeUInt24(b(0xff, 0xff, 0xff), 0));
+		assertEquals(0xffffff, NB.decodeUInt24(padb(3, 0xff, 0xff, 0xff), 3));
+	}
+
+	@Test
 	public void testDecodeInt32() {
 		assertEquals(0, NB.decodeInt32(b(0, 0, 0, 0), 0));
 		assertEquals(0, NB.decodeInt32(padb(3, 0, 0, 0, 0), 3));
@@ -198,6 +216,39 @@
 	}
 
 	@Test
+	public void testEncodeInt24() {
+		byte[] out = new byte[16];
+
+		prepareOutput(out);
+		NB.encodeInt24(out, 0, 0);
+		assertOutput(b(0, 0, 0), out, 0);
+
+		prepareOutput(out);
+		NB.encodeInt24(out, 3, 0);
+		assertOutput(b(0, 0, 0), out, 3);
+
+		prepareOutput(out);
+		NB.encodeInt24(out, 0, 3);
+		assertOutput(b(0, 0, 3), out, 0);
+
+		prepareOutput(out);
+		NB.encodeInt24(out, 3, 3);
+		assertOutput(b(0, 0, 3), out, 3);
+
+		prepareOutput(out);
+		NB.encodeInt24(out, 0, 0xc0deac);
+		assertOutput(b(0xc0, 0xde, 0xac), out, 0);
+
+		prepareOutput(out);
+		NB.encodeInt24(out, 3, 0xbadeac);
+		assertOutput(b(0xba, 0xde, 0xac), out, 3);
+
+		prepareOutput(out);
+		NB.encodeInt24(out, 3, -1);
+		assertOutput(b(0xff, 0xff, 0xff), out, 3);
+	}
+
+	@Test
 	public void testEncodeInt32() {
 		final byte[] out = new byte[16];
 
@@ -315,10 +366,24 @@
 		return r;
 	}
 
+	private static byte[] b(int a, int b, int c) {
+		return new byte[] { (byte) a, (byte) b, (byte) c };
+	}
+
 	private static byte[] b(final int a, final int b, final int c, final int d) {
 		return new byte[] { (byte) a, (byte) b, (byte) c, (byte) d };
 	}
 
+	private static byte[] padb(int len, int a, int b, int c) {
+		final byte[] r = new byte[len + 4];
+		for (int i = 0; i < len; i++)
+			r[i] = (byte) 0xaf;
+		r[len] = (byte) a;
+		r[len + 1] = (byte) b;
+		r[len + 2] = (byte) c;
+		return r;
+	}
+
 	private static byte[] padb(final int len, final int a, final int b,
 			final int c, final int d) {
 		final byte[] r = new byte[len + 4];
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
index 8536f1d..471a499 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
@@ -113,6 +113,24 @@
 	}
 
 	/**
+	 * Convert sequence of 3 bytes (network byte order) into unsigned value.
+	 *
+	 * @param intbuf
+	 *            buffer to acquire the 3 bytes of data from.
+	 * @param offset
+	 *            position within the buffer to begin reading from. This
+	 *            position and the next 2 bytes after it (for a total of 3
+	 *            bytes) will be read.
+	 * @return signed integer value that matches the 24 bits read.
+	 * @since 4.9
+	 */
+	public static int decodeUInt24(byte[] intbuf, int offset) {
+		int r = (intbuf[offset] & 0xff) << 8;
+		r |= intbuf[offset + 1] & 0xff;
+		return (r << 8) | (intbuf[offset + 2] & 0xff);
+	}
+
+	/**
 	 * Convert sequence of 4 bytes (network byte order) into signed value.
 	 *
 	 * @param intbuf
@@ -223,6 +241,29 @@
 	}
 
 	/**
+	 * Write a 24 bit integer as a sequence of 3 bytes (network byte order).
+	 *
+	 * @param intbuf
+	 *            buffer to write the 3 bytes of data into.
+	 * @param offset
+	 *            position within the buffer to begin writing to. This position
+	 *            and the next 2 bytes after it (for a total of 3 bytes) will be
+	 *            replaced.
+	 * @param v
+	 *            the value to write.
+	 * @since 4.9
+	 */
+	public static void encodeInt24(byte[] intbuf, int offset, int v) {
+		intbuf[offset + 2] = (byte) v;
+		v >>>= 8;
+
+		intbuf[offset + 1] = (byte) v;
+		v >>>= 8;
+
+		intbuf[offset] = (byte) v;
+	}
+
+	/**
 	 * Write a 32 bit integer as a sequence of 4 bytes (network byte order).
 	 *
 	 * @param intbuf