/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Icy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.type.collection.array;
import icy.type.DataType;
import icy.type.TypeUtil;
import java.util.ArrayList;
import java.util.List;
/**
* @author Stephane
*/
public abstract class DynamicArray
{
/**
* Create a DynamicArray with specified type.<br>
*
* @param type
* DataType of the dynamic array object.
* @param granularity
* Accepted values go from 0 to 8 where lower value mean less memory used but more
* allocation time where higher mean more memory used but less allocation time (default =
* 4).
*/
public static DynamicArray create(DataType type, int granularity)
{
switch (type.getJavaType())
{
case BYTE:
return new DynamicArray.Byte(granularity);
case SHORT:
return new DynamicArray.Short();
case INT:
return new DynamicArray.Int();
case LONG:
return new DynamicArray.Long();
case FLOAT:
return new DynamicArray.Float();
case DOUBLE:
return new DynamicArray.Double();
default:
return null;
}
}
/**
* Create a DynamicArray with specified type
*/
public static DynamicArray create(DataType type)
{
return create(type, 4);
}
/**
* Create a DynamicArray with specified type ({@link TypeUtil} constant)
*
* @deprecated
*/
@Deprecated
public static DynamicArray create(int type)
{
return create(DataType.getDataType(type));
}
public static class Generic extends DynamicArray
{
public void addSingle(Object value)
{
final ArrayBlock block = getAvailableBlock(true);
((Object[]) block.array)[block.size++] = value;
}
@Override
protected Object createArray(int size)
{
return new Object[size];
}
@Override
protected int getArraySize(Object array)
{
return ((Object[]) array).length;
}
@Override
public Object[] asArray()
{
return (Object[]) super.asArray();
}
}
public static class Byte extends DynamicArray
{
/**
* Create a Byte DynamicArray.<br>
*
* @param granularity
* Accepted values go from 0 to 8 where lower value mean less memory used but more
* allocation time where higher mean more memory used but less allocation time
* (default =
* 4).
*/
public Byte(int granularity)
{
super(granularity);
}
/**
* Create a Byte DynamicArray.
*/
public Byte()
{
super();
}
public void addSingle(byte value)
{
final ArrayBlock block = getAvailableBlock(true);
((byte[]) block.array)[block.size++] = value;
}
@Override
protected Object createArray(int size)
{
return new byte[size];
}
@Override
protected int getArraySize(Object array)
{
return ((byte[]) array).length;
}
@Override
public byte[] asArray()
{
return (byte[]) super.asArray();
}
}
public static class Short extends DynamicArray
{
/**
* Create a Short DynamicArray.<br>
*
* @param granularity
* Accepted values go from 0 to 8 where lower value mean less memory used but more
* allocation time where higher mean more memory used but less allocation time
* (default =
* 4).
*/
public Short(int granularity)
{
super(granularity);
}
/**
* Create a Short DynamicArray.
*/
public Short()
{
super();
}
public void addSingle(short value)
{
final ArrayBlock block = getAvailableBlock(true);
((short[]) block.array)[block.size++] = value;
}
@Override
protected Object createArray(int size)
{
return new short[size];
}
@Override
protected int getArraySize(Object array)
{
return ((short[]) array).length;
}
@Override
public short[] asArray()
{
return (short[]) super.asArray();
}
}
public static class Int extends DynamicArray
{
/**
* Create a Integer DynamicArray.<br>
*
* @param granularity
* Accepted values go from 0 to 8 where lower value mean less memory used but more
* allocation time where higher mean more memory used but less allocation time
* (default = 4).
*/
public Int(int granularity)
{
super(granularity);
}
/**
* Create a Integer DynamicArray.
*/
public Int()
{
super();
}
public void addSingle(int value)
{
final ArrayBlock block = getAvailableBlock(true);
((int[]) block.array)[block.size++] = value;
}
@Override
protected Object createArray(int size)
{
return new int[size];
}
@Override
protected int getArraySize(Object array)
{
return ((int[]) array).length;
}
@Override
public int[] asArray()
{
return (int[]) super.asArray();
}
}
public static class Long extends DynamicArray
{
/**
* Create a Long DynamicArray.<br>
*
* @param granularity
* Accepted values go from 0 to 8 where lower value mean less memory used but more
* allocation time where higher mean more memory used but less allocation time
* (default =
* 4).
*/
public Long(int granularity)
{
super(granularity);
}
/**
* Create a Long DynamicArray.
*/
public Long()
{
super();
}
public void addSingle(long value)
{
final ArrayBlock block = getAvailableBlock(true);
((long[]) block.array)[block.size++] = value;
}
@Override
protected Object createArray(int size)
{
return new long[size];
}
@Override
protected int getArraySize(Object array)
{
return ((long[]) array).length;
}
@Override
public long[] asArray()
{
return (long[]) super.asArray();
}
}
public static class Float extends DynamicArray
{
/**
* Create a Float DynamicArray.<br>
*
* @param granularity
* Accepted values go from 0 to 8 where lower value mean less memory used but more
* allocation time where higher mean more memory used but less allocation time
* (default =
* 4).
*/
public Float(int granularity)
{
super(granularity);
}
/**
* Create a Float DynamicArray.
*/
public Float()
{
super();
}
public void addSingle(float value)
{
final ArrayBlock block = getAvailableBlock(true);
((float[]) block.array)[block.size++] = value;
}
@Override
protected Object createArray(int size)
{
return new float[size];
}
@Override
protected int getArraySize(Object array)
{
return ((float[]) array).length;
}
@Override
public float[] asArray()
{
return (float[]) super.asArray();
}
}
public static class Double extends DynamicArray
{
/**
* Create a Double DynamicArray.<br>
*
* @param granularity
* Accepted values go from 0 to 8 where lower value mean less memory used but more
* allocation time where higher mean more memory used but less allocation time
* (default =
* 4).
*/
public Double(int granularity)
{
super(granularity);
}
/**
* Create a Double DynamicArray.
*/
public Double()
{
super();
}
public void addSingle(double value)
{
final ArrayBlock block = getAvailableBlock(true);
((double[]) block.array)[block.size++] = value;
}
@Override
protected Object createArray(int size)
{
return new double[size];
}
@Override
protected int getArraySize(Object array)
{
return ((double[]) array).length;
}
@Override
public double[] asArray()
{
return (double[]) super.asArray();
}
}
protected class ArrayBlock
{
protected Object array;
protected int size;
public ArrayBlock()
{
super();
array = createArray(blockSize);
size = 0;
}
protected void clear()
{
size = 0;
}
/**
* @return the (used) size
*/
public int getSize()
{
return size;
}
/**
* @deprecated USe {@link #getAvailable()} instead.
*/
@Deprecated
public int getFreeSpace()
{
return getAvailable();
}
/**
* @return the available space
*/
public int getAvailable()
{
return blockSize - getSize();
}
protected void get(Object out, int inOffset, int outOffset, int len)
{
System.arraycopy(array, inOffset, out, outOffset, len);
}
protected void add(Object in, int inOffset, int len)
{
System.arraycopy(in, inOffset, array, size, len);
size += len;
}
protected void put(Object in, int inOffset, int outOffset, int len)
{
System.arraycopy(in, inOffset, array, outOffset, len);
size = Math.max(size, outOffset + len);
}
}
// blockSize is a power of 2
final int blockSize;
private final List<ArrayBlock> blocks;
DynamicArray(int granularity)
{
super();
blockSize = 1 << (8 + Math.min(Math.max(granularity, 0), 8));
blocks = new ArrayList<ArrayBlock>();
}
DynamicArray()
{
this(4);
}
protected abstract Object createArray(int size);
protected abstract int getArraySize(Object array);
public void clear()
{
setSize(0);
}
public boolean isEmpty()
{
return getSize() == 0;
}
protected ArrayBlock addBlock()
{
final ArrayBlock result = new ArrayBlock();
blocks.add(result);
return result;
}
protected void removeBlock()
{
final int numBlock = blocks.size();
// remove last block if it exists
if (numBlock > 0)
blocks.remove(numBlock - 1);
}
protected void checkCapacity(int size)
{
while (getCapacity() < size)
setSize(size);
}
public int getCapacity()
{
return blocks.size() * blockSize;
}
public int getSize()
{
final int lastBlockIndex = getLastBlockIndex();
if (lastBlockIndex < 0)
return 0;
return (blockSize * lastBlockIndex) + blocks.get(lastBlockIndex).getSize();
}
protected int getLastBlockIndex()
{
return blocks.size() - 1;
}
protected ArrayBlock getLastBlock()
{
final int lastBlockIndex = getLastBlockIndex();
if (lastBlockIndex < 0)
return null;
return blocks.get(lastBlockIndex);
}
protected ArrayBlock getBlockFromOffset(int offset)
{
final int blockIndex = offset / blockSize;
if (blockIndex < blocks.size())
return blocks.get(blockIndex);
return null;
}
protected ArrayBlock getAvailableBlock(boolean create)
{
final ArrayBlock lastBlock = getLastBlock();
// last block exist and has free space ? --> return it
if ((lastBlock != null) && (lastBlock.getAvailable() > 0))
return lastBlock;
if (create)
return addBlock();
return null;
}
public void setSize(int size)
{
// special case
if (size == 0)
{
blocks.clear();
return;
}
// add blocks if needed
while (getCapacity() < size)
{
final ArrayBlock block = addBlock();
// set block size
block.size = blockSize;
}
// remove blocks if needed
while ((getCapacity() - blockSize) > size)
removeBlock();
// adjust last block size if needed
if (getCapacity() > size)
getLastBlock().size = getCapacity() - size;
}
public void addAll(DynamicArray in)
{
final Object array = in.asArray();
add(array, 0, getArraySize(array));
}
public void add(Object in)
{
add(in, 0, getArraySize(in));
}
public void get(Object out, int inOffset, int outOffset, int len)
{
int srcOffset = inOffset;
int dstOffset = outOffset;
int cnt = len;
while (cnt > 0)
{
final ArrayBlock block = getBlockFromOffset(srcOffset);
final int subSrcOffset = srcOffset & (blockSize - 1);
final int subLen = Math.min(blockSize - subSrcOffset, cnt);
block.get(out, subSrcOffset, dstOffset, subLen);
srcOffset += subLen;
dstOffset += subLen;
cnt -= subLen;
}
}
public void add(Object in, int inOffset, int len)
{
int offset = inOffset;
int cnt = len;
while (cnt > 0)
{
final ArrayBlock block = getAvailableBlock(true);
final int blockSpace = block.getAvailable();
final int toCopy = Math.min(blockSpace, cnt);
block.add(in, offset, toCopy);
offset += toCopy;
cnt -= toCopy;
}
}
public void put(Object in, int inOffset, int outOffset, int len)
{
checkCapacity(outOffset + len);
int srcOffset = inOffset;
int dstOffset = outOffset;
int cnt = len;
while (cnt > 0)
{
final ArrayBlock block = getBlockFromOffset(dstOffset);
final int subDstOffset = dstOffset & (blockSize - 1);
final int subLen = Math.min(blockSize - subDstOffset, cnt);
block.put(in, srcOffset, subDstOffset, subLen);
srcOffset += subLen;
dstOffset += subLen;
cnt -= subLen;
}
}
public Object asArray()
{
final Object result = createArray(getSize());
int offset = 0;
for (ArrayBlock block : blocks)
{
final int blockSize = block.getSize();
block.get(result, 0, offset, blockSize);
offset += blockSize;
}
return result;
}
}