package io.eguan.hash;
/*
* #%L
* Project eguan
* %%
* Copyright (C) 2012 - 2017 Oodrive
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import io.eguan.hash.MD5Native;
import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Test;
import com.google.protobuf.ByteString;
public class TestMD5Native {
@Test
public void testMD54k() throws NoSuchAlgorithmException {
final byte[] src = new byte[4096];
new SecureRandom().nextBytes(src);
for (int i = 0; i <= 4096; i++) {
for (int j = 0; j < i; j += 30) {
final byte[] digLocalDirect = digestLocalDirect(src, j, i);
final byte[] digLocalWrap = digestLocalWrap(src, j, i);
final byte[] digJvm = digestJvm(src, j, i);
Assert.assertTrue(Arrays.equals(digJvm, digLocalDirect));
Assert.assertTrue(Arrays.equals(digJvm, digLocalWrap));
}
}
}
private byte[] digestLocalDirect(final byte[] src, final int offsetStart, final int offsetEnd) {
final ByteBuffer buffer = ByteBuffer.allocateDirect(src.length);
buffer.put(src).clear();
buffer.position(offsetStart).limit(offsetEnd);
return MD5Native.hashToByteArray(buffer);
}
private byte[] digestLocalWrap(final byte[] src, final int offsetStart, final int offsetEnd) {
final ByteBuffer buffer = ByteBuffer.wrap(src);
buffer.position(offsetStart).limit(offsetEnd);
return MD5Native.hashToByteArray(buffer);
}
private byte[] digestJvm(final byte[] src, final int offsetStart, final int offsetEnd)
throws NoSuchAlgorithmException {
final MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(src, offsetStart, offsetEnd - offsetStart);
return digest.digest();
}
@Test
public void testMD5ByteString() throws NoSuchAlgorithmException {
final byte[] src = new byte[4096];
new SecureRandom().nextBytes(src);
// MD5 sum JVM
final byte[] digJvm = digestJvm(src, 0, src.length);
// MD5 sum native ByteString
final ByteString byteString = ByteString.copyFrom(src);
final byte[] digLocal = MD5Native.hashToByteArray(byteString);
// Compare
Assert.assertTrue(Arrays.equals(digJvm, digLocal));
}
@Test
public void testMD5Perf() throws Exception {
testMD5Perf(true);
}
/**
* Real world usage of local hash function. Base code for a performance comparison campaign.
*
* @param callAssert
* @throws DigestException
* @throws NoSuchAlgorithmException
*/
private void testMD5Perf(final boolean callAssert) throws DigestException, NoSuchAlgorithmException {
// Prepare buffer
final int len = 4096;
final int loopCount = 40960;
final byte[] src = new byte[len];
new SecureRandom().nextBytes(src);
final ByteBuffer bufJvm = ByteBuffer.allocateDirect(len);
bufJvm.put(src).rewind();
final ByteBuffer bufLocal = ByteBuffer.allocateDirect(len);
bufLocal.put(src).rewind();
// Count JVM implementation time
final byte[] digJvm = new byte[16];
{
final long startJvm = System.nanoTime();
for (int i = 0; i < loopCount; i++) {
final MessageDigest digest = MessageDigest.getInstance("MD5");
// Get data from ByteBuffer
final byte[] blockCopy = new byte[len];
bufJvm.position(0);
bufJvm.limit(len);
bufJvm.get(blockCopy);
digest.update(blockCopy, 0, len);
digest.digest(digJvm, 0, digJvm.length);
bufJvm.rewind();
}
final long endJvm = System.nanoTime();
System.out.println("Jvm impl " + ((float) loopCount / ((float) (endJvm - startJvm)) * 1000000000)
+ " hash/seconds");
}
// Count local implementation time
final ByteBuffer digLocal = ByteBuffer.allocateDirect(16);
final long startLocal = System.nanoTime();
for (int i = 0; i < loopCount; i++) {
MD5Native.hash(bufLocal, digLocal);
digLocal.rewind();
}
final long endLocal = System.nanoTime();
System.out.println("Local impl " + ((float) loopCount / ((float) (endLocal - startLocal)) * 1000000000)
+ " hash/seconds");
if (callAssert) {
final byte[] digLocalArray = new byte[16];
digLocal.get(digLocalArray);
Assert.assertTrue(Arrays.equals(digJvm, digLocalArray));
}
}
/**
* Call perf test.
*
* @param ignored
*/
public static void main(final String[] ignored) {
try {
new TestMD5Native().testMD5Perf(false);
}
catch (DigestException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}