Author: liyin Date: Fri Mar 14 18:09:54 2014 New Revision: 1577642 URL: http://svn.apache.org/r1577642 Log: [private] Optimize head/tail/padHead/appendToTail in Bytes, add testcases
Author: daviddeng Summary: Reduce the number of allocation of array when possible Add testcases Test Plan: `TestBytes` Reviewers: liyintang, manukranthk, fan Reviewed By: manukranthk CC: hbase-eng@, andrewcox, gauravm Differential Revision: https://phabricator.fb.com/D1218974 Task ID: 3839997 Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Bytes.java hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Bytes.java URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Bytes.java?rev=1577642&r1=1577641&r2=1577642&view=diff ============================================================================== --- hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Bytes.java (original) +++ hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Bytes.java Fri Mar 14 18:09:54 2014 @@ -19,8 +19,16 @@ */ package org.apache.hadoop.hbase.util; -import com.facebook.nifty.header.protocol.TFacebookCompactProtocol; -import com.facebook.swift.codec.ThriftCodec; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HConstants; @@ -33,15 +41,8 @@ import org.apache.thrift.protocol.TProto import org.apache.thrift.transport.TMemoryBuffer; import org.apache.thrift.transport.TMemoryInputTransport; -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Iterator; +import com.facebook.nifty.header.protocol.TFacebookCompactProtocol; +import com.facebook.swift.codec.ThriftCodec; /** * Utility class that handles byte arrays, conversions to/from other types, @@ -1109,13 +1110,22 @@ public class Bytes { } /** - * @param a array - * @param length amount of bytes to grab + * Returns the first elements of an array. + * + * NOTE the method may return <code>a</code> if possible. + * + * @param a + * a non-null array + * @param length + * amount of bytes to grab * @return First <code>length</code> bytes from <code>a</code> */ - public static byte [] head(final byte [] a, final int length) { - if (a.length < length) { - return null; + public static byte[] head(final byte[] a, final int length) { + if (length > a.length) { + throw new ArrayIndexOutOfBoundsException(length - 1); + } + if (length == a.length) { + return a; } byte [] result = new byte[length]; System.arraycopy(a, 0, result, 0, length); @@ -1123,13 +1133,22 @@ public class Bytes { } /** - * @param a array - * @param length amount of bytes to snarf + * Returns the last elements of an array. + * + * NOTE the method may return <code>a</code> if possible. + * + * @param a + * a non-null array + * @param length + * amount of bytes to snarf * @return Last <code>length</code> bytes from <code>a</code> */ - public static byte [] tail(final byte [] a, final int length) { - if (a.length < length) { - return null; + public static byte[] tail(final byte[] a, final int length) { + if (length > a.length) { + throw new ArrayIndexOutOfBoundsException(a.length - length); + } + if (length == a.length) { + return a; } byte [] result = new byte[length]; System.arraycopy(a, a.length - length, result, 0, length); @@ -1137,21 +1156,35 @@ public class Bytes { } /** - * @param a array - * @param length new array size - * @return Value in <code>a</code> plus <code>length</code> prepended 0 bytes - */ - public static byte [] padHead(final byte [] a, final int length) { - byte [] padding = new byte[length]; - for (int i = 0; i < length; i++) { - padding[i] = 0; + * Pads zeros in front of an array. + * + * NOTE the method may return <code>a</code> if possible. + * + * @param a + * a non-null array + * @param length + * the number of zeros to be padded in + * @return Value in <code>a</code> plus <code>length</code> prepended 0 bytes. + * could be the same instance of <code>a</code> if lenght == 0. + */ + public static byte[] padHead(final byte[] a, final int length) { + if (length == 0) { + return a; } - return add(padding,a); + byte[] res = new byte[a.length + length]; + System.arraycopy(a, 0, res, length, a.length); + return res; } /** - * @param a array - * @param length new array size + * Appends zeros at the end of <code>a</code>. + * + * NOTE the method may return <code>a</code> if possible. + * + * @param a + * array + * @param length + * new array size * @return Value in <code>a</code> plus <code>length</code> appended 0 bytes */ public static byte [] padTail(final byte [] a, final int length) { @@ -1161,18 +1194,31 @@ public class Bytes { /** * Appends length bytes to the end of the array and returns the new array * Fills byte b in the newly allocated space in the byte[]. - * @param a array - * @param length new array size - * @param b byte to write to the tail. + * + * NOTE the method may return <code>a</code> if possible. + * + * @param a + * array + * @param length + * new array size + * @param b + * byte to write to the tail. * @return Value in <code>a</code> plus <code>length</code> appended 0 bytes */ - public static byte [] appendToTail(final byte [] a, final int length, byte b) - { - byte [] padding = new byte[length]; - for (int i = 0; i < length; i++) { - padding[i] = b; + public static byte[] appendToTail(final byte[] a, int length, byte b) { + if (length == 0) { + return a; + } + int total = a.length + length; + byte[] res = new byte[total]; + System.arraycopy(a, 0, res, 0, a.length); + + if (b != 0) { + for (int i = a.length; i < total; i++) { + res[i] = b; + } } - return add(a,padding); + return res; } /** Modified: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java?rev=1577642&r1=1577641&r2=1577642&view=diff ============================================================================== --- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java (original) +++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java Fri Mar 14 18:09:54 2014 @@ -28,6 +28,8 @@ import java.util.Arrays; import junit.framework.TestCase; +import org.junit.Assert; + public class TestBytes extends TestCase { public void testNullHashCode() { byte [] b = null; @@ -170,7 +172,7 @@ public class TestBytes extends TestCase Bytes.BYTES_RAWCOMPARATOR)); } } - + public void testStartsWith() { assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("h"))); assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes(""))); @@ -214,7 +216,7 @@ public class TestBytes extends TestCase return (Bytes.toLong(testValue) + amount) == incrementResult; } - + public void testFixedSizeString() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); @@ -240,4 +242,51 @@ public class TestBytes extends TestCase assertEquals("", Bytes.readStringFixedSize(dis, 9)); } + public void testHead() throws Exception { + byte[] a = new byte[] { 1, 2, 3, 4 }; + Assert.assertArrayEquals("head(a, 0)", Bytes.head(a, 0), new byte[] {}); + Assert.assertArrayEquals("head(a, 2)", Bytes.head(a, 2), + new byte[] { 1, 2 }); + Assert.assertArrayEquals("head(a, 4)", Bytes.head(a, 4), new byte[] { 1, 2, + 3, 4 }); + try { + Bytes.head(a, 5); + fail("Should throw exception for head(a, 5)"); + } catch (ArrayIndexOutOfBoundsException e) { + // Correct + } + } + + public void testTail() throws Exception { + byte[] a = new byte[] { 1, 2, 3, 4 }; + Assert.assertArrayEquals("tail(a, 0)", Bytes.tail(a, 0), new byte[] {}); + Assert.assertArrayEquals("tail(a, 2)", Bytes.tail(a, 2), + new byte[] { 3, 4 }); + Assert.assertArrayEquals("tail(a, 4)", Bytes.tail(a, 4), new byte[] { 1, 2, + 3, 4 }); + try { + Bytes.tail(a, 5); + fail("Should throw exception for tail(a, 5)"); + } catch (ArrayIndexOutOfBoundsException e) { + // Correct + } + } + + public void testPadHead() throws Exception { + byte[] a = new byte[] { 1, 2 }; + Assert.assertArrayEquals("padHead(a, 0)", Bytes.padHead(a, 0), new byte[] { + 1, 2 }); + Assert.assertArrayEquals("padHead(a, 2)", Bytes.padHead(a, 2), new byte[] { + 0, 0, 1, 2 }); + } + + public void testAppendToTail() throws Exception { + byte[] a = new byte[] { 1, 2 }; + Assert.assertArrayEquals("appendToTail(a, 0, 0)", + Bytes.appendToTail(a, 0, (byte) 0), new byte[] { 1, 2 }); + Assert.assertArrayEquals("appendToTail(a, 2, 0)", + Bytes.appendToTail(a, 2, (byte) 0), new byte[] { 1, 2, 0, 0 }); + Assert.assertArrayEquals("appendToTail(a, 0, 6)", + Bytes.appendToTail(a, 2, (byte) 6), new byte[] { 1, 2, 6, 6 }); + } }
