/*
* Copyright (C) 2013 Omry Yadan <omry@yadan.net>
* All rights reserved.
*
* See https://github.com/omry/banana/blob/master/BSD-LICENSE for licensing information
*/
package net.yadan.banana.memory.block;
import net.yadan.banana.memory.IBlockAllocator;
import net.yadan.banana.memory.IBuffer;
import net.yadan.banana.memory.MemInitializer;
import net.yadan.banana.memory.OutOfMemoryException;
import net.yadan.banana.memory.initializers.PrototypeInitializer;
/**
* @author omry
* created 20/5/2013
*/
public class BigBlockAllocator implements IBlockAllocator {
private static final int MAX_INTS_PER_ARRAY;
static {
String max = System.getProperty("BigIntBlockAllocator.MAX_INTS_PER_ARRAY",
String.valueOf(Integer.MAX_VALUE - 8));
MAX_INTS_PER_ARRAY = Integer.parseInt(max);
}
protected final int m_blockSize;
private int m_watermark;
private int m_free;
private int m_head;
int m_buffer[][];
private int m_maxCapacity;
private MemInitializer m_initializer;
private boolean m_debug;
private double m_growthFactor;
private int m_maxBlocksPerArray;
private int m_reservedBlocks;
/**
* @param maxBlocks number of blocks to reserve space for
* @param blockSize record size in ints.
*/
public BigBlockAllocator(int maxBlocks, int blockSize) {
this(maxBlocks, blockSize, null);
}
/**
* @param maxBlocks number of records to reserve space for
* @param blockSize record size in ints.
* @param initializer a callback to initialize newly allocated records
*/
public BigBlockAllocator(int maxBlocks, int blockSize, MemInitializer initializer) {
this(maxBlocks, blockSize, 0, initializer);
}
/**
* @param maxBlocks number of records to reserve space for
* @param blockSize record size in ints.
* @param growthFactor determines by how much to grow buffer when it runs out
* of memory. 0 to disable growth
*/
public BigBlockAllocator(int maxBlocks, int blockSize, double growthFactor) {
this(maxBlocks, blockSize, growthFactor, null);
}
/**
* @param maxBlocks number of records to reserve space for
* @param blockSize record size in ints.
* @param growthFactor determines by how much to grow buffer when it runs out
* of memory. 0 to disable growth
* @param initializer a callback to initialize newly allocated records
*/
public BigBlockAllocator(int maxBlocks, int blockSize, double growthFactor,
MemInitializer initializer) {
m_reservedBlocks = 1;
m_head = -1;
m_maxCapacity = maxBlocks + m_reservedBlocks;
m_blockSize = blockSize;
m_growthFactor = growthFactor;
if (initializer == null) {
initializer = new PrototypeInitializer(blockSize);
}
m_debug = false;
m_initializer = initializer;
if (maxBlocks < 1)
throw new IllegalArgumentException("maxBlocks " + maxBlocks + " < 1");
// block 0 is reserved
long size = (m_reservedBlocks + (long) maxBlocks) * m_blockSize;
m_maxBlocksPerArray = MAX_INTS_PER_ARRAY / m_blockSize;
int maxArrayUsage = m_maxBlocksPerArray * m_blockSize;
long long_num_arrays = 1 + (size - 1) / maxArrayUsage;
if (long_num_arrays > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Attempted to allocate " + long_num_arrays
+ " arrays of int[] , which is greated than Integer.MAX_VALUE (" + Integer.MAX_VALUE
+ ")");
}
long remains = size;
int num_arrays = (int) long_num_arrays;
m_buffer = new int[num_arrays][];
m_head = -1;
m_free = 0;
m_watermark = 0;
for (int i = 0; i < num_arrays; i++) {
long allocate = Math.min(m_maxBlocksPerArray * m_blockSize, remains);
if (allocate > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Attempted to allocate " + allocate
+ " ints, which is greated than Integer.MAX_VALUE (" + Integer.MAX_VALUE + ")");
}
m_buffer[i] = new int[(int) allocate];
remains -= allocate;
}
assert remains == 0;
clear();
}
/**
* Allocates a single block and returns a pointer to that block
*
* @return pointer to newly allocated block
*
* @throws net.yadan.banana.memory.OutOfMemoryException : if there are 0 free blocks
*/
@Override
public int malloc() throws OutOfMemoryException {
if (m_head == -1) {
if (m_watermark == m_maxCapacity) {
if (m_growthFactor == 0) {
throw new OutOfMemoryException("Out of memory (" + maxBlocks() + "/" + usedBlocks()
+ " blocks used)");
} else {
increaseSize();
return malloc();
}
} else {
m_head = m_watermark;
set_next(m_head, -1);
m_watermark++;
}
} else {
m_free--;
}
int oldHead = m_head;
m_head = next(oldHead);
set_next(oldHead, -1);
m_initializer.initialize(this, oldHead, m_blockSize);
return oldHead;
}
@Override
public void memCopy(int srcPtr, int srcPos, int dstPtr, int dstPos, int length) {
assert srcPtr >= 0 : "Negative pointer : " + srcPtr;
assert dstPtr >= 0 : "Negative pointer : " + srcPtr;
assert length <= m_blockSize : "length > m_blockSize";
assert srcPos + length <= m_blockSize : "src overflow";
assert dstPos + length <= m_blockSize : "dst overflow";
int srcArray = srcPtr / m_maxBlocksPerArray;
srcPtr = srcPtr % m_maxBlocksPerArray;
int dstArray = dstPtr / m_maxBlocksPerArray;
dstPtr = dstPtr % m_maxBlocksPerArray;
System.arraycopy(m_buffer[srcArray], srcPtr * m_blockSize + srcPos, m_buffer[dstArray], dstPtr
* m_blockSize + dstPos, length);
}
@Override
public void memSet(int pointer, int srcPos, int length, int value) {
assert pointer >= 0 : "Negative pointer : " + pointer;
assert length <= m_blockSize : "length > m_blockSize";
assert srcPos + length <= m_blockSize : "overflow";
int arr = pointer / m_maxBlocksPerArray;
pointer = pointer % m_maxBlocksPerArray;
int p = pointer * m_blockSize;
for (int i = srcPos; i < srcPos + length; i++) {
m_buffer[arr][p + i] = value;
}
}
private void increaseSize() {
int currentMaxBlocks = maxBlocks();
int new_max_capacity = m_reservedBlocks
+ Math.max(currentMaxBlocks + 1, (int) (currentMaxBlocks * m_growthFactor));
int used_blocks = usedBlocks() + m_reservedBlocks;
int array_num = used_blocks / m_maxBlocksPerArray;
int last_array_blocks_used = used_blocks % m_maxBlocksPerArray;
int increase = new_max_capacity - used_blocks;
if (last_array_blocks_used != 0) {
int alloc = Math.min(increase + last_array_blocks_used, m_maxBlocksPerArray);
int new_buffer[] = new int[alloc * m_blockSize];
System.arraycopy(m_buffer[array_num], 0, new_buffer, 0, m_buffer[array_num].length);
int old_size = m_buffer[array_num].length;
m_buffer[array_num] = new_buffer;
increase -= (alloc - old_size);
}
if (increase > 0) {
int additional_required_arrays = 1 + (increase - 1) / m_maxBlocksPerArray;
if (additional_required_arrays > 0) {
int num_new_arrays = m_buffer.length + additional_required_arrays;
int new_bufer[][] = new int[num_new_arrays][];
System.arraycopy(m_buffer, 0, new_bufer, 0, m_buffer.length);
for (int i = m_buffer.length; i < num_new_arrays; i++) {
int alloc = increase;
if (increase > m_maxBlocksPerArray) {
alloc = m_maxBlocksPerArray;
}
new_bufer[i] = new int[alloc * m_blockSize];
increase -= alloc;
}
m_buffer = new_bufer;
}
}
assert increase == 0;
m_maxCapacity = new_max_capacity;
}
@Override
public void free(int pointer) {
assert pointer != 0 : "pointer 0 should not be freed";
assert pointer != -1 : "pointer -1 should not be freed";
set_next(pointer, m_head);
m_head = pointer;
m_free++;
}
@Override
public short getUpperShort(int pointer, int offset) {
assert pointer >= 0 : "Negative pointer : " + pointer;
assert offset >= 0 : "Negative offset_in_data " + offset;
assert offset < m_blockSize : String.format("offset >= m_blockSize : %d >= %d", offset, m_blockSize);
int array_num = pointer / m_maxBlocksPerArray;
int array_pointer = pointer % m_maxBlocksPerArray;
return (short) (m_buffer[array_num][array_pointer * m_blockSize + offset] >>> 16);
}
@Override
public short getLowerShort(int pointer, int offset) {
assert pointer >= 0 : "Negative pointer : " + pointer;
assert offset >= 0 : "Negative offset_in_data " + offset;
assert offset < m_blockSize : String.format("offset >= m_blockSize : %d >= %d", offset, m_blockSize);
int array_num = pointer / m_maxBlocksPerArray;
int array_pointer = pointer % m_maxBlocksPerArray;
return (short) (m_buffer[array_num][array_pointer * m_blockSize + offset]);
}
@Override
public void setUpperShort(int pointer, int offset, int s) {
assert pointer >= 0 : "Negative pointer : " + pointer;
assert offset >= 0 : "Negative offset " + offset;
assert offset < m_blockSize : String.format("offset >= m_blockSize : %d >= %d", offset, m_blockSize);
int array_num = pointer / m_maxBlocksPerArray;
int array_pointer = pointer % m_maxBlocksPerArray;
int off = array_pointer * m_blockSize + offset;
int lower = m_buffer[array_num][off] & 0x0000ffff;
m_buffer[array_num][off] = (s << 16) | lower;
}
@Override
public void setLowerShort(int pointer, int offset, int s) {
assert pointer >= 0 : "Negative pointer : " + pointer;
assert offset >= 0 : "Negative offset " + offset;
assert offset < m_blockSize : String.format("offset >= m_blockSize : %d >= %d", offset, m_blockSize);
int array_num = pointer / m_maxBlocksPerArray;
int array_pointer = pointer % m_maxBlocksPerArray;
int off = array_pointer * m_blockSize + offset;
int upper = m_buffer[array_num][off] & 0xffff0000;
m_buffer[array_num][off] = upper | (s & 0x0000ffff);
}
@Override
public int getInt(int pointer, int offset_in_data) {
assert pointer >= 0 : "Negative pointer : " + pointer;
assert offset_in_data >= 0 : "Negative offset_in_data " + offset_in_data;
assert offset_in_data < m_blockSize : String.format("offset_in_data >= m_blockSize : %d >= %d",
offset_in_data, m_blockSize);
int array_num = pointer / m_maxBlocksPerArray;
int array_pointer = pointer % m_maxBlocksPerArray;
return m_buffer[array_num][array_pointer * m_blockSize + offset_in_data];
}
@Override
public void setInt(int pointer, int offset_in_data, int data) {
assert pointer >= 0 : "Negative pointer : " + pointer;
int buffer[] = m_buffer[pointer / m_maxBlocksPerArray];
pointer = pointer % m_maxBlocksPerArray;
assert offset_in_data >= 0 : "Negative offset_in_data " + offset_in_data;
assert offset_in_data < m_blockSize : String.format("offset_in_data >= m_blockSize : %d >= %d",
offset_in_data, m_blockSize);
buffer[pointer * m_blockSize + offset_in_data] = data;
}
@Override
public void setInts(int pointer, int dst_offset_in_record,
int src_data[], int src_pos, int length) {
assert pointer >= 0 : "Negative pointer : " + pointer;
int buffer[] = m_buffer[pointer / m_maxBlocksPerArray];
pointer = pointer % m_maxBlocksPerArray;
assert src_pos >= 0 : "Negative src_pos";
assert src_pos + length <= src_data.length : String.format(
"src_pos + length > src_data.length : %d + %d > %d", src_pos, length, src_data.length);
assert pointer * m_blockSize + src_pos + length <= buffer.length : String.format(
"pointer + src_pos + length > m_buffer.length : %d + %d + %d >= %d", pointer,
src_pos, length, buffer.length);
assert dst_offset_in_record + length <= m_blockSize : String.format(
"dst_offset_in_record + length > m_blockSize : %d + %d >= %d", dst_offset_in_record,
length, m_blockSize);
System.arraycopy(src_data, src_pos, buffer, pointer * m_blockSize + dst_offset_in_record,
length);
}
@Override
public void getInts(int pointer, int src_offset_in_record,
int dst_data[], int dst_pos, int length) {
assert pointer >= 0 : "Negative pointer : " + pointer;
int array_num = pointer / m_maxBlocksPerArray;
int buffer[] = m_buffer[array_num];
int array_pointer = pointer % m_maxBlocksPerArray;
assert array_pointer * m_blockSize < buffer.length : String.format(
"pointer >= m_buffer.length : %d < %d", array_pointer, buffer.length);
assert src_offset_in_record >= 0 : String.format("src_offset_in_record < 0 : %d < 0",
src_offset_in_record);
assert src_offset_in_record < m_blockSize : String.format(
"src_offset_in_record >= m_blockSize : %d >= %d", src_offset_in_record, m_blockSize);
assert dst_pos >= 0 : String.format("dst_pos < 0 : %d", dst_pos);
assert dst_pos + length <= dst_data.length : String.format(
"dst_pos + length > dst_data.length : %d + %d >= %d", dst_pos, length, dst_data.length);
System.arraycopy(buffer, array_pointer * m_blockSize + src_offset_in_record, dst_data, dst_pos,
length);
}
@Override
public void getBuffer(int pointer, int src_offset_in_record, IBuffer dst, int length) {
getInts(pointer, src_offset_in_record, dst.array(), 0, length);
dst.setUsed(length);
}
@Override
public long getLong(int pointer, int offset_in_data) {
int ilower = getInt(pointer, offset_in_data + 1);
int iupper = getInt(pointer, offset_in_data);
long lower = 0x00000000FFFFFFFFL & ilower;
long upper = ((long) iupper) << 32;
long ret = upper | lower;
return ret;
}
@Override
public void setLong(int pointer, int offset_in_data, long data) {
// upper int
setInt(pointer, offset_in_data, (int) (data >> 32));
// lower int
setInt(pointer, offset_in_data + 1, (int) (data));
}
/**
* @return the number of free blocks
*/
@Override
public int freeBlocks() {
return m_free + m_maxCapacity - m_watermark;
}
/**
* @return the total block capacity for this allocator
*/
@Override
public int maxBlocks() {
return m_maxCapacity - m_reservedBlocks; // block 0 is reserved
}
/**
* @return number of used blocks
*/
@Override
public int usedBlocks() {
return maxBlocks() - freeBlocks();
}
/**
* @return the fixed block size for this allocator
*/
@Override
public int blockSize() {
return m_blockSize;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
try {
s.append(String.format(
"BigIntBlockAllocator %s/%s records of %d ints used, total ints allocated %d",
usedBlocks(), maxBlocks(), m_blockSize, (long) m_maxCapacity * m_blockSize));
if (m_debug) {
s.append('\n');
for (int i = m_reservedBlocks; i < m_maxCapacity; i++) {
s.append('(');
for (int j = 0; j < m_blockSize; j++) {
s.append(getInt(i, j));
if (j != m_blockSize - 1) {
s.append(',');
}
}
s.append(')');
if (i != m_maxCapacity - 1) {
s.append(',');
}
}
}
} catch (RuntimeException e) {
s.append(" :: Exception inToString() " + e.getClass().getName() + " : " + e.getMessage());
}
return s.toString();
}
private int next(int pointer) {
return getInt(pointer, 0);
}
private void set_next(int pointer, int next) {
setInt(pointer, 0, next);
}
@Override
public void clear() {
m_head = -1;
m_watermark = m_reservedBlocks;
m_free = 0;
set_next(0, -1);
}
public String debugString() {
StringBuilder sb = new StringBuilder();
for (int buffer[] : m_buffer) {
for (int d : buffer) {
sb.append(String.format("%05X", d & 0xFFFFF)).append(' ');
}
}
return sb.toString();
}
public static int getIntArraySize(int capacity, int blockSize) {
return capacity * blockSize;
}
/**
* Sets the allocator growth factor.
*
* @param d new growth factor, 0 to disable growth (default)
*/
@Override
public void setGrowthFactor(double d) {
m_growthFactor = d;
}
/**
* @return the current list growth factor
*/
@Override
public double getGrowthFactor() {
return m_growthFactor;
}
@Override
public boolean isDebug() {
return m_debug;
}
@Override
public void setDebug(boolean debug) {
m_debug = debug;
}
public MemInitializer getInitializer() {
return m_initializer;
}
@Override
public void setInitializer(MemInitializer initializer) {
m_initializer = initializer;
}
@Override
public void initialize(int pointer) {
m_initializer.initialize(this, pointer, m_blockSize);
}
@Override
public long computeMemoryUsage() {
long mem = 0;
for (int arr[] : m_buffer) {
mem += arr.length;
}
return mem * 4;
}
@Override
public int maximumCapacityFor(int pointer) {
return m_blockSize;
}
@Override
public float getFloat(int pointer, int offset) {
return Float.intBitsToFloat(getInt(pointer, offset));
}
@Override
public void setFloat(int pointer, int offset, float f) {
setInt(pointer, offset, Float.floatToIntBits(f));
}
@Override
public double getDouble(int pointer, int offset_in_data) {
return Double.longBitsToDouble(getLong(pointer, offset_in_data));
}
@Override
public void setDouble(int pointer, int offset_in_data, double data) {
setLong(pointer, offset_in_data, Double.doubleToLongBits(data));
}
@Override
public void setChars(int pointer, int dst_offset, char[] src_data, int src_pos, int num_chars) {
if (num_chars == 0) {
return;
}
int numInts = 1 + (num_chars - 1) / 2; // ceil(num_chars/2)
for (int i = dst_offset, src_index = src_pos, num_copied = 0; i < dst_offset + numInts; i++, src_index += 2) {
setUpperShort(pointer, i, src_data[src_index]);
num_copied++;
if (num_copied < num_chars) {
setLowerShort(pointer, i, src_data[src_index + 1]);
num_copied++;
}
}
}
@Override
public void getChars(int pointer, int src_offset, char[] dst_data, int dst_pos, int num_chars) {
if (num_chars == 0) {
return;
}
int numInts = 1 + (num_chars - 1) / 2; // ceil(length/2)
for (int i = src_offset, dst_index = dst_pos, num_copied = 0; i < src_offset + numInts; i++, dst_index += 2) {
dst_data[dst_index] = (char) getUpperShort(pointer, i);
num_copied++;
if (num_copied < num_chars) {
dst_data[dst_index + 1] = (char) getLowerShort(pointer, i);
num_copied++;
}
}
}
}