How to use JNI to call MEMCMP from Java
I need to compare 2 byte arrays and know which one is bigger or if they are equal (just equal or different is not enough). The byte arrays represent a String value of 15 characters or more. This comparison is repeated considerably in my code.
I would like to improve the bye array compare by using an equivalent of C++ memcmp method in Java (hopefully by JNI). I found an example to use DLLImport in C#, so I hope a JNI call can be applied as well.
Here is the C# code segment:
[DllImport开发者_开发知识库("msvcrt.dll")]
unsafe static extern int memcmp(void* b1, void* b2, long count);
unsafe static int ByteArrayCompare1(byte[] b1, int b1Index, int b1Length, byte[] b2, int b2Index, int b2Length)
{
CompareCount++;
fixed (byte* p1 = b1)
fixed (byte* p2 = b2)
{
int cmp = memcmp(p1 + b1Index, p2 + b2Index, Math.Min(b1Length, b2Length));
if (cmp == 0)
{
cmp = b1Length.CompareTo(b2Length);
}
return cmp;
}
}
Does anyone know how to implement this in Java?
Thanks in advance,
Diana
Are you certain your code is spending significant time on those comparisons? I would suggest calling a Java function for now, and then timing it; if you still need to, you can add JNI/JNA.
Remember that by adding JNI you increase your chance of bugs significantly, and you limit your program to only the architectures that you compile the library for.
You can use JNI, but Java has a variation on JNI called JNA (Java Native Access), which lets you access shared libraries directly without needing a JNI interface wrapped around them, so you can use that to access memcmp
directly:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class Test {
public interface CStdLib extends Library {
int memcmp(Pointer s1, Pointer s2, int n);
}
public static void main(String[] args) {
CStdLib c = (CStdLib)Native.loadLibrary("msvcrt", CStdLib.class);
c.memcmp(...);
}
}
I've not tested the above, and I'm not sure in particular about the memcmp
signature, since it takes void*
and size_t
, which both don't have obvious Java equivalents, but some variation of that should work correctly
(Attribution: I pulled some of the JNA info from another answer of mine)
Just take the following code and see if it is fast enough.
package so3883485;
import java.util.concurrent.atomic.AtomicLong;
public class ByteArrayUtils {
static final AtomicLong COMPARE_COUNT = new AtomicLong(0);
public static int compare(byte[] b1, int b1Index, int b1Length, byte[] b2, int b2Index, int b2Length) {
COMPARE_COUNT.incrementAndGet();
final int commonLength = Math.min(b1Length, b2Length);
for (int i = 0; i < commonLength; i++) {
final byte byte1 = b1[b1Index + i];
final byte byte2 = b2[b2Index + i];
if (byte1 != byte2) {
return (byte1 < byte2) ? -1 : 1;
}
}
if (b1Length != b2Length) {
return (b1Length < b2Length) ? -2 : 2;
}
return 0;
}
}
And some unit test to make sure the basic cases work as expected.
package so3883485;
import static org.junit.Assert.*;
import static so3883485.ByteArrayUtils.*;
import org.junit.Test;
public class ByteArrayUtilsTest {
@Test
public void test() {
byte[] bytes = { 1, 2, 3, 4, 5 };
assertEquals(0, compare(bytes, 0, bytes.length, bytes, 0, bytes.length));
assertEquals(0, compare(bytes, 0, 0, bytes, 0, 0));
assertEquals(-2, compare(bytes, 0, 0, bytes, 0, 1));
assertEquals(2, compare(bytes, 0, 1, bytes, 0, 0));
assertEquals(-1, compare(bytes, 1, 1, bytes, 2, 1));
assertEquals(1, compare(bytes, 2, 1, bytes, 1, 1));
}
}
精彩评论