001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.type.collection.array;
020
021import icy.type.DataType;
022import icy.type.TypeUtil;
023
024import java.util.ArrayList;
025import java.util.List;
026
027/**
028 * @author Stephane
029 */
030public abstract class DynamicArray
031{
032    /**
033     * Create a DynamicArray with specified type.<br>
034     * 
035     * @param type
036     *        DataType of the dynamic array object.
037     * @param granularity
038     *        Accepted values go from 0 to 8 where lower value mean less memory used but more
039     *        allocation time where higher mean more memory used but less allocation time (default =
040     *        4).
041     */
042    public static DynamicArray create(DataType type, int granularity)
043    {
044        switch (type.getJavaType())
045        {
046            case BYTE:
047                return new DynamicArray.Byte(granularity);
048            case SHORT:
049                return new DynamicArray.Short();
050            case INT:
051                return new DynamicArray.Int();
052            case LONG:
053                return new DynamicArray.Long();
054            case FLOAT:
055                return new DynamicArray.Float();
056            case DOUBLE:
057                return new DynamicArray.Double();
058            default:
059                return null;
060        }
061    }
062
063    /**
064     * Create a DynamicArray with specified type
065     */
066    public static DynamicArray create(DataType type)
067    {
068        return create(type, 4);
069    }
070
071    /**
072     * Create a DynamicArray with specified type ({@link TypeUtil} constant)
073     * 
074     * @deprecated
075     */
076    @Deprecated
077    public static DynamicArray create(int type)
078    {
079        return create(DataType.getDataType(type));
080    }
081
082    public static class Generic extends DynamicArray
083    {
084        public void addSingle(Object value)
085        {
086            final ArrayBlock block = getAvailableBlock(true);
087            ((Object[]) block.array)[block.size++] = value;
088        }
089
090        @Override
091        protected Object createArray(int size)
092        {
093            return new Object[size];
094        }
095
096        @Override
097        protected int getArraySize(Object array)
098        {
099            return ((Object[]) array).length;
100        }
101
102        @Override
103        public Object[] asArray()
104        {
105            return (Object[]) super.asArray();
106        }
107    }
108
109    public static class Byte extends DynamicArray
110    {
111        /**
112         * Create a Byte DynamicArray.<br>
113         * 
114         * @param granularity
115         *        Accepted values go from 0 to 8 where lower value mean less memory used but more
116         *        allocation time where higher mean more memory used but less allocation time
117         *        (default =
118         *        4).
119         */
120        public Byte(int granularity)
121        {
122            super(granularity);
123        }
124
125        /**
126         * Create a Byte DynamicArray.
127         */
128        public Byte()
129        {
130            super();
131        }
132
133        public void addSingle(byte value)
134        {
135            final ArrayBlock block = getAvailableBlock(true);
136            ((byte[]) block.array)[block.size++] = value;
137        }
138
139        @Override
140        protected Object createArray(int size)
141        {
142            return new byte[size];
143        }
144
145        @Override
146        protected int getArraySize(Object array)
147        {
148            return ((byte[]) array).length;
149        }
150
151        @Override
152        public byte[] asArray()
153        {
154            return (byte[]) super.asArray();
155        }
156    }
157
158    public static class Short extends DynamicArray
159    {
160        /**
161         * Create a Short DynamicArray.<br>
162         * 
163         * @param granularity
164         *        Accepted values go from 0 to 8 where lower value mean less memory used but more
165         *        allocation time where higher mean more memory used but less allocation time
166         *        (default =
167         *        4).
168         */
169        public Short(int granularity)
170        {
171            super(granularity);
172        }
173
174        /**
175         * Create a Short DynamicArray.
176         */
177        public Short()
178        {
179            super();
180        }
181
182        public void addSingle(short value)
183        {
184            final ArrayBlock block = getAvailableBlock(true);
185            ((short[]) block.array)[block.size++] = value;
186        }
187
188        @Override
189        protected Object createArray(int size)
190        {
191            return new short[size];
192        }
193
194        @Override
195        protected int getArraySize(Object array)
196        {
197            return ((short[]) array).length;
198        }
199
200        @Override
201        public short[] asArray()
202        {
203            return (short[]) super.asArray();
204        }
205    }
206
207    public static class Int extends DynamicArray
208    {
209        /**
210         * Create a Integer DynamicArray.<br>
211         * 
212         * @param granularity
213         *        Accepted values go from 0 to 8 where lower value mean less memory used but more
214         *        allocation time where higher mean more memory used but less allocation time
215         *        (default = 4).
216         */
217        public Int(int granularity)
218        {
219            super(granularity);
220        }
221
222        /**
223         * Create a Integer DynamicArray.
224         */
225        public Int()
226        {
227            super();
228        }
229
230        public void addSingle(int value)
231        {
232            final ArrayBlock block = getAvailableBlock(true);
233            ((int[]) block.array)[block.size++] = value;
234        }
235
236        @Override
237        protected Object createArray(int size)
238        {
239            return new int[size];
240        }
241
242        @Override
243        protected int getArraySize(Object array)
244        {
245            return ((int[]) array).length;
246        }
247
248        @Override
249        public int[] asArray()
250        {
251            return (int[]) super.asArray();
252        }
253    }
254
255    public static class Long extends DynamicArray
256    {
257        /**
258         * Create a Long DynamicArray.<br>
259         * 
260         * @param granularity
261         *        Accepted values go from 0 to 8 where lower value mean less memory used but more
262         *        allocation time where higher mean more memory used but less allocation time
263         *        (default =
264         *        4).
265         */
266        public Long(int granularity)
267        {
268            super(granularity);
269        }
270
271        /**
272         * Create a Long DynamicArray.
273         */
274        public Long()
275        {
276            super();
277        }
278
279        public void addSingle(long value)
280        {
281            final ArrayBlock block = getAvailableBlock(true);
282            ((long[]) block.array)[block.size++] = value;
283        }
284
285        @Override
286        protected Object createArray(int size)
287        {
288            return new long[size];
289        }
290
291        @Override
292        protected int getArraySize(Object array)
293        {
294            return ((long[]) array).length;
295        }
296
297        @Override
298        public long[] asArray()
299        {
300            return (long[]) super.asArray();
301        }
302    }
303
304    public static class Float extends DynamicArray
305    {
306        /**
307         * Create a Float DynamicArray.<br>
308         * 
309         * @param granularity
310         *        Accepted values go from 0 to 8 where lower value mean less memory used but more
311         *        allocation time where higher mean more memory used but less allocation time
312         *        (default =
313         *        4).
314         */
315        public Float(int granularity)
316        {
317            super(granularity);
318        }
319
320        /**
321         * Create a Float DynamicArray.
322         */
323        public Float()
324        {
325            super();
326        }
327
328        public void addSingle(float value)
329        {
330            final ArrayBlock block = getAvailableBlock(true);
331            ((float[]) block.array)[block.size++] = value;
332        }
333
334        @Override
335        protected Object createArray(int size)
336        {
337            return new float[size];
338        }
339
340        @Override
341        protected int getArraySize(Object array)
342        {
343            return ((float[]) array).length;
344        }
345
346        @Override
347        public float[] asArray()
348        {
349            return (float[]) super.asArray();
350        }
351    }
352
353    public static class Double extends DynamicArray
354    {
355        /**
356         * Create a Double DynamicArray.<br>
357         * 
358         * @param granularity
359         *        Accepted values go from 0 to 8 where lower value mean less memory used but more
360         *        allocation time where higher mean more memory used but less allocation time
361         *        (default =
362         *        4).
363         */
364        public Double(int granularity)
365        {
366            super(granularity);
367        }
368
369        /**
370         * Create a Double DynamicArray.
371         */
372        public Double()
373        {
374            super();
375        }
376
377        public void addSingle(double value)
378        {
379            final ArrayBlock block = getAvailableBlock(true);
380            ((double[]) block.array)[block.size++] = value;
381        }
382
383        @Override
384        protected Object createArray(int size)
385        {
386            return new double[size];
387        }
388
389        @Override
390        protected int getArraySize(Object array)
391        {
392            return ((double[]) array).length;
393        }
394
395        @Override
396        public double[] asArray()
397        {
398            return (double[]) super.asArray();
399        }
400    }
401
402    protected class ArrayBlock
403    {
404        protected Object array;
405        protected int size;
406
407        public ArrayBlock()
408        {
409            super();
410
411            array = createArray(blockSize);
412            size = 0;
413        }
414
415        protected void clear()
416        {
417            size = 0;
418        }
419
420        /**
421         * @return the (used) size
422         */
423        public int getSize()
424        {
425            return size;
426        }
427
428        /**
429         * @deprecated USe {@link #getAvailable()} instead.
430         */
431        @Deprecated
432        public int getFreeSpace()
433        {
434            return getAvailable();
435        }
436
437        /**
438         * @return the available space
439         */
440        public int getAvailable()
441        {
442            return blockSize - getSize();
443        }
444
445        protected void get(Object out, int inOffset, int outOffset, int len)
446        {
447            System.arraycopy(array, inOffset, out, outOffset, len);
448        }
449
450        protected void add(Object in, int inOffset, int len)
451        {
452            System.arraycopy(in, inOffset, array, size, len);
453            size += len;
454        }
455
456        protected void put(Object in, int inOffset, int outOffset, int len)
457        {
458            System.arraycopy(in, inOffset, array, outOffset, len);
459            size = Math.max(size, outOffset + len);
460        }
461    }
462
463    // blockSize is a power of 2
464    final int blockSize;
465    private final List<ArrayBlock> blocks;
466
467    DynamicArray(int granularity)
468    {
469        super();
470
471        blockSize = 1 << (8 + Math.min(Math.max(granularity, 0), 8));
472        blocks = new ArrayList<ArrayBlock>();
473    }
474
475    DynamicArray()
476    {
477        this(4);
478    }
479
480    protected abstract Object createArray(int size);
481
482    protected abstract int getArraySize(Object array);
483
484    public void clear()
485    {
486        setSize(0);
487    }
488
489    public boolean isEmpty()
490    {
491        return getSize() == 0;
492    }
493
494    protected ArrayBlock addBlock()
495    {
496        final ArrayBlock result = new ArrayBlock();
497
498        blocks.add(result);
499
500        return result;
501    }
502
503    protected void removeBlock()
504    {
505        final int numBlock = blocks.size();
506
507        // remove last block if it exists
508        if (numBlock > 0)
509            blocks.remove(numBlock - 1);
510    }
511
512    protected void checkCapacity(int size)
513    {
514        while (getCapacity() < size)
515            setSize(size);
516    }
517
518    public int getCapacity()
519    {
520        return blocks.size() * blockSize;
521    }
522
523    public int getSize()
524    {
525        final int lastBlockIndex = getLastBlockIndex();
526
527        if (lastBlockIndex < 0)
528            return 0;
529
530        return (blockSize * lastBlockIndex) + blocks.get(lastBlockIndex).getSize();
531    }
532
533    protected int getLastBlockIndex()
534    {
535        return blocks.size() - 1;
536    }
537
538    protected ArrayBlock getLastBlock()
539    {
540        final int lastBlockIndex = getLastBlockIndex();
541
542        if (lastBlockIndex < 0)
543            return null;
544
545        return blocks.get(lastBlockIndex);
546    }
547
548    protected ArrayBlock getBlockFromOffset(int offset)
549    {
550        final int blockIndex = offset / blockSize;
551
552        if (blockIndex < blocks.size())
553            return blocks.get(blockIndex);
554
555        return null;
556    }
557
558    protected ArrayBlock getAvailableBlock(boolean create)
559    {
560        final ArrayBlock lastBlock = getLastBlock();
561
562        // last block exist and has free space ? --> return it
563        if ((lastBlock != null) && (lastBlock.getAvailable() > 0))
564            return lastBlock;
565
566        if (create)
567            return addBlock();
568
569        return null;
570    }
571
572    public void setSize(int size)
573    {
574        // special case
575        if (size == 0)
576        {
577            blocks.clear();
578            return;
579        }
580
581        // add blocks if needed
582        while (getCapacity() < size)
583        {
584            final ArrayBlock block = addBlock();
585            // set block size
586            block.size = blockSize;
587        }
588        // remove blocks if needed
589        while ((getCapacity() - blockSize) > size)
590            removeBlock();
591
592        // adjust last block size if needed
593        if (getCapacity() > size)
594            getLastBlock().size = getCapacity() - size;
595    }
596
597    public void addAll(DynamicArray in)
598    {
599        final Object array = in.asArray();
600
601        add(array, 0, getArraySize(array));
602    }
603
604    public void add(Object in)
605    {
606        add(in, 0, getArraySize(in));
607    }
608
609    public void get(Object out, int inOffset, int outOffset, int len)
610    {
611        int srcOffset = inOffset;
612        int dstOffset = outOffset;
613        int cnt = len;
614        while (cnt > 0)
615        {
616            final ArrayBlock block = getBlockFromOffset(srcOffset);
617            final int subSrcOffset = srcOffset & (blockSize - 1);
618            final int subLen = Math.min(blockSize - subSrcOffset, cnt);
619
620            block.get(out, subSrcOffset, dstOffset, subLen);
621            srcOffset += subLen;
622            dstOffset += subLen;
623            cnt -= subLen;
624        }
625    }
626
627    public void add(Object in, int inOffset, int len)
628    {
629        int offset = inOffset;
630        int cnt = len;
631        while (cnt > 0)
632        {
633            final ArrayBlock block = getAvailableBlock(true);
634            final int blockSpace = block.getAvailable();
635            final int toCopy = Math.min(blockSpace, cnt);
636
637            block.add(in, offset, toCopy);
638            offset += toCopy;
639            cnt -= toCopy;
640        }
641    }
642
643    public void put(Object in, int inOffset, int outOffset, int len)
644    {
645        checkCapacity(outOffset + len);
646
647        int srcOffset = inOffset;
648        int dstOffset = outOffset;
649        int cnt = len;
650        while (cnt > 0)
651        {
652            final ArrayBlock block = getBlockFromOffset(dstOffset);
653            final int subDstOffset = dstOffset & (blockSize - 1);
654            final int subLen = Math.min(blockSize - subDstOffset, cnt);
655
656            block.put(in, srcOffset, subDstOffset, subLen);
657            srcOffset += subLen;
658            dstOffset += subLen;
659            cnt -= subLen;
660        }
661    }
662
663    public Object asArray()
664    {
665        final Object result = createArray(getSize());
666
667        int offset = 0;
668        for (ArrayBlock block : blocks)
669        {
670            final int blockSize = block.getSize();
671            block.get(result, 0, offset, blockSize);
672            offset += blockSize;
673        }
674
675        return result;
676    }
677
678}