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}