package net.yadan.banana.memory.malloc;
import net.yadan.banana.memory.IMemAllocator;
import net.yadan.banana.memory.OutOfMemoryException;
import net.yadan.banana.memory.initializers.MemSetInitializer;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static org.junit.Assert.*;
@RunWith(value = Parameterized.class)
public abstract class AbstractReallocTest {
public final boolean DEBUG = false;
private int m_numBlocks;
private int m_blockSize;
private List<IMemAllocator> allocators;
public AbstractReallocTest(int numBlocks, int blockSize) {
init(numBlocks, blockSize);
}
public void init(int numBlocks, int blockSize) {
m_numBlocks = numBlocks;
m_blockSize = blockSize;
allocators = new ArrayList<IMemAllocator>();
}
private IMemAllocator create() {
IMemAllocator mm = createImpl(m_numBlocks, m_blockSize);
allocators.add(mm);
return mm;
}
public abstract IMemAllocator createImpl(int numBlocks, int blockSize);
@Parameters
public static Collection<Object[]> data() {
//@formatter:off
Object[][] data = new Object[][] {
{ 10 , 3},
{ 10 , 10},
{ 100 , 3},
{ 100 , 4},
{ 100 , 5},
// { 1000 , 4},
};
return Arrays.asList(data);
}
//@formatter:on
@After
public void postTest() {
for (IMemAllocator mm : allocators) {
assertEquals("Test leaks memory", 0, mm.usedBlocks());
}
allocators.clear();
}
@Test
public void testReallocOfSingleBlock_expand() {
/*
* Tests the case where expanding a single block
*/
IMemAllocator m = create();
int blockSize = m.blockSize();
int data[] = new int[blockSize];
int out[] = new int[blockSize];
for (int i = 0; i < data.length; i++) {
data[i] = i;
}
int p1 = m.malloc(blockSize);
m.setInts(p1, 0, data, 0, data.length);
int p2 = m.realloc(p1, blockSize * 2);
m.getInts(p2, 0, out, 0, out.length);
assertTrue(p1 > 0); // single block
assertTrue(p2 < 0); // multi block
assertTrue(blockSize * 2 <= m.maximumCapacityFor(p2));
assertArrayEquals(data, out);
m.free(p2); // don't free p1!
}
@Test
public void testReallocSingleBlock_sameSize() {
/*
* Tests the case where requesting realloc for the same size for a single
* block pointer
*/
IMemAllocator m = create();
int blockSize = m.blockSize();
int data[] = new int[blockSize];
int out[] = new int[blockSize];
for (int i = 0; i < data.length; i++) {
data[i] = i;
}
int p1 = m.malloc(blockSize);
m.setInts(p1, 0, data, 0, data.length);
int p2 = m.realloc(p1, blockSize);
m.getInts(p2, 0, out, 0, out.length);
assertEquals(p1, p2);// same pointer
assertArrayEquals(data, out);
m.free(p2); // don't free p1!
}
@Test
public void testReallocMultiBlock_sameSize() {
/*
* Tests the case where requesting realloc for the same size for a multi
* block
*/
IMemAllocator m = create();
int blockSize = m.blockSize();
int data[] = new int[blockSize * 2];
int out[] = new int[blockSize * 2];
for (int i = 0; i < data.length; i++) {
data[i] = i;
}
int p1 = m.malloc(blockSize * 2);
m.setInts(p1, 0, data, 0, data.length);
int p2 = m.realloc(p1, blockSize * 2);
m.getInts(p2, 0, out, 0, out.length);
assertEquals(p1, p2);// same pointer
assertArrayEquals(data, out);
m.free(p2); // don't free p1!
}
@Test
public void testReallocMultiBlock_expand() {
/*
* Tests the case where expanding a multi block
*/
IMemAllocator m = create();
int blockSize = m.blockSize();
int data[] = new int[blockSize * 2];
int out[] = new int[blockSize];
for (int i = 0; i < data.length; i++) {
data[i] = i;
}
int p1 = m.malloc(blockSize * 2);
m.setInts(p1, 0, data, 0, data.length);
int p2 = m.realloc(p1, blockSize * 3);
m.getInts(p2, 0, out, 0, out.length);
assertTrue(blockSize * 3 <= m.maximumCapacityFor(p2));
m.free(p2); // don't free p1!
}
@Test
public void testReallocMultiBlock_shrink() {
/*
* Tests the case where shrinking a multi block to a muilti block
*/
IMemAllocator m = create();
int blockSize = m.blockSize();
int data[] = new int[blockSize * 3];
int out[] = new int[blockSize * 2];
for (int i = 0; i < data.length; i++) {
data[i] = i;
}
int p1 = m.malloc(blockSize * 3);
m.setInts(p1, 0, data, 0, data.length);
int p2 = m.realloc(p1, blockSize * 2);
m.getInts(p2, 0, out, 0, out.length);
assertTrue(p1 < 0); // multi block
assertTrue(p2 < 0); // multi block
int max = m.maximumCapacityFor(p2);
assertTrue(blockSize * 3 > max);
assertTrue(blockSize * 2 <= max);
for (int i = 0; i < out.length; i++) {
assertEquals(data[i], out[i]);
}
m.free(p2); // don't free p1!
}
@Test
public void testRealloc_expand_multiblock_OOM() {
/*
* Tests the case where expanding a multi block and getting an OOM original
* block should be unaffected and memory should not leak
*/
IMemAllocator m = create();
int alloc = m.blockSize() * m.maxBlocks() / 2;
int data[] = new int[alloc];
int out[] = new int[alloc];
for (int i = 0; i < data.length; i++) {
data[i] = i;
}
int p1 = m.malloc(alloc);
int free = m.freeBlocks();
m.setInts(p1, 0, data, 0, data.length);
try {
m.realloc(p1, alloc * 3);
fail(); // should throw
} catch (OutOfMemoryException e) {
}
assertEquals(free, m.freeBlocks());
m.getInts(p1, 0, out, 0, out.length);
assertArrayEquals(data, out);
assertTrue(alloc <= m.maximumCapacityFor(p1));
m.free(p1);
}
@Test
public void testRealloc_expand_singleblock_OOM() {
/*
* Tests the case where expanding a single block and getting an OOM original
* block should be unaffected and memory should not leak
*/
IMemAllocator m = create();
int alloc = m.blockSize();
int data[] = new int[alloc];
int out[] = new int[alloc];
for (int i = 0; i < data.length; i++) {
data[i] = i;
}
int p1 = m.malloc(alloc);
int free = m.freeBlocks();
m.setInts(p1, 0, data, 0, data.length);
try {
p1 = m.realloc(p1, m.blockSize() * (m.maxBlocks() + 5));
fail(); // should throw
} catch (OutOfMemoryException e) {
}
assertEquals(free, m.freeBlocks());
m.getInts(p1, 0, out, 0, out.length);
assertArrayEquals(data, out);
assertTrue(alloc <= m.maximumCapacityFor(p1));
m.free(p1);
}
@Test
public void reallocTestLoop() {
IMemAllocator m = create();
int blockSize = m.blockSize();
int size = blockSize;
int pointer = m.malloc(size);
int max = m.maxBlocks();
try {
for (int i = 0; i < max; i++) {
int data[] = new int[size];
int out[] = new int[size];
for (int j = 0; j < data.length; j++) {
data[j] = j;
}
m.setInts(pointer, 0, data, 0, data.length);
size += blockSize;
pointer = m.realloc(pointer, size);
m.getInts(pointer, 0, out, 0, out.length);
assertArrayEquals(data, out);
assertTrue(m.maximumCapacityFor(pointer) >= size);
}
} catch (OutOfMemoryException e) {
// expected due to allocators overhead, just run till we find max
// allocation size
}
m.free(pointer);
}
/**
* Full spectrum realloc OOM test that ensures memory structure and content
* are unmodified if an OOM is thrown during realloc
*/
@Test
public void testRealloc_singleBlockIncrement_OOM_loop() {
IMemAllocator m = create();
int blockSize = m.blockSize();
int max = m.maxBlocks();
int data[] = new int[max * blockSize];
int out[] = new int[max * blockSize];
for (int j = 0; j < data.length; j++) {
data[j] = j;
}
int initialBlockCount = 0;
int realllocFirstValue = 0;
int reallocBlockCount = -1;
boolean exit = false;
try {
for (; initialBlockCount < max && !exit; initialBlockCount++) {
if (reallocBlockCount != -1) {
reallocBlockCount = 0;
} else {
reallocBlockCount = realllocFirstValue;
}
for (; reallocBlockCount < max && !exit; reallocBlockCount++) {
int initialSize = initialBlockCount * blockSize;
int p1;
try {
p1 = m.malloc(initialSize);
} catch (OutOfMemoryException e) {
// can't allocate initial buffer, we are done
exit = true;
break;
}
int free = m.freeBlocks();
m.setInts(p1, 0, data, 0, initialSize);
String beforeRealloc = m.pointerDebugString(p1);
if (DEBUG) {
System.out.println("Before realloc:\n" + beforeRealloc);
}
try {
p1 = m.realloc(p1, reallocBlockCount * blockSize);
m.free(p1); // if didn't throw, move to next loop
continue;
} catch (OutOfMemoryException e) {
}
String afterRealloc = m.pointerDebugString(p1);
if (DEBUG) {
System.out.println("After realloc:\n" + afterRealloc);
}
assertEquals(free, m.freeBlocks());
assertEquals(beforeRealloc, afterRealloc);
assertEquals(free, m.freeBlocks());
m.getInts(p1, 0, out, 0, initialSize);
for (int k = 0; k < initialSize; k++) {
assertEquals(data[k], out[k]);
}
assertTrue(initialSize <= m.maximumCapacityFor(p1));
m.free(p1);
}
}
} catch (AssertionError e) {
e.printStackTrace();
throw new AssertionError(String.format("Error in %d -> %d", initialBlockCount,
reallocBlockCount));
}
assertEquals("Test leaks memory", 0, m.usedBlocks());
}
/**
* Full spectrum realloc test for increasing memory size
*/
@Test
public void reallocAddSingleBlockTest() {
int maxBlocks = m_numBlocks;
int blockSize = m_blockSize;
IMemAllocator m1 = create();
IMemAllocator m2 = create();
for (int n = 0; n < maxBlocks; n++) {
int p1 = -1;
int p2 = -1;
try {
int size = n * blockSize;
int newSize = (n + 1) * blockSize;
m1.setInitializer(new MemSetInitializer(-1));
m1.setDebug(true);
m2.setInitializer(new MemSetInitializer(-1));
m2.setDebug(true);
int data[] = new int[size];
int out[] = new int[size];
for (int j = 0; j < data.length; j++) {
data[j] = j;
}
p2 = m2.malloc(newSize);
m2.setInts(p2, 0, data, 0, data.length);
p1 = m1.malloc(size);
m1.setInts(p1, 0, data, 0, data.length);
if (DEBUG) {
System.out.println("Before : \n" + m1.pointerDebugString(p1));
System.out.println("Needed : \n" + m2.pointerDebugString(p2));
}
p1 = m1.realloc(p1, newSize);
if (DEBUG) {
System.out.println("After : \n" + m1.pointerDebugString(p1));
}
m1.getInts(p1, 0, out, 0, out.length);
assertArrayEquals("Size : " + size, data, out);
assertTrue("Size : " + size, m1.maximumCapacityFor(p1) >= newSize);
assertEquals("Size : " + size, m1.usedBlocks(), m2.usedBlocks());
assertEquals("Size : " + size, m1.maxBlocks(), m2.maxBlocks());
assertEquals("Size : " + size, m1.blockSize(), m2.blockSize());
assertEquals("Error growing from " + size + " to " + newSize, m1.pointerDebugString(p1),
m2.pointerDebugString(p2));
} catch (AssertionError e) {
e.printStackTrace();
throw new AssertionError("Error, n=" + n);
} catch (OutOfMemoryException e) {
// expected due to allocators overhead, just run till we find max
// allocation size
break;
} finally {
if (p1 != -1) {
m1.free(p1);
}
if (p2 != -1) {
m2.free(p2);
}
assertEquals(0, m1.usedBlocks());
assertEquals(0, m2.usedBlocks());
}
}
}
/**
* Full spectrum realloc test for decreasing memory size
*/
@Test
public void reallocRemoveSingleBlockTest() {
int maxBlocks = m_numBlocks;
int blockSize = m_blockSize;
IMemAllocator m1 = create();
IMemAllocator m2 = create();
for (int n = 1; n < maxBlocks; n++) {
try {
int size = n * blockSize;
int newSize = (n - 1) * blockSize;
m1.setInitializer(new MemSetInitializer(-1));
m1.setDebug(true);
m2.setInitializer(new MemSetInitializer(-1));
m2.setDebug(true);
int data[] = new int[newSize];
int out[] = new int[newSize];
for (int j = 0; j < data.length; j++) {
data[j] = j;
}
int p1 = m1.malloc(size);
m1.setInts(p1, 0, data, 0, data.length);
int p2 = m2.malloc(newSize);
m2.setInts(p2, 0, data, 0, data.length);
String s1 = m1.pointerDebugString(p1);
String s2 = m2.pointerDebugString(p2);
if (DEBUG) {
System.out.println("Before : \n" + s1);
System.out.println("Needed : \n" + s2);
}
p1 = m1.realloc(p1, newSize);
s1 = m1.pointerDebugString(p1);
if (DEBUG) {
System.out.println("After : \n" + s1);
}
m1.getInts(p1, 0, out, 0, out.length);
assertArrayEquals("Size : " + size, data, out);
assertTrue("Size : " + size, m1.maximumCapacityFor(p1) >= newSize);
assertEquals("Size : " + size, m1.usedBlocks(), m2.usedBlocks());
assertEquals("Size : " + size, m1.maxBlocks(), m2.maxBlocks());
assertEquals("Size : " + size, m1.blockSize(), m2.blockSize());
assertEquals("Error shrinking from " + size + " to " + newSize, s1, s2);
m1.free(p1);
m2.free(p2);
assertEquals(0, m1.usedBlocks());
assertEquals(0, m2.usedBlocks());
} catch (AssertionError e) {
e.printStackTrace();
throw new AssertionError("Error, n=" + n);
} catch (OutOfMemoryException e) {
// expected due to allocators overhead, just run till we find max
// allocation size
m1.clear();
m2.clear();
}
}
}
}