/* * 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; } }