You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sl...@apache.org on 2011/07/29 17:33:21 UTC
svn commit: r1152265 - in /cassandra/branches/cassandra-0.8: CHANGES.txt
src/java/org/apache/cassandra/utils/ByteBufferUtil.java
src/java/org/apache/cassandra/utils/FBUtilities.java
test/unit/org/apache/cassandra/utils/ByteBufferUtilTest.java
Author: slebresne
Date: Fri Jul 29 15:33:19 2011
New Revision: 1152265
URL: http://svn.apache.org/viewvc?rev=1152265&view=rev
Log:
Speedup bytes to hex conversions dramatically
patch by dallsopp; reviewed by slebresne for CASSANDRA-2850
Modified:
cassandra/branches/cassandra-0.8/CHANGES.txt
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/ByteBufferUtil.java
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/FBUtilities.java
cassandra/branches/cassandra-0.8/test/unit/org/apache/cassandra/utils/ByteBufferUtilTest.java
Modified: cassandra/branches/cassandra-0.8/CHANGES.txt
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/CHANGES.txt?rev=1152265&r1=1152264&r2=1152265&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/CHANGES.txt (original)
+++ cassandra/branches/cassandra-0.8/CHANGES.txt Fri Jul 29 15:33:19 2011
@@ -12,6 +12,7 @@
* use lazy initialization instead of class initialization in NodeId
(CASSANDRA-2953)
* check column family validity in nodetool repair (CASSANDRA-2933)
+ * speedup bytes to hex conversions dramatically (CASSANDRA-2850)
0.8.2
Modified: cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/ByteBufferUtil.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/ByteBufferUtil.java?rev=1152265&r1=1152264&r2=1152265&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/ByteBufferUtil.java (original)
+++ cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/ByteBufferUtil.java Fri Jul 29 15:33:19 2011
@@ -445,7 +445,6 @@ public class ByteBufferUtil
return ByteBuffer.allocate(8).putDouble(0, d);
}
-
public static InputStream inputStream(ByteBuffer bytes)
{
final ByteBuffer copy = bytes.duplicate();
@@ -481,16 +480,16 @@ public class ByteBufferUtil
public static String bytesToHex(ByteBuffer bytes)
{
- StringBuilder sb = new StringBuilder();
- for (int i = bytes.position(); i < bytes.limit(); i++)
+ final int offset = bytes.position();
+ final int size = bytes.remaining();
+ final char[] c = new char[size * 2];
+ for (int i = 0; i < size; i++)
{
- int bint = bytes.get(i) & 0xff;
- if (bint <= 0xF)
- // toHexString does not 0 pad its results.
- sb.append("0");
- sb.append(Integer.toHexString(bint));
+ final int bint = bytes.get(i+offset);
+ c[i * 2] = FBUtilities.byteToChar[(bint & 0xf0) >> 4];
+ c[1 + i * 2] = FBUtilities.byteToChar[bint & 0x0f];
}
- return sb.toString();
+ return FBUtilities.wrapCharArray(c);
}
public static ByteBuffer hexToBytes(String str)
Modified: cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/FBUtilities.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/FBUtilities.java?rev=1152265&r1=1152264&r2=1152265&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/FBUtilities.java (original)
+++ cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/utils/FBUtilities.java Fri Jul 29 15:33:19 2011
@@ -19,6 +19,7 @@
package org.apache.cassandra.utils;
import java.io.*;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
@@ -62,6 +63,59 @@ public class FBUtilities
private static volatile InetAddress localInetAddress_;
+ private final static byte[] charToByte = new byte[256];
+ // package protected for use by ByteBufferUtil. Do not modify this array !!
+ static final char[] byteToChar = new char[16];
+ static
+ {
+ for (char c = 0; c < charToByte.length; ++c)
+ {
+ if (c >= '0' && c <= '9')
+ charToByte[c] = (byte)(c - '0');
+ else if (c >= 'A' && c <= 'F')
+ charToByte[c] = (byte)(c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ charToByte[c] = (byte)(c - 'a' + 10);
+ else
+ charToByte[c] = (byte)-1;
+ }
+
+ for (int i = 0; i < 16; ++i)
+ {
+ byteToChar[i] = Integer.toHexString(i).charAt(0);
+ }
+ }
+
+ /**
+ * This constructor enables us to construct a String directly by wrapping a char array, with zero-copy.
+ * This can save time, and a lot of memory, when converting large column values.
+ */
+ private static final Constructor<String> stringConstructor = getProtectedConstructor(String.class, int.class, int.class, char[].class);
+
+ /**
+ * Create a String from a char array with zero-copy (if available), using reflection to access a package-protected constructor of String.
+ * */
+ public static String wrapCharArray(char[] c)
+ {
+ if (c == null)
+ return null;
+
+ String s = null;
+
+ if (stringConstructor != null)
+ {
+ try
+ {
+ s = stringConstructor.newInstance(0, c.length, c);
+ }
+ catch (Exception e)
+ {
+ // Swallowing as we'll just use a copying constructor
+ }
+ }
+ return s == null ? new String(c) : s;
+ }
+
private static final ThreadLocal<MessageDigest> localMD5Digest = new ThreadLocal<MessageDigest>()
{
@Override
@@ -304,23 +358,22 @@ public class FBUtilities
byte[] bytes = new byte[str.length()/2];
for (int i = 0; i < bytes.length; i++)
{
- bytes[i] = (byte)Integer.parseInt(str.substring(i*2, i*2+2), 16);
+ bytes[i] = (byte)((charToByte[str.charAt(i * 2)] << 4) | charToByte[str.charAt(i*2 + 1)]);
}
return bytes;
}
public static String bytesToHex(byte... bytes)
{
- StringBuilder sb = new StringBuilder();
- for (byte b : bytes)
+ char[] c = new char[bytes.length * 2];
+ for (int i = 0; i < bytes.length; i++)
{
- int bint = b & 0xff;
- if (bint <= 0xF)
- // toHexString does not 0 pad its results.
- sb.append("0");
- sb.append(Integer.toHexString(bint));
+ int bint = bytes[i];
+ c[i * 2] = FBUtilities.byteToChar[(bint & 0xf0) >> 4];
+ c[1 + i * 2] = FBUtilities.byteToChar[bint & 0x0f];
}
- return sb.toString();
+
+ return wrapCharArray(c);
}
public static void renameWithConfirm(String tmpFilename, String filename) throws IOException
@@ -607,6 +660,28 @@ public class FBUtilities
return field;
}
+ /**
+ * Used to get access to protected/private constructor of the specified class
+ * @param klass - name of the class
+ * @param paramTypes - types of the constructor parameters
+ * @return Constructor if successful, null if the constructor cannot be
+ * accessed
+ */
+ public static Constructor getProtectedConstructor(Class klass, Class... paramTypes)
+ {
+ Constructor c;
+ try
+ {
+ c = klass.getDeclaredConstructor(paramTypes);
+ c.setAccessible(true);
+ return c;
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
public static IRowCacheProvider newCacheProvider(String cache_provider) throws ConfigurationException
{
if (!cache_provider.contains("."))
Modified: cassandra/branches/cassandra-0.8/test/unit/org/apache/cassandra/utils/ByteBufferUtilTest.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/test/unit/org/apache/cassandra/utils/ByteBufferUtilTest.java?rev=1152265&r1=1152264&r2=1152265&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/test/unit/org/apache/cassandra/utils/ByteBufferUtilTest.java (original)
+++ cassandra/branches/cassandra-0.8/test/unit/org/apache/cassandra/utils/ByteBufferUtilTest.java Fri Jul 29 15:33:19 2011
@@ -227,11 +227,26 @@ public class ByteBufferUtilTest
for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++)
{
ByteBuffer bb = ByteBuffer.allocate(1);
- bb.put((byte)i);
+ bb.put((byte) i);
bb.clear();
String s = ByteBufferUtil.bytesToHex(bb);
ByteBuffer bb2 = ByteBufferUtil.hexToBytes(s);
- assert bb.equals(bb2);
+ assertEquals(bb, bb2);
}
+ // check that non-zero buffer positions work,
+ // i.e. that conversion accounts for the buffer offset and limit correctly
+ ByteBuffer bb = ByteBuffer.allocate(4);
+ for (int i = 0; i < 4; i++)
+ {
+ bb.put((byte) i);
+ }
+ // use a chunk out of the middle of the buffer
+ bb.position(1);
+ bb.limit(3);
+ assertEquals(2, bb.remaining());
+ String s = ByteBufferUtil.bytesToHex(bb);
+ ByteBuffer bb2 = ByteBufferUtil.hexToBytes(s);
+ assertEquals(bb, bb2);
+ assertEquals("0102", s);
}
}