/*
* 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.flink.core.memory;
import org.junit.Test;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class CrossSegmentTypeTest {
private final int pageSize = 32*1024;
// ------------------------------------------------------------------------
@Test
public void testCompareBytesMixedSegments() {
try {
MemorySegment[] segs1 = {
new HeapMemorySegment(new byte[pageSize]),
new HybridMemorySegment(new byte[pageSize]),
new HybridMemorySegment(ByteBuffer.allocateDirect(pageSize))
};
MemorySegment[] segs2 = {
new HeapMemorySegment(new byte[pageSize]),
new HybridMemorySegment(new byte[pageSize]),
new HybridMemorySegment(ByteBuffer.allocateDirect(pageSize))
};
Random rnd = new Random();
for (MemorySegment seg1 : segs1) {
for (MemorySegment seg2 : segs2) {
testCompare(seg1, seg2, rnd);
}
}
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
private void testCompare(MemorySegment seg1, MemorySegment seg2, Random random) {
assertEquals(pageSize, seg1.size());
assertEquals(pageSize, seg2.size());
final byte[] bytes1 = new byte[pageSize];
final byte[] bytes2 = new byte[pageSize];
final int stride = pageSize / 255;
final int shift = 16666;
for (int i = 0; i < pageSize; i++) {
byte val = (byte) ((i / stride) & 0xff);
bytes1[i] = val;
if (i + shift < bytes2.length) {
bytes2[i + shift] = val;
}
}
seg1.put(0, bytes1);
seg2.put(0, bytes2);
for (int i = 0; i < 1000; i++) {
int pos1 = random.nextInt(bytes1.length);
int pos2 = random.nextInt(bytes2.length);
int len = Math.min(Math.min(bytes1.length - pos1, bytes2.length - pos2),
random.nextInt(pageSize / 50 ));
int cmp = seg1.compare(seg2, pos1, pos2, len);
if (pos1 < pos2 - shift) {
assertTrue(cmp <= 0);
}
else {
assertTrue(cmp >= 0);
}
}
}
@Test
public void testSwapBytesMixedSegments() {
try {
final int HALF_SIZE = pageSize / 2;
MemorySegment[] segs1 = {
new HeapMemorySegment(new byte[pageSize]),
new HybridMemorySegment(new byte[pageSize]),
new HybridMemorySegment(ByteBuffer.allocateDirect(pageSize))
};
MemorySegment[] segs2 = {
new HeapMemorySegment(new byte[HALF_SIZE]),
new HybridMemorySegment(new byte[HALF_SIZE]),
new HybridMemorySegment(ByteBuffer.allocateDirect(HALF_SIZE))
};
Random rnd = new Random();
for (MemorySegment seg1 : segs1) {
for (MemorySegment seg2 : segs2) {
testSwap(seg1, seg2, rnd, HALF_SIZE);
}
}
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
private void testSwap(MemorySegment seg1, MemorySegment seg2, Random random, int smallerSize) {
assertEquals(pageSize, seg1.size());
assertEquals(smallerSize, seg2.size());
final byte[] bytes1 = new byte[pageSize];
final byte[] bytes2 = new byte[smallerSize];
Arrays.fill(bytes2, (byte) 1);
seg1.put(0, bytes1);
seg2.put(0, bytes2);
// wap the second half of the first segment with the second segment
int pos = 0;
while (pos < smallerSize) {
int len = random.nextInt(pageSize / 40);
len = Math.min(len, smallerSize - pos);
seg1.swapBytes(new byte[len], seg2, pos + smallerSize, pos, len);
pos += len;
}
// the second segment should now be all zeros, the first segment should have one in its second half
for (int i = 0; i < smallerSize; i++) {
assertEquals((byte) 0, seg1.get(i));
assertEquals((byte) 0, seg2.get(i));
assertEquals((byte) 1, seg1.get(i + smallerSize));
}
}
@Test
public void testCopyMixedSegments() {
try {
MemorySegment[] segs1 = {
new HeapMemorySegment(new byte[pageSize]),
new HybridMemorySegment(new byte[pageSize]),
new HybridMemorySegment(ByteBuffer.allocateDirect(pageSize))
};
MemorySegment[] segs2 = {
new HeapMemorySegment(new byte[pageSize]),
new HybridMemorySegment(new byte[pageSize]),
new HybridMemorySegment(ByteBuffer.allocateDirect(pageSize))
};
Random rnd = new Random();
for (MemorySegment seg1 : segs1) {
for (MemorySegment seg2 : segs2) {
testCopy(seg1, seg2, rnd);
}
}
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
private void testCopy(MemorySegment seg1, MemorySegment seg2, Random random) {
assertEquals(pageSize, seg1.size());
assertEquals(pageSize, seg2.size());
byte[] expected = new byte[pageSize];
byte[] actual = new byte[pageSize];
// zero out the memory
seg1.put(0, expected);
seg2.put(0, expected);
for (int i = 0; i < 40; i++) {
int numBytes = random.nextInt(pageSize / 20);
byte[] bytes = new byte[numBytes];
random.nextBytes(bytes);
int thisPos = random.nextInt(pageSize - numBytes);
int otherPos = random.nextInt(pageSize - numBytes);
// track what we expect
System.arraycopy(bytes, 0, expected, otherPos, numBytes);
seg1.put(thisPos, bytes);
seg1.copyTo(thisPos, seg2, otherPos, numBytes);
}
seg2.get(0, actual);
assertArrayEquals(expected, actual);
// test out of bound conditions
final int[] validOffsets = { 0, 1, pageSize / 10 * 9 };
final int[] invalidOffsets = { -1, pageSize + 1, -pageSize, Integer.MAX_VALUE, Integer.MIN_VALUE };
final int[] validLengths = { 0, 1, pageSize / 10, pageSize };
final int[] invalidLengths = { -1, -pageSize, pageSize + 1, Integer.MAX_VALUE, Integer.MIN_VALUE };
for (int off1 : validOffsets) {
for (int off2 : validOffsets) {
for (int len : invalidLengths) {
try {
seg1.copyTo(off1, seg2, off2, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg1.copyTo(off2, seg2, off1, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg2.copyTo(off1, seg1, off2, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg2.copyTo(off2, seg1, off1, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
}
}
}
for (int off1 : validOffsets) {
for (int off2 : invalidOffsets) {
for (int len : validLengths) {
try {
seg1.copyTo(off1, seg2, off2, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg1.copyTo(off2, seg2, off1, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg2.copyTo(off1, seg1, off2, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg2.copyTo(off2, seg1, off1, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
}
}
}
for (int off1 : invalidOffsets) {
for (int off2 : validOffsets) {
for (int len : validLengths) {
try {
seg1.copyTo(off1, seg2, off2, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg1.copyTo(off2, seg2, off1, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg2.copyTo(off1, seg1, off2, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg2.copyTo(off2, seg1, off1, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
}
}
}
for (int off1 : invalidOffsets) {
for (int off2 : invalidOffsets) {
for (int len : validLengths) {
try {
seg1.copyTo(off1, seg2, off2, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg1.copyTo(off2, seg2, off1, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg2.copyTo(off1, seg1, off2, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
try {
seg2.copyTo(off2, seg1, off1, len);
fail("should fail with an IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException ignored) {}
}
}
}
}
}