blob: 795fc63277ca3fec50dabb007d841322c6daf9c1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.ignite.internal.direct.stream.v2;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.ignite.IgniteException;
import org.apache.ignite.internal.direct.stream.DirectByteBufferStream;
import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.plugin.extensions.communication.Message;
import org.apache.ignite.plugin.extensions.communication.MessageFactory;
import org.jetbrains.annotations.Nullable;
import org.junit.Before;
import org.junit.Test;
import static org.apache.ignite.GridTestIoUtils.getCharByByteLE;
import static org.apache.ignite.GridTestIoUtils.getDoubleByByteLE;
import static org.apache.ignite.GridTestIoUtils.getFloatByByteLE;
import static org.apache.ignite.GridTestIoUtils.getIntByByteLE;
import static org.apache.ignite.GridTestIoUtils.getLongByByteLE;
import static org.apache.ignite.GridTestIoUtils.getShortByByteLE;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* {@link DirectByteBufferStreamImplV2} byte order sanity tests.
*/
public class DirectByteBufferStreamImplV2ByteOrderSelfTest {
/** Array length. */
private static final int ARR_LEN = 16;
/** Length bytes. */
private static final int LEN_BYTES = 1;
/** Rnd. */
private static final Random RND = new Random();
/** Stream. */
private DirectByteBufferStream stream;
/** Buff. */
private ByteBuffer buff;
/** Array. */
private byte[] outArr;
/** */
@Before
public void setUp() throws Exception {
outArr = new byte[ARR_LEN * 8 + LEN_BYTES];
buff = ByteBuffer.wrap(outArr);
stream = createStream(buff);
}
/**
* Creates a dummy stream on top of the provided buffer.
*
* @param buff Buffer.
* @return Stream.
*/
private static DirectByteBufferStreamImplV2 createStream(ByteBuffer buff) {
DirectByteBufferStreamImplV2 stream = new DirectByteBufferStreamImplV2(new MessageFactory() {
@Nullable @Override public Message create(short type) {
return null;
}
});
stream.setBuffer(buff);
return stream;
}
/**
*
*/
@Test
public void testShortArray() {
short[] arr = new short[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = (short)RND.nextLong();
stream.writeShortArray(arr);
buff.rewind();
for (int i = 0; i < ARR_LEN; i++)
assertEquals(arr[i], getShortByByteLE(outArr, i * 2 + LEN_BYTES));
assertArrayEquals(arr, stream.readShortArray());
}
/**
*
*/
@Test
public void testCharArray() {
char[] arr = new char[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = (char)RND.nextLong();
stream.writeCharArray(arr);
buff.rewind();
for (int i = 0; i < ARR_LEN; i++)
assertEquals(arr[i], getCharByByteLE(outArr, i * 2 + LEN_BYTES));
assertArrayEquals(arr, stream.readCharArray());
}
/**
*
*/
@Test
public void testIntArray() {
int[] arr = new int[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = RND.nextInt();
stream.writeIntArray(arr);
buff.rewind();
for (int i = 0; i < ARR_LEN; i++)
assertEquals(arr[i], getIntByByteLE(outArr, i * 4 + LEN_BYTES));
assertArrayEquals(arr, stream.readIntArray());
}
/**
*
*/
@Test
public void testLongArray() {
long[] arr = new long[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = RND.nextLong();
stream.writeLongArray(arr);
buff.rewind();
for (int i = 0; i < ARR_LEN; i++)
assertEquals(arr[i], getLongByByteLE(outArr, i * 8 + LEN_BYTES));
assertArrayEquals(arr, stream.readLongArray());
}
/**
*
*/
@Test
public void testFloatArray() {
float[] arr = new float[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = RND.nextFloat();
stream.writeFloatArray(arr);
buff.rewind();
for (int i = 0; i < ARR_LEN; i++)
assertEquals(arr[i], getFloatByByteLE(outArr, i * 4 + LEN_BYTES), 0);
assertArrayEquals(arr, stream.readFloatArray(), 0);
}
/**
*
*/
@Test
public void testDoubleArray() {
double[] arr = new double[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = RND.nextDouble();
stream.writeDoubleArray(arr);
buff.rewind();
for (int i = 0; i < ARR_LEN; i++)
assertEquals(arr[i], getDoubleByByteLE(outArr, i * 8 + LEN_BYTES), 0);
assertArrayEquals(arr, stream.readDoubleArray(), 0);
}
/**
*
*/
@Test
public void testCharArrayInternal() {
char[] arr = new char[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = (char)RND.nextInt();
testWriteArrayInternal(arr, false, false, 1);
testWriteArrayInternal(arr, false, true, 1);
testWriteArrayInternal(arr, true, false, 1);
testWriteArrayInternal(arr, true, true, 1);
testWriteArrayInternalOverflow(arr, false, false, 1);
testWriteArrayInternalOverflow(arr, false, true, 1);
testWriteArrayInternalOverflow(arr, true, false, 1);
testWriteArrayInternalOverflow(arr, true, true, 1);
}
/**
*
*/
@Test
public void testShortArrayInternal() {
short[] arr = new short[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = (short) RND.nextInt();
testWriteArrayInternal(arr, false, false, 1);
testWriteArrayInternal(arr, false, true, 1);
testWriteArrayInternal(arr, true, false, 1);
testWriteArrayInternal(arr, true, true, 1);
testWriteArrayInternalOverflow(arr, false, false, 1);
testWriteArrayInternalOverflow(arr, false, true, 1);
testWriteArrayInternalOverflow(arr, true, false, 1);
testWriteArrayInternalOverflow(arr, true, true, 1);
}
/**
*
*/
@Test
public void testIntArrayInternal() {
int[] arr = new int[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = RND.nextInt();
testWriteArrayInternal(arr, false, false,2);
testWriteArrayInternal(arr, false, true,2);
testWriteArrayInternal(arr, true, false,2);
testWriteArrayInternal(arr, true, true,2);
testWriteArrayInternalOverflow(arr, false, false,2);
testWriteArrayInternalOverflow(arr, false, true,2);
testWriteArrayInternalOverflow(arr, true, false,2);
testWriteArrayInternalOverflow(arr, true, true,2);
}
/**
*
*/
@Test
public void testLongArrayInternal() {
long[] arr = new long[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = RND.nextLong();
testWriteArrayInternal(arr, false, false, 3);
testWriteArrayInternal(arr, false, true, 3);
testWriteArrayInternal(arr, true, false, 3);
testWriteArrayInternal(arr, true, true, 3);
testWriteArrayInternalOverflow(arr, false, false, 3);
testWriteArrayInternalOverflow(arr, false, true, 3);
testWriteArrayInternalOverflow(arr, true, false, 3);
testWriteArrayInternalOverflow(arr, true, true, 3);
}
/**
*
*/
@Test
public void testFloatArrayInternal() {
float[] arr = new float[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = RND.nextFloat();
testWriteArrayInternal(arr, false, false, 2);
testWriteArrayInternal(arr, false, true, 2);
testWriteArrayInternal(arr, true, false, 2);
testWriteArrayInternal(arr, true, true, 2);
testWriteArrayInternalOverflow(arr, false, false, 2);
testWriteArrayInternalOverflow(arr, false, true, 2);
testWriteArrayInternalOverflow(arr, true, false, 2);
testWriteArrayInternalOverflow(arr, true, true, 2);
}
/**
*
*/
@Test
public void testDoubleArrayInternal() {
double[] arr = new double[ARR_LEN];
for (int i = 0; i < ARR_LEN; i++)
arr[i] = RND.nextDouble();
testWriteArrayInternal(arr, false, false, 3);
testWriteArrayInternal(arr, false, true, 3);
testWriteArrayInternal(arr, true, false, 3);
testWriteArrayInternal(arr, true, true, 3);
testWriteArrayInternalOverflow(arr, false, false, 3);
testWriteArrayInternalOverflow(arr, false, true, 3);
testWriteArrayInternalOverflow(arr, true, false, 3);
testWriteArrayInternalOverflow(arr, true, true, 3);
}
/**
* @param srcArr Source array.
* @param writeBigEndian If {@code true}, then write in big-endian mode.
* @param readBigEndian If {@code true}, then read in big-endian mode.
* @param lenShift Length shift.
* @param <T> Array type.
*/
private <T> void testWriteArrayInternal(T srcArr, boolean writeBigEndian, boolean readBigEndian, int lenShift) {
DirectByteBufferStreamImplV2 writeStream = createStream(buff);
DirectByteBufferStreamImplV2 readStream = createStream(buff);
int outBytes = (ARR_LEN << lenShift) + LEN_BYTES;
int typeSize = 1 << lenShift;
long baseOff = baseOffset(srcArr);
buff.limit(outBytes);
buff.rewind();
boolean writeRes;
if (writeBigEndian)
writeRes = writeStream.writeArrayLE(srcArr, baseOff, ARR_LEN, typeSize, lenShift);
else
writeRes = writeStream.writeArray(srcArr, baseOff, ARR_LEN, ARR_LEN << lenShift);
assertTrue(writeRes);
buff.rewind();
DirectByteBufferStreamImplV2.ArrayCreator<T> arrCreator = arrayCreator(srcArr);
T resArr;
if (readBigEndian)
resArr = readStream.readArrayLE(arrCreator, typeSize, lenShift, baseOff);
else
resArr = readStream.readArray(arrCreator, lenShift, baseOff);
assertNotNull(resArr);
if (readBigEndian != writeBigEndian)
revertByteOrder(resArr, baseOff, typeSize);
assertEquals(toList(srcArr), toList(resArr));
}
/**
* @param srcArr Source array.
* @param writeBigEndian If {@code true}, then write in big-endian mode.
* @param readBigEndian If {@code true}, then read in big-endian mode.
* @param lenShift Length shift.
* @param <T> Array type.
*/
private <T> void testWriteArrayInternalOverflow(T srcArr, boolean writeBigEndian, boolean readBigEndian, int lenShift) {
DirectByteBufferStreamImplV2 writeStream = createStream(buff);
DirectByteBufferStreamImplV2 readStream = createStream(buff);
int outBytes = (ARR_LEN << lenShift) + LEN_BYTES;
int typeSize = 1 << lenShift;
long baseOff = baseOffset(srcArr);
buff.limit(outBytes - 1);
buff.rewind();
// Write and read the first part.
boolean writeRes;
if (writeBigEndian)
writeRes = writeStream.writeArrayLE(srcArr, baseOff, ARR_LEN, typeSize, lenShift);
else
writeRes = writeStream.writeArray(srcArr, baseOff, ARR_LEN, ARR_LEN << lenShift);
assertFalse(writeRes);
buff.limit(buff.position());
buff.rewind();
DirectByteBufferStreamImplV2.ArrayCreator<T> arrCreator = arrayCreator(srcArr);
T resArr;
if (readBigEndian)
resArr = readStream.readArrayLE(arrCreator, typeSize, lenShift, baseOff);
else
resArr = readStream.readArray(arrCreator, lenShift, baseOff);
assertEquals(null, resArr);
buff.limit(outBytes);
buff.rewind();
// Write and read the second part.
if (writeBigEndian)
writeRes = writeStream.writeArrayLE(srcArr, baseOff, ARR_LEN, typeSize, lenShift);
else
writeRes = writeStream.writeArray(srcArr, baseOff, ARR_LEN, ARR_LEN << lenShift);
assertTrue(writeRes);
buff.limit(buff.position());
buff.rewind();
if (readBigEndian)
resArr = readStream.readArrayLE(arrCreator, typeSize, lenShift, baseOff);
else
resArr = readStream.readArray(arrCreator, lenShift, baseOff);
assertNotNull(resArr);
if (readBigEndian != writeBigEndian)
revertByteOrder(resArr, baseOff, typeSize);
assertEquals(toList(srcArr), toList(resArr));
}
/**
* Convert provided array into a list.
*
* @param arr Source array.
* @param <T> Array type.
* @return {@code List}, containing all elements from the array.
*/
@SuppressWarnings("unchecked")
private <T> List toList(T arr) {
List list = new ArrayList<>();
int len = Array.getLength(arr);
for (int i = 0; i < len; i++)
list.add(Array.get(arr, i));
return list;
}
/**
* Change byte order for the provided array.
*
* @param arr Source array.
* @param baseOff Base offset for the provided array.
* @param typeSize Type size.
* @param <T> Array type.
*/
private <T> void revertByteOrder(T arr, long baseOff, int typeSize) {
int len = Array.getLength(arr);
byte[] tmp = new byte[typeSize];
for (int i = 0; i < len; i++) {
for (int j = 0; j < typeSize; j++)
tmp[j] = GridUnsafe.getByteField(arr, baseOff + i * typeSize + j);
for (int j = 0; j < typeSize; j++)
GridUnsafe.putByteField(arr, baseOff + i * typeSize + j, tmp[typeSize - j - 1]);
}
}
/**
* Construct {@code ArrayCreator} for a type of a provided array.
*
* @param arr Reference array.
* @param <T> Array type.
* @return {@code ArrayCreator} for a required type.
*/
private <T> DirectByteBufferStreamImplV2.ArrayCreator<T> arrayCreator(final T arr) {
return new DirectByteBufferStreamImplV2.ArrayCreator<T>() {
@Override public T create(int len) {
if (len < 0)
throw new IgniteException("Invalid array length: " + len);
else
return (T)Array.newInstance(arr.getClass().getComponentType(), len);
}
};
}
/**
* @param arr Array.
* @param <T> Array type.
* @return Base offset for the provided array type.
*/
private <T> long baseOffset(T arr) {
if (arr.getClass().getComponentType() == char.class)
return GridUnsafe.CHAR_ARR_OFF;
else if (arr.getClass().getComponentType() == short.class)
return GridUnsafe.SHORT_ARR_OFF;
else if (arr.getClass().getComponentType() == int.class)
return GridUnsafe.INT_ARR_OFF;
else if (arr.getClass().getComponentType() == long.class)
return GridUnsafe.LONG_ARR_OFF;
else if (arr.getClass().getComponentType() == float.class)
return GridUnsafe.FLOAT_ARR_OFF;
else if (arr.getClass().getComponentType() == double.class)
return GridUnsafe.DOUBLE_ARR_OFF;
else
throw new IllegalArgumentException("Unsupported array type");
}
}