/* * 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.malloc; import java.util.LinkedList; import java.util.Queue; import net.yadan.banana.memory.IBlockAllocator; import net.yadan.banana.memory.IBuffer; import net.yadan.banana.memory.IMemAllocator; import net.yadan.banana.memory.MemInitializer; import net.yadan.banana.memory.OutOfMemoryException; import net.yadan.banana.memory.block.BlockAllocator; //TODO: simplify public class TreeAllocator implements IMemAllocator { private static final int INDEX_NUM_BLOCKS_OFFSET = 0; private static final int INDEX_DATA_OFFSET = 1; IBlockAllocator m_blocks; private final int m_blockSize; private final int m_indexBlockCapacity; public TreeAllocator(int maxBlocks, int blockSize) { this(maxBlocks, blockSize, 0, null); } public TreeAllocator(int maxBlocks, int blockSize, MemInitializer initializer) { this(maxBlocks, blockSize, 0, initializer); } public TreeAllocator(int maxBlocks, int blockSize, double growthFactor) { this(maxBlocks, blockSize, growthFactor, null); } public TreeAllocator(int maxBlocks, int blockSize, double growthFactor, MemInitializer initializer) { this(new BlockAllocator(maxBlocks, blockSize, growthFactor, initializer)); } public TreeAllocator(IBlockAllocator blocks) { m_blockSize = blocks.blockSize(); m_indexBlockCapacity = m_blockSize - INDEX_DATA_OFFSET; m_blocks = blocks; } @Override public int malloc(int size) throws OutOfMemoryException { if (size < 0) { throw new IllegalArgumentException("malloc size must be non-negative"); } if (size <= m_blockSize) { return m_blocks.malloc(); } else { if (m_blockSize <= 2) { throw new UnsupportedOperationException( "TreeIntAllocator Multi block allocation requires minimum block size of 3"); } return ~multiBlockMalloc(size); } } private int multiBlockMalloc(int size) { int indexPointer = m_blocks.malloc(); try { int numDataBlocks = 1 + ((size - 1) / m_blockSize); // ceil(a/b) setInt(indexPointer, INDEX_NUM_BLOCKS_OFFSET, numDataBlocks); // capacity of index that supports memory size int maxCapacity = maximumCapacityForNumBlocks(numDataBlocks); if (m_indexBlockCapacity >= numDataBlocks) { // allocating data int blockNum = 0; try { for (; blockNum < numDataBlocks; blockNum++) { m_blocks.setInt(indexPointer, INDEX_DATA_OFFSET + blockNum, m_blocks.malloc()); } } catch (OutOfMemoryException e) { for (int i = 0; i < blockNum; i++) { int p = m_blocks.getInt(indexPointer, INDEX_DATA_OFFSET + i); m_blocks.free(p); } throw e; } } else { // allocating index int nextLevelCapacity = maxCapacity / m_indexBlockCapacity; int numIndexBlocksToAllocate = 1 + (size - 1) / nextLevelCapacity; int left = size; int indexedMemorySize = nextLevelCapacity; int blockNum = 0; try { for (; blockNum < numIndexBlocksToAllocate; blockNum++) { if (left <= nextLevelCapacity) indexedMemorySize = left; left -= indexedMemorySize; assert left >= 0; if (indexedMemorySize <= m_blockSize) { m_blocks.setInt(indexPointer, INDEX_DATA_OFFSET + blockNum, m_blocks.malloc()); } else { int p = multiBlockMalloc(indexedMemorySize); m_blocks.setInt(indexPointer, INDEX_DATA_OFFSET + blockNum, ~p); } } } catch (OutOfMemoryException e) { for (int i = 0; i < blockNum; i++) { int p = m_blocks.getInt(indexPointer, INDEX_DATA_OFFSET + i); free(p); } throw e; } } return indexPointer; } catch (OutOfMemoryException e) { m_blocks.free(indexPointer); throw e; } } @Override public int realloc(int pointer, int size) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (size < 0) { throw new IllegalArgumentException("malloc size must be non-negative"); } if (pointer > 0) { if (size <= m_blockSize) { return pointer; } else { // single block int ret = malloc(size); int p = findPointerForOffset(ret, 0); m_blocks.memCopy(pointer, 0, p, 0, m_blockSize); m_blocks.free(pointer); return ret; } } else { // multi block int directPointer = ~pointer; int numBlocks = m_blocks.getInt(directPointer, INDEX_NUM_BLOCKS_OFFSET); int neededBlocks = 1 + ((size - 1) / m_blockSize); // ceil(a/b) if (neededBlocks > numBlocks) { int initialSize = maximumCapacityFor(pointer); try { while (numBlocks++ < neededBlocks) { appendBlock(pointer); } } catch (OutOfMemoryException e) { realloc(pointer, initialSize); throw e; } } else if (numBlocks > neededBlocks) { while (numBlocks-- > neededBlocks) { removeBlock(pointer); } if (neededBlocks < 2 && pointer < 0) { pointer = ~pointer; } } return pointer; } } private void removeBlock(int pointer) { assert pointer < 0; int rootPtr = ~pointer; int numBlocks = m_blocks.getInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET); int last_block = numBlocks - 1; while (true) { int maxCap = maximumCapacityForNumBlocks(numBlocks) / m_blockSize; int nextLevelMaxCap = maxCap / m_indexBlockCapacity; if (numBlocks - 1 == nextLevelMaxCap) { // full tree, need to remove a level int removedBlock1 = m_blocks.getInt(rootPtr, INDEX_DATA_OFFSET + 0); int removedBlock2 = m_blocks.getInt(rootPtr, INDEX_DATA_OFFSET + 1); removedBlock1 = removedBlock1 > 0 ? removedBlock1 : ~removedBlock1; m_blocks.memCopy(removedBlock1, 0, rootPtr, 0, m_blockSize); free(removedBlock1); free(removedBlock2); break; } int blockNum = last_block / nextLevelMaxCap; last_block -= blockNum * nextLevelMaxCap; if (nextLevelMaxCap > 1) { int p = m_blocks.getInt(rootPtr, INDEX_DATA_OFFSET + blockNum); if (last_block == 0) { m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks - 1); int removedBlock = m_blocks.getInt(rootPtr, INDEX_DATA_OFFSET + blockNum); free(removedBlock); break; } else if (p > 0) { // data pointer m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks - 1); int removedBlock1 = m_blocks.getInt(rootPtr, INDEX_DATA_OFFSET + blockNum); int removedBlock2 = m_blocks.getInt(removedBlock1, INDEX_DATA_OFFSET + 1); int preservedBlock = m_blocks.getInt(removedBlock1, INDEX_DATA_OFFSET + 0); m_blocks.setInt(rootPtr, INDEX_DATA_OFFSET + 1, preservedBlock); free(removedBlock1); free(removedBlock2); break; } else { // p < 0, index pointer m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks - 1); int nextLevelMemSize = m_blocks.getInt(~p, INDEX_NUM_BLOCKS_OFFSET) * m_blockSize; if (nextLevelMemSize - m_blockSize == m_blockSize) { // index block referencing two data blocks, removal of one data block // will cause removal of index block int removedBlock1 = ~p; int removedBlock2 = m_blocks.getInt(removedBlock1, INDEX_DATA_OFFSET + 1); int preservedBlock = m_blocks.getInt(removedBlock1, INDEX_DATA_OFFSET + 0); m_blocks.setInt(rootPtr, INDEX_DATA_OFFSET + blockNum, preservedBlock); free(removedBlock1); free(removedBlock2); break; } else { // keep digging down rootPtr = ~p; } } } else { m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks - 1); int removedBlock = m_blocks.getInt(rootPtr, INDEX_DATA_OFFSET + blockNum); free(removedBlock); break; } numBlocks = m_blocks.getInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET); } } private void appendBlock(int pointer) { assert pointer < 0; int rootPtr = ~pointer; int numBlocks = m_blocks.getInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET); int memSize = numBlocks * m_blockSize; int newBlock1 = m_blocks.malloc(); int newBlock2; try { newBlock2 = m_blocks.malloc(); } catch (OutOfMemoryException e) { free(newBlock1); throw e; } int offset_in_data = memSize; while (true) { int maxCap = maximumCapacityForNumBlocks(numBlocks); if (memSize == maxCap) { // full tree, need to add a level m_blocks.memCopy(rootPtr, 0, newBlock1, 0, m_blockSize); // zero out new root, not strictly needed but nice to have for clarity m_blocks.memSet(rootPtr, 0, m_blockSize, 0); m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks + 1); m_blocks.setInt(rootPtr, INDEX_DATA_OFFSET + 0, ~newBlock1); m_blocks.setInt(rootPtr, INDEX_DATA_OFFSET + 1, newBlock2); break; } int maxCapacityPerIndexPtr = maxCap / m_indexBlockCapacity; int blockNum = offset_in_data / maxCapacityPerIndexPtr; offset_in_data -= blockNum * maxCapacityPerIndexPtr; if (maxCapacityPerIndexPtr > m_blockSize) { int p = m_blocks.getInt(rootPtr, INDEX_DATA_OFFSET + blockNum); if (offset_in_data == 0) { m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks + 1); m_blocks.setInt(rootPtr, INDEX_DATA_OFFSET + blockNum, newBlock1); free(newBlock2); break; } else if (p > 0) { // data pointer int newIndexBlock = newBlock2; m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks + 1); m_blocks.setInt(rootPtr, INDEX_DATA_OFFSET + blockNum, ~newIndexBlock); m_blocks.setInt(newIndexBlock, INDEX_NUM_BLOCKS_OFFSET, 2); m_blocks.setInt(newIndexBlock, INDEX_DATA_OFFSET + 0, p); m_blocks.setInt(newIndexBlock, INDEX_DATA_OFFSET + 1, newBlock1); break; } else { // index pointer, keep digging down m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks + 1); rootPtr = ~p; } } else { m_blocks.setInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET, numBlocks + 1); m_blocks.setInt(rootPtr, INDEX_DATA_OFFSET + blockNum, newBlock1); free(newBlock2); break; } numBlocks = m_blocks.getInt(rootPtr, INDEX_NUM_BLOCKS_OFFSET); memSize = numBlocks * m_blockSize; } } // TODO: re-eval this function private int findPointerForOffset(int pointer, int offset_in_data) { int indexPointer = ~pointer; int numBlocks = m_blocks.getInt(indexPointer, INDEX_NUM_BLOCKS_OFFSET); int maxCapacityPerIndexPtr = maximumCapacityForNumBlocks(numBlocks) / m_indexBlockCapacity; int blockNum = offset_in_data / maxCapacityPerIndexPtr; int dataPtr = m_blocks.getInt(indexPointer, INDEX_DATA_OFFSET + blockNum); if (maxCapacityPerIndexPtr <= m_blockSize || dataPtr >= 0) { return dataPtr; } else { int new_offset_in_data = offset_in_data - blockNum * maxCapacityPerIndexPtr; return findPointerForOffset(dataPtr, new_offset_in_data); } } @Override public void free(int pointer) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { int indexPointer = ~pointer; int nb = m_blocks.getInt(indexPointer, INDEX_NUM_BLOCKS_OFFSET); int memSize = nb * m_blockSize; int maxCapacityFor = maximumCapacityForNumBlocks(nb) / m_indexBlockCapacity; int numBlocks = 1 + ((memSize - 1) / maxCapacityFor); // ceil(a/b) for (int i = 0; i < numBlocks; i++) { int p = m_blocks.getInt(indexPointer, i + INDEX_DATA_OFFSET); free(p); } free(indexPointer); } else { m_blocks.free(pointer); } } @Override public short getUpperShort(int pointer, int offset) { if (pointer < 0) { pointer = getDataBlockPointerFor(pointer, offset); offset = retOffset; } return m_blocks.getUpperShort(pointer, offset); } @Override public short getLowerShort(int pointer, int offset) { if (pointer < 0) { pointer = getDataBlockPointerFor(pointer, offset); offset = retOffset; } return m_blocks.getLowerShort(pointer, offset); } @Override public void setUpperShort(int pointer, int offset, int s) { if (pointer < 0) { pointer = getDataBlockPointerFor(pointer, offset); offset = retOffset; } m_blocks.setUpperShort(pointer, offset, s); } @Override public void setLowerShort(int pointer, int offset, int s) { if (pointer < 0) { pointer = getDataBlockPointerFor(pointer, offset); offset = retOffset; } m_blocks.setLowerShort(pointer, offset, s); } @Override public final void setInt(int pointer, int offset_in_data, int data) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { setIntDataForIndexedBlock(pointer, data, offset_in_data); } else { m_blocks.setInt(pointer, offset_in_data, data); } } protected void setIntDataForIndexedBlock(int pointer, int data, int offset_in_data) { int indexPointer = ~pointer; int numBlocks = m_blocks.getInt(indexPointer, INDEX_NUM_BLOCKS_OFFSET); int maxCapacityPerIndexPtr = maximumCapacityForNumBlocks(numBlocks) / m_indexBlockCapacity; int blockNum = offset_in_data / maxCapacityPerIndexPtr; int dataPtr = m_blocks.getInt(indexPointer, INDEX_DATA_OFFSET + blockNum); if (maxCapacityPerIndexPtr <= m_blockSize || dataPtr >= 0) { int new_offset_in_data = offset_in_data - blockNum * maxCapacityPerIndexPtr; m_blocks.setInt(dataPtr, new_offset_in_data, data); } else { int new_offset_in_data = offset_in_data - blockNum * maxCapacityPerIndexPtr; setIntDataForIndexedBlock(dataPtr, data, new_offset_in_data); } } @Override public int getInt(int pointer, int offset_in_data) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { return getIntDataForIndexedBlock(pointer, offset_in_data); } else { return m_blocks.getInt(pointer, offset_in_data); } } public int getIntDataForIndexedBlock(int pointer, int offset_in_data) { int indexPointer = ~pointer; int numBlocks = m_blocks.getInt(indexPointer, INDEX_NUM_BLOCKS_OFFSET); int maxCapacity = maximumCapacityForNumBlocks(numBlocks) / m_indexBlockCapacity; int blockNum = offset_in_data / maxCapacity; int dataPtr = m_blocks.getInt(indexPointer, INDEX_DATA_OFFSET + blockNum); if (maxCapacity <= m_blockSize || dataPtr >= 0) { int new_offset_in_data = offset_in_data - blockNum * maxCapacity; return m_blocks.getInt(dataPtr, new_offset_in_data); } else { int new_offset_in_data = offset_in_data - blockNum * maxCapacity; return getIntDataForIndexedBlock(dataPtr, new_offset_in_data); } } @Override public void setInts(int pointer, int dst_offset_in_record, int src_data[], int src_pos, int length) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { int numBlocks = m_blocks.getInt(~pointer, INDEX_NUM_BLOCKS_OFFSET); int currentLevelCapacity = maximumCapacityForNumBlocks(numBlocks); assert src_pos >= 0 : "Negative src_pos " + src_pos; assert dst_offset_in_record + length <= numBlocks * m_blockSize : String.format( "src_pos + length > memSize : %d + %d < %d", src_pos, length, numBlocks * m_blockSize); int left_to_copy = length; int dst_offset = dst_offset_in_record; for (int src_offset = src_pos; src_offset < length;) { int this_copy_length; if (dst_offset % m_blockSize != 0) { this_copy_length = m_blockSize - dst_offset % m_blockSize; } else { if (left_to_copy > m_blockSize) { this_copy_length = m_blockSize; } else { this_copy_length = left_to_copy; } } int dataPtr = pointer; int current_dst_offset = dst_offset; int thisLevelCapacity = currentLevelCapacity; while (dataPtr < 0) { int directDataPtr = ~dataPtr; int levelMemSize = m_blocks.getInt(directDataPtr, INDEX_NUM_BLOCKS_OFFSET) * m_blockSize; int nextLevelCapacity = thisLevelCapacity / m_indexBlockCapacity; while (nextLevelCapacity >= levelMemSize) { nextLevelCapacity /= m_indexBlockCapacity; } int offset_in_index = current_dst_offset / nextLevelCapacity; current_dst_offset = current_dst_offset % nextLevelCapacity; dataPtr = m_blocks.getInt(directDataPtr, INDEX_DATA_OFFSET + offset_in_index); thisLevelCapacity = nextLevelCapacity; } m_blocks.setInts(dataPtr, current_dst_offset, src_data, src_pos + src_offset, this_copy_length); src_offset += this_copy_length; left_to_copy -= this_copy_length; dst_offset += this_copy_length; } } else { m_blocks.setInts(pointer, dst_offset_in_record, src_data, src_pos, length); } } @Override public void setChars(int pointer, int dst_int_offset, char[] src_char_data, int src_char_pos, int num_chars) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (num_chars == 0) { return; } if (pointer < 0) { int numBlocks = m_blocks.getInt(~pointer, INDEX_NUM_BLOCKS_OFFSET); int currentLevelCapacity = maximumCapacityForNumBlocks(numBlocks); int num_ints = 1 + (num_chars - 1) / 2; // ceil (num_chars / 2) assert src_char_pos >= 0 : "Negative src_pos " + src_char_pos; assert dst_int_offset + num_ints <= numBlocks * m_blockSize : String .format("dst_offset_in_record + num_ints > memSize : %d + %d < %d", dst_int_offset, num_ints, numBlocks * m_blockSize); int chars_left_to_copy = num_chars; for (int src_offset_chars = src_char_pos; src_offset_chars < num_chars;) { int num_ints_to_copy; if (dst_int_offset % m_blockSize != 0) { num_ints_to_copy = m_blockSize - dst_int_offset % m_blockSize; } else { if (chars_left_to_copy > m_blockSize) { num_ints_to_copy = m_blockSize; } else { num_ints_to_copy = chars_left_to_copy; } } int num_chars_to_copy = Math.min(num_ints_to_copy * 2, chars_left_to_copy); int dataPtr = pointer; int current_dst_offset = dst_int_offset; int thisLevelCapacity = currentLevelCapacity; while (dataPtr < 0) { int directDataPtr = ~dataPtr; int levelMemSize = m_blocks.getInt(directDataPtr, INDEX_NUM_BLOCKS_OFFSET) * m_blockSize; int nextLevelCapacity = thisLevelCapacity / m_indexBlockCapacity; while (nextLevelCapacity >= levelMemSize) { nextLevelCapacity /= m_indexBlockCapacity; } int offset_in_index = current_dst_offset / nextLevelCapacity; current_dst_offset = current_dst_offset % nextLevelCapacity; dataPtr = m_blocks.getInt(directDataPtr, INDEX_DATA_OFFSET + offset_in_index); thisLevelCapacity = nextLevelCapacity; } m_blocks.setChars(dataPtr, current_dst_offset, src_char_data, src_char_pos + src_offset_chars, num_chars_to_copy); src_offset_chars += num_chars_to_copy; chars_left_to_copy -= num_chars_to_copy; dst_int_offset += num_ints_to_copy; } } else { m_blocks.setChars(pointer, dst_int_offset, src_char_data, src_char_pos, num_chars); } } @Override public void getChars(int pointer, int src_int_offset, char[] dst_char_data, int dst_char_pos, int num_chars) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (num_chars == 0) { return; } if (pointer < 0) { int numBlocks = m_blocks.getInt(~pointer, INDEX_NUM_BLOCKS_OFFSET); int currentLevelCapacity = maximumCapacityForNumBlocks(numBlocks); int num_ints = 1 + (num_chars - 1) / 2; // ceil (num_chars / 2) assert dst_char_pos >= 0 : "Negative dst_pos " + dst_char_pos; assert src_int_offset + num_ints <= numBlocks * m_blockSize : String.format( "dst_pos + num_ints > memSize : %d + %d < %d", dst_char_pos, num_ints, numBlocks * m_blockSize); int chars_left_to_copy = num_chars; for (int dst_offset_chars = dst_char_pos; dst_offset_chars < num_chars;) { int num_ints_to_copy; if (src_int_offset % m_blockSize != 0) { num_ints_to_copy = m_blockSize - src_int_offset % m_blockSize; } else { if (chars_left_to_copy > m_blockSize) { num_ints_to_copy = m_blockSize; } else { num_ints_to_copy = chars_left_to_copy; } } int num_chars_to_copy = Math.min(num_ints_to_copy * 2, chars_left_to_copy); int dataPtr = pointer; int current_src_offset = src_int_offset; int thisLevelCapacity = currentLevelCapacity; while (dataPtr < 0) { int directDataPtr = ~dataPtr; int levelMemSize = m_blocks.getInt(directDataPtr, INDEX_NUM_BLOCKS_OFFSET) * m_blockSize; int nextLevelCapacity = thisLevelCapacity / m_indexBlockCapacity; while (nextLevelCapacity >= levelMemSize) { nextLevelCapacity /= m_indexBlockCapacity; } int offset_in_index = current_src_offset / nextLevelCapacity; current_src_offset = current_src_offset % nextLevelCapacity; dataPtr = m_blocks.getInt(directDataPtr, INDEX_DATA_OFFSET + offset_in_index); thisLevelCapacity = nextLevelCapacity; } m_blocks.getChars(dataPtr, current_src_offset, dst_char_data, dst_char_pos + dst_offset_chars, num_chars_to_copy); dst_offset_chars += num_chars_to_copy; chars_left_to_copy -= num_chars_to_copy; src_int_offset += num_ints_to_copy; } } else { m_blocks.getChars(pointer, src_int_offset, dst_char_data, dst_char_pos, num_chars); } } @Override public void memSet(int pointer, int src_pos, int length, int value) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { int numBlocks = m_blocks.getInt(~pointer, INDEX_NUM_BLOCKS_OFFSET); int currentLevelCapacity = maximumCapacityForNumBlocks(numBlocks); assert src_pos >= 0 : "Negative src_pos " + src_pos; assert src_pos + length <= numBlocks * m_blockSize : String.format("src_pos + length > memSize : %d + %d < %d", src_pos, length, numBlocks * m_blockSize); int left_to_copy = length; int src1_offset = src_pos; while (left_to_copy > 0) { int this_set_length; if (src1_offset % m_blockSize != 0) { this_set_length = m_blockSize - src1_offset % m_blockSize; } else { if (left_to_copy > m_blockSize) { this_set_length = m_blockSize; } else { this_set_length = left_to_copy; } } int dataPtr = pointer; int current_dst_offset = src1_offset; int thisLevelCapacity = currentLevelCapacity; while (dataPtr < 0) { int directDataPtr = ~dataPtr; int levelMemSize = m_blocks.getInt(directDataPtr, INDEX_NUM_BLOCKS_OFFSET) * m_blockSize; int nextLevelCapacity = thisLevelCapacity / m_indexBlockCapacity; while (nextLevelCapacity >= levelMemSize) { nextLevelCapacity /= m_indexBlockCapacity; } int offset_in_index = current_dst_offset / nextLevelCapacity; current_dst_offset = current_dst_offset % nextLevelCapacity; dataPtr = m_blocks.getInt(directDataPtr, INDEX_DATA_OFFSET + offset_in_index); thisLevelCapacity = nextLevelCapacity; } m_blocks.memSet(dataPtr, current_dst_offset, this_set_length, value); left_to_copy -= this_set_length; src1_offset += this_set_length; } } else { m_blocks.memSet(pointer, src_pos, length, value); } } @Override public void getInts(int pointer, int src_offset_in_record, int dst_data[], int dst_pos, int length) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { int numBlocks = m_blocks.getInt(~pointer, INDEX_NUM_BLOCKS_OFFSET); int currentLevelCapacity = maximumCapacityForNumBlocks(numBlocks); assert dst_pos >= 0 : "Negative dst_pos " + dst_pos; assert src_offset_in_record + length <= numBlocks * m_blockSize : String.format( "dst_pos + length > memSize : %d + %d < %d", dst_pos, length, numBlocks * m_blockSize); int left_to_copy = length; int src_offset = src_offset_in_record; for (int dst_offset = dst_pos; dst_offset < length;) { int this_copy_length; if (src_offset % m_blockSize != 0) { this_copy_length = m_blockSize - src_offset % m_blockSize; } else { if (left_to_copy > m_blockSize) { this_copy_length = m_blockSize; } else { this_copy_length = left_to_copy; } } int dataPtr = pointer; int current_src_offset = src_offset; int thisLevelCapacity = currentLevelCapacity; while (dataPtr < 0) { int directDataPtr = ~dataPtr; int levelMemSize = m_blocks.getInt(directDataPtr, INDEX_NUM_BLOCKS_OFFSET) * m_blockSize; int nextLevelCapacity = thisLevelCapacity / m_indexBlockCapacity; while (nextLevelCapacity >= levelMemSize) { nextLevelCapacity /= m_indexBlockCapacity; } int offset_in_index = current_src_offset / nextLevelCapacity; current_src_offset = current_src_offset % nextLevelCapacity; dataPtr = m_blocks.getInt(directDataPtr, INDEX_DATA_OFFSET + offset_in_index); thisLevelCapacity = nextLevelCapacity; } m_blocks.getInts(dataPtr, current_src_offset, dst_data, dst_pos + dst_offset, this_copy_length); dst_offset += this_copy_length; left_to_copy -= this_copy_length; src_offset += this_copy_length; } } else { m_blocks.getInts(pointer, 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) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { int ilower = getInt(pointer, offset_in_data + 1); int iupper = getInt(pointer, offset_in_data); long lower = 0x00000000FFFFFFFFL & ilower; long upper = ((long) iupper) << 32; return upper | lower; } else { return m_blocks.getLong(pointer, offset_in_data); } } @Override public void setLong(int pointer, int offset_in_data, long data) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { // upper int int int1 = (int) (data >>> 32); // lower int int int2 = (int) (data); setInt(pointer, offset_in_data, int1); setInt(pointer, offset_in_data + 1, int2); } else { m_blocks.setLong(pointer, offset_in_data, data); } } @Override public int computeMemoryUsageFor(int size) { return 4 * m_blockSize * calculateNumBlocksFor(size); } private int calculateNumBlocksFor(int size) { return calculateNumBlocksFor(size, m_blockSize); } public static int calculateNumBlocksFor(int size, int blockSize) { if (size <= blockSize) { return 1; } else { int blocks = 1; int indexBlockCapacity = blockSize - INDEX_DATA_OFFSET; int numDataBlocks = 1 + ((size - 1) / blockSize); // ceil(a/b) // capacity of index that supports memory size int maxCapacity; { // ceil(memSize / m_blockSize); int neededBlocks = 1 + (size - 1) / blockSize; int indexCapacity = blockSize - INDEX_DATA_OFFSET; int currIndexCapacity = blockSize; while (neededBlocks > 1) { currIndexCapacity *= indexCapacity; // ceil(neededBlocks / indexCapacity); neededBlocks = 1 + (neededBlocks - 1) / indexCapacity; } maxCapacity = currIndexCapacity; } if (indexBlockCapacity * blockSize >= size) { blocks += numDataBlocks; } else { // allocating index int nextLevelCapacity = maxCapacity / indexBlockCapacity; int numIndexBlocksToAllocate = 1 + (size - 1) / nextLevelCapacity; int left = size; int indexedMemorySize = nextLevelCapacity; int blockNum = 0; for (; blockNum < numIndexBlocksToAllocate; blockNum++) { if (left <= nextLevelCapacity) indexedMemorySize = left; left -= indexedMemorySize; assert left >= 0; if (indexedMemorySize < blockSize) { blocks += 1; } else { blocks += calculateNumBlocksFor(indexedMemorySize, blockSize); } } } return blocks; } } private int maximumCapacityForNumBlocks(int numBlocks) { int currIndexCapacity = m_blockSize; while (numBlocks > 1) { currIndexCapacity *= m_indexBlockCapacity; // ceil(neededBlocks / indexCapacity); numBlocks = 1 + (numBlocks - 1) / m_indexBlockCapacity; } return currIndexCapacity; } @Override public boolean isDebug() { return m_blocks.isDebug(); } @Override public void setDebug(boolean debug) { m_blocks.setDebug(debug); } @Override public void setInitializer(MemInitializer initializer) { m_blocks.setInitializer(initializer); } @Override public int usedBlocks() { return m_blocks.usedBlocks(); } @Override public int blockSize() { return m_blocks.blockSize(); } @Override public int maxBlocks() { return m_blocks.maxBlocks(); } @Override public int freeBlocks() { return m_blocks.freeBlocks(); } @Override public void clear() { m_blocks.clear(); } @Override public long computeMemoryUsage() { return m_blocks.computeMemoryUsage(); } @Override public int maximumCapacityFor(int pointer) { int capacity = 0; if (pointer < 0) { capacity = m_blockSize * m_blocks.getInt(~pointer, INDEX_NUM_BLOCKS_OFFSET); } else { capacity = m_blocks.blockSize(); } return capacity; } @Override public void setGrowthFactor(double d) { m_blocks.setGrowthFactor(d); } @Override public double getGrowthFactor() { return m_blocks.getGrowthFactor(); } @Override public String toString() { return m_blocks.toString(); } @Override public IBlockAllocator getBlocks() { return m_blocks; } @Override public String pointerDebugString(int pointer) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; StringBuilder sb = new StringBuilder(); if (pointer > 0) { sb.append("["); for (int i = 0; i < m_blockSize; i++) { sb.append(m_blocks.getInt(pointer, i)); if (i + 1 < m_blockSize) sb.append(","); } sb.append("] "); } else { Queue<Integer> thisLevel = new LinkedList<Integer>(); thisLevel.add(pointer); Queue<Integer> nextLevel = new LinkedList<Integer>(); while (!thisLevel.isEmpty()) { for (int next : thisLevel) { if (next < 0) { int indexPointer = ~next; int nb = m_blocks.getInt(indexPointer, INDEX_NUM_BLOCKS_OFFSET); int memSize = nb * m_blockSize; int maxCapacityFor = maximumCapacityForNumBlocks(nb) / m_indexBlockCapacity; int numBlocks = 1 + ((memSize - 1) / maxCapacityFor); // ceil(a/b) for (int i = 0; i < numBlocks; i++) { int child = m_blocks.getInt(indexPointer, i + INDEX_DATA_OFFSET); nextLevel.add(child); } sb.append("[NB=").append(nb).append(", NC=").append(numBlocks).append("]"); } else { sb.append("["); for (int i = 0; i < m_blockSize; i++) { sb.append(m_blocks.getInt(next, i)); if (i + 1 < m_blockSize) sb.append(","); } sb.append("] "); } } thisLevel.clear(); thisLevel.addAll(nextLevel); nextLevel.clear(); sb.append("\n"); } } return sb.toString(); } @Override public void initialize(int pointer) { assert pointer != 0 : "Invalid pointer " + pointer; assert pointer != -1 : "Invalid pointer " + pointer; if (pointer < 0) { int indexPointer = ~pointer; int nb = m_blocks.getInt(indexPointer, INDEX_NUM_BLOCKS_OFFSET); int memSize = nb * m_blockSize; int maxCapacityFor = maximumCapacityForNumBlocks(nb) / m_indexBlockCapacity; int numBlocks = 1 + ((memSize - 1) / maxCapacityFor); // ceil(a/b) for (int i = 0; i < numBlocks; i++) { int p = m_blocks.getInt(indexPointer, i + INDEX_DATA_OFFSET); initialize(p); } } else { m_blocks.initialize(pointer); } } // this is super ugly, but since nothing here is thread safe anyway it's okay. int retOffset; // TODO: can this be iterative? can we eliminate the call to // maximumCapacityForNumBlocks? protected int getDataBlockPointerFor(int pointer, int offset) { assert pointer < 0; int indexPointer = ~pointer; int numBlocks = m_blocks.getInt(indexPointer, INDEX_NUM_BLOCKS_OFFSET); int maxCapacityPerIndexPtr = maximumCapacityForNumBlocks(numBlocks) / m_indexBlockCapacity; int blockNum = offset / maxCapacityPerIndexPtr; int dataPtr = m_blocks.getInt(indexPointer, INDEX_DATA_OFFSET + blockNum); if (maxCapacityPerIndexPtr <= m_blockSize || dataPtr >= 0) { int new_offset_in_data = offset - blockNum * maxCapacityPerIndexPtr; retOffset = new_offset_in_data; return dataPtr; } else { int new_offset_in_data = offset - blockNum * maxCapacityPerIndexPtr; return getDataBlockPointerFor(dataPtr, new_offset_in_data); } } @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)); } }