All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.algart.arrays.CombinedMemoryModel Maven / Gradle / Ivy

Go to download

Open-source Java libraries, supporting generalized smart arrays and matrices with elements of any types, including a wide set of 2D-, 3D- and multidimensional image processing and other algorithms, working with arrays and matrices.

There is a newer version: 1.4.23
Show newest version
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.arrays;

import java.util.EmptyStackException;
import java.nio.ByteBuffer;
import java.util.Objects;

/**
 * 

The memory model allowing to create combined arrays: * special kind of AlgART arrays, that store an array of Java objects with minimal amount of memory, * namely in one or several another "parallel" arrays. * A set of these arrays is named "the internal storage" of the combined array.

* *

There is an essential problem with storing large arrays of small objects in Java language. * For example, let a point is described by 2 integer values: * x and y, * and we need to store 10 million points. In C++ or Pascal language, we can create an array * of 10 000 000 structures (struct or record keyword), and this array * occupies ~80 MB memory (4 bytes per every integer).

* *

Unfortunately, the only simple way to store 10 million points in Java * is usage of Java array (or collection) containing instance of Java-class, such as

 * class Point {
 *     int x;
 *     int y;
 * }
* *

For example,

 *     Point[] points = new Point[10000000];
 *     for (int k = 0; k < points.length; k++)
 *         points[k] = new Point();
* *

Such an array occupies more than 200 MB and is created very slowly (several seconds on * CPU P-IV 2 GHz, under Java 1.5). The reason is creating separate object for every point * and saving a pointer to it in Java array. Only array of primitive types work * efficiently in Java.

* *

The arrays created by this memory model ("combined" arrays) allow a solution of this problem. * This memory model stores an array of any Java objects in one or several "parallel" another AlgART arrays - * typically, wrappers for Java-arrays of primitive types * (created by {@link SimpleMemoryModel}) or for ByteBuffer objects * (created by {@link LargeMemoryModel}). This array is created quickly, * does not make a difficult problem for future garbage collection * and occupies only necessary amount of memory.

* *

However, in many situations, combined arrays work slower, than * simple array of references as listed above. In particular, * an access to stored elements of combined arrays are usually slower in several times, * than usage direct references array or standard ArrayList. * Usually, you need combined arrays if you should save occupied memory * or quickly create large array.

* *

The only thing that you should provide to use this class is implementing * {@link CombinedMemoryModel.Combiner} interface or its inheritors * {@link CombinedMemoryModel.CombinerInPlace}, * {@link CombinedMemoryModel.BufferedCombiner}, * which "tell" how to store (or load) one object in (from) a set of AlgART arrays. * The {@link CombinedMemoryModel.CombinerInPlace} interface allows to * quite eliminate usage of Java heap while working with combined array; * the {@link CombinedMemoryModel.BufferedCombiner} interface allows to optimize * block access to the combined array.

* *

Below are the main features of arrays created via this memory model.

    * *
  • Only element types inherited from Object are supported (not primitive): * so, the created arrays always implement the {@link ObjectArray} / * {@link UpdatableObjectArray} / {@link MutableObjectArray} interface.
  • * *
  • Arrays, created by
      *
    • {@link #newEmptyArray(Class) newEmptyArray(Class elementType)},
    • *
    • {@link #newEmptyArray(Class, long) newEmptyArray(Class elementType, long initialCapacity)},
    • *
    • {@link #newArray(Class, long) newArray(Class elementType, long initialLength)}
    • *
    are always mutable and resizable: they implement {@link MutableObjectArray} interface. * Arrays, created by
      *
    • {@link #asUpdatableCombinedArray(Class, UpdatableArray[]) * asUpdatableCombinedArray(Class elementType, UpdatableArray[] storage)},
    • *
    are unresizable. * Arrays, created by
      *
    • {@link #asCombinedArray(Class, Array[]) * asCombinedArray(Class elementType, Array[] storage)},
    • *
    are immutable.
  • * *
  • Arrays, created by this memory model, never implement {@link DirectAccessible} interface.
  • * *
  • Arrays, created by this memory model, never have new or new-read-only-view * status: {@link Array#isNew()} and {@link Array#isNewReadOnlyView()} method always return false, * because they are views of some other ({@link #getStorage(Array) underlying}) arrays.
  • * *
  • The {@link Array#loadResources(ArrayContext)}, * {@link Array#flushResources(ArrayContext)}, {@link Array#flushResources(ArrayContext, boolean)} and * {@link Array#freeResources(ArrayContext, boolean)} methods just call the same methods of the storage arrays.
  • * *
  • If the combiner, passed while creating the memory model by {@link #getInstance(Combiner)}, implements * extended {@link CombinedMemoryModel.CombinerInPlace} interface, then the arrays, created by this memory model, * implement {@link ObjectInPlaceArray} / {@link UpdatableObjectInPlaceArray} * / {@link MutableObjectInPlaceArray} * interface, that allow to avoid allocating new Java objects while accessing elements.
  • * *
  • {@link UpdatableArray#copy(Array)}, {@link UpdatableArray#swap(UpdatableArray)}, * {@link Array#updatableClone(MemoryModel)}, {@link Array#mutableClone(MemoryModel)} methods * copies the content of elements (not references to elements!) by calling * the corresponding methods of the storage arrays (and their * {@link Array#subArray(long, long) subarrays}).
  • * *
  • The {@link Array#equals(Object)} method, * in a case when another array is combined and uses the same combiner, * is based on calls of the corresponding methods of the storage array (and their * {@link Array#subArray(long, long) subarrays}); * so, result of {@link Array#equals(Object)} (for the same combiner in both arrays) * does not depend on implementation of equals method * in the class of elements ({@link Array#elementType()})
  • * *
  • However, {@link Array#hashCode()} method, according to the comments for it, * is still based on implementation of hashCode method * in the class of elements. * So, you should store in combined arrays only such objects, * that have hashCode method based on the their content * (stored inside a combined array via {@link CombinedMemoryModel.Combiner}). * In another case, standard contract for hashCode and equals * methods can be violated, that can lead to problems, for example, while using arrays * as keys in hash maps.
  • *
* *

This class is immutable and thread-safe: * there are no ways to modify settings of the created instance.

* * @param the generic type of array elements in object arrays created by this memory model. * @author Daniel Alievsky */ public final class CombinedMemoryModel extends AbstractMemoryModel { /** *

This interface should be implemented to allow saving objects * in arrays created via {@link CombinedMemoryModel#getInstance(Combiner) * combined memory model}.

* *

It describes how to store (or load) one object in (from) a set of AlgART arrays. * More precisely, this interface should provide methods for storing and loading * all data of one object (an element of a combined array) in/from * several "parallel" AlgART arrays, named "storage". * An element with given index should be stored in the following elements in storage:

*
     *     storage[k][d[k]*index...d[k]*(index+1)-1], k=0,1,...
*

where d is an array returned by {@link #numbersOfElementsPerOneCombinedElement(int)} * method.

* *

Important! The classes of objects stored or loaded by this combiner should * have correct hashCode method, based on the content of an object: * the data stored or loaded by this combiner. See also comments * to CombinedMemoryModel. * *

Typical implementation supposes that a storage is one or several arrays of primitive types * (created by {@link SimpleMemoryModel}).

* *

The methods of this interface may not check indexes of elements: * all necessary checks are performed by AlgART array implementation created by {@link CombinedMemoryModel}.

* * @param the generic type of array elements in object arrays. */ public interface Combiner { /** * Returns an element #index of the combined array from the given set of AlgART arrays. * Unlike {@link CombinerInPlace#getInPlace(long, Object, Array[])}, * this method always creates a new object and should work always. * This method is called by {@link ObjectArray#getElement(long)}. * * @param index an index in the combined array. * @param storage a set of arrays where the retrieved content is stored now. * @return new created object containing an element #index of combined array. */ E get(long index, Array[] storage); /** * Stores the element value at position #index of the combined array * inside the given set of AlgART arrays. * This method is called by {@link UpdatableObjectArray#setElement(long, Object)}. * *

Important: this method must not throw NullPointerException * if the value argument is {@code null}. Instead, it should * store some "signal" value in the storage, that cannot be stored for any * possible non-null elements, or just some default ("empty") value. * In the first case, further {@link #get get(index, storage)} should * return {@code null}; in the second case, it should return an instance * in the default state. * * @param index an index in the combined array. * @param value the stored element * @param storage a set of arrays where the content will be stored. */ void set(long index, E value, UpdatableArray[] storage); /** * Should create a storage (several AlgART arrays), * allowing to store elements of the combined arrays. * Called while creating new combined arrays. * *

The initial lengths of created arrays should be calculated on the base of * passed length argument, that means the length of necessary * combined array. Namely, the length of returned array #k * should be equal to length*{@link #numbersOfElementsPerOneCombinedElement(int) * numbersOfElementsPerOneCombinedElement}(k). This condition is automatically * verified while creating combined arrays. * *

If unresizable argument is true, it means that this method * is called for creating {@link Array#isUnresizable() unresizable} combined array. * In this case we recommend to use {@link MemoryModel#newUnresizableArray(Class, long) * MemoryModel.newUnresizableArray} method for creating storage arrays. * If unresizable argument is false, every element of returned * Java array must implement {@link MutableArray} interface. * * @param length initial length of corresponding combined arrays. * @param unresizable if true, the created arrays should be unresizable, * in other case they must be mutable and implement {@link MutableArray} interface. * @return created storage. */ UpdatableArray[] allocateStorage(long length, boolean unresizable); /** * Should return the number of sequential elements of the array #indexOfArrayInStorage * in the storage, used for storing one element of the combined array. * Called while creating new combined arrays. * * @param indexOfArrayInStorage index of the storage array. * @return the number of sequential elements used for storing one combined element. */ int numbersOfElementsPerOneCombinedElement(int indexOfArrayInStorage); } /** *

Special version of {@link Combiner} interface allowing * to load an element without creating new Java object. * If the argument of {@link CombinedMemoryModel#getInstance(Combiner)} method * implements this interface, then the arrays created by returned memory model * will implement {@link MutableObjectInPlaceArray} * (or, maybe, only {@link UpdatableObjectInPlaceArray} or {@link ObjectInPlaceArray}, * for arrays created by * {@link CombinedMemoryModel#asUpdatableCombinedArray(Class, UpdatableArray[])} or * {@link CombinedMemoryModel#asCombinedArray(Class, Array[])} methods).

* * @param the generic type of array elements in object arrays. */ public interface CombinerInPlace extends Combiner { /** * Creates a new element that can be stored in or loaded from the combined array. * Never returns {@code null}. * * @return some instance of an element of the combined array. */ E allocateElement(); /** * Loads an element #index of the combined array from the given set of arrays * into resultValue object. * This method is called by {@link ObjectInPlaceArray#getInPlace(long, Object)} method. * * @param index an index in the combined array. * @param resultValue the object where the retrieved content will be stored. * @param storage a set of arrays where the retrieved content is stored now. * @throws NullPointerException if resultValue is {@code null}. */ void getInPlace(long index, E resultValue, Array[] storage); } /** *

Special version of {@link Combiner} interface allowing * to optimize block access to the combined array.

* *

If the argument of {@link CombinedMemoryModel#getInstance(Combiner)} method * implements this interface, then {@link Array#getData(long, Object, int, int) getData(...)}, * {@link UpdatableArray#setData(long, Object, int, int) setData(...)} and * {@link Array#buffer buffer-access} methods will use block get/set * methods declared in this interface. In another case, those methods * will call separate get/set method, declared in {@link Combiner}, * for every loaded/stored element of the combined array.

* * @param the generic type of array elements in object arrays. */ public interface BufferedCombiner extends Combiner { /** * Reads count elements of the combined array, * starting from te specified index, from the given set of AlgART arrays (storage). * Loaded elements are placed into resultValues * Java array at the positions #offset..#offset+count-1. * This method is called by {@link ObjectArray#getData(long, Object, int, int)} * and {@link DataObjectBuffer#map(long)} methods. * *

Note: if resultValues[offset+k]!=null for some index k (0<=k<count), * and the element type allows changing full element state, * this method may not to allocate new object for this index, * but load the corresponding combined element #index+k * into resultValues[offset+k]. * It can essentially optimize loading a large number of elements.

* * @param index starting index in the combined array. * @param resultValues the target Java array. * @param offset starting position in the target Java array. * @param count the number of elements to be retrieved. * @param storage a set of arrays where the retrieved content is stored now. */ void get(long index, E[] resultValues, int offset, int count, Array[] storage); /** * Stores count elements of the combined array, * starting from te specified index, * inside the given set of AlgART arrays (storage). * The elements are loaded from values Java array * from at the positions #offset..#offset+count-1. * This method is called by {@link UpdatableObjectArray#setData(long, Object, int, int)} * and {@link DataObjectBuffer#force(long, long)} methods. * *

Important: this method must not throw NullPointerException * if some element of values Java array is {@code null}. Instead, it should * store some "signal" value in the storage, that cannot be stored for any * possible non-null elements, or just some default ("empty") value. * In the first case, further {@link #get get(index, storage)} should * return {@code null} for this element; in the second case, it should return an instance * in the default state. * * @param index starting index in the combined array. * @param values the source Java array. * @param offset starting position in the source Java array. * @param count the number of elements to be stored. * @param storage a set of arrays where the content will be stored. */ void set(long index, E[] values, int offset, int count, UpdatableArray[] storage); } /** *

A skeleton class allowing to simplify implementation of * {@link Combiner} interface.

* *

To create a combiner, based in this class, it's enough * to override only 2 very simple methods * {@link #loadElement()} and {@link #storeElement(Object)}. * These methods operate with a little ByteBuffer * {@link #workStorage}, and this class automatically * copy the content of this buffer from / to the storage array.

* *

This combiner always use a single {@link ByteArray} as a storage. * If you need to store different types of data (for example, int * and double fields of the stored objects), * you may use corresponding views of {@link #workStorage} buffer * (asIntBuffer(), asDoubleBuffer, etc.).

* *

Unfortunately, for simple structure of element types, * this combiner usually work essentially slower * than the direct implementation of {@link Combiner} interface.

* * @param the generic type of array elements in object arrays. */ public abstract static class AbstractByteBufferCombiner implements Combiner { /** * A little ByteBuffer for storing one element of the combined array. * This reference is copied from the corresponding constructor argument. */ protected final ByteBuffer workStorage; final Class elementType; final int elementSize; final boolean isDirect; final byte[] array; private final MemoryModel mm; /** * Creates a new instance of this combiner. * * @param elementType the type of elements of the combined array. * @param workStorageForOneElement a little ByteBuffer enough to store one element * of the combined array. May be direct ByteBuffer, but the heap * one usually provides better performance. * @param memoryModel the {@link MemoryModel memory model} which will be used for creating * combined arrays. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IllegalArgumentException if the passed ByteBuffer is read-only. */ protected AbstractByteBufferCombiner( Class elementType, ByteBuffer workStorageForOneElement, MemoryModel memoryModel) { Objects.requireNonNull(elementType, "Null elementType"); if (elementType == void.class) { throw new IllegalArgumentException("Illegal elementType: it cannot be void.class"); } Objects.requireNonNull(workStorageForOneElement, "Null workStorageForOneElement argument"); if (workStorageForOneElement.isReadOnly()) { throw new IllegalArgumentException("Illegal workStorageForOneElement argument: " + "it must not be read-only"); } Objects.requireNonNull(memoryModel, "Null memoryModel argument"); if (!memoryModel.isElementTypeSupported(byte.class)) { throw new IllegalArgumentException("Illegal memoryModel argument: it must support byte elements"); } this.elementType = elementType; this.workStorage = workStorageForOneElement; this.elementSize = workStorageForOneElement.limit(); this.isDirect = !workStorageForOneElement.hasArray(); this.array = isDirect ? new byte[elementSize] : workStorageForOneElement.array(); this.mm = memoryModel; } /** * Should create one element of the combined array and fill it from {@link #workStorage}. * * @return newly created object. */ protected abstract E loadElement(); /** * Should store all information about the passed element of the combiner array in {@link #workStorage}. * * @param element the stored element. */ protected abstract void storeElement(E element); public final E get(long index, Array[] storage) { storage[0].getData(index * elementSize, array, 0, elementSize); if (isDirect) { workStorage.rewind(); workStorage.put(array); } return loadElement(); } public final void set(long index, E value, UpdatableArray[] storage) { storeElement(value); if (isDirect) { workStorage.rewind(); workStorage.get(array); } storage[0].setData(index * elementSize, array, 0, elementSize); } public final UpdatableArray[] allocateStorage(long length, boolean unresizable) { if (unresizable) { return new UpdatableByteArray[]{mm.newUnresizableByteArray(length * elementSize)}; } else { return new MutableByteArray[]{mm.newByteArray(length * elementSize)}; } } public final int numbersOfElementsPerOneCombinedElement(int indexOfArrayInStorage) { return elementSize; } } /** *

A version of {@link AbstractByteBufferCombiner} skeleton class * implementing {@link CombinerInPlace} interface.

* * @param the generic type of array elements in object arrays. */ public abstract static class AbstractByteBufferCombinerInPlace extends AbstractByteBufferCombiner implements CombinerInPlace { /** * Creates a new instance of this combiner. * * @param elementType the type of elements of the combined array. * @param workStorageForOneElement a little ByteBuffer enough to store one element * of the combined array. May be direct ByteBuffer, but the heap * one usually provides better performance. * @param memoryModel the {@link MemoryModel memory model} which will be used for creating * combined arrays. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IllegalArgumentException if the passed ByteBuffer is read-only. */ protected AbstractByteBufferCombinerInPlace( Class elementType, ByteBuffer workStorageForOneElement, MemoryModel memoryModel) { super(elementType, workStorageForOneElement, memoryModel); } public abstract E allocateElement(); /** * Should fill the passed element of the combined array from {@link #workStorage}. * * @param resultElement the object where the retrieved content will be stored. * @throws NullPointerException if the argument is {@code null}. */ protected abstract void loadElementInPlace(E resultElement); public final void getInPlace(long index, E resultValue, Array[] storage) { storage[0].getData(index * elementSize, array, 0, elementSize); if (isDirect) { workStorage.rewind(); workStorage.put(array); } loadElementInPlace(resultValue); } } private final Combiner combiner; private CombinedMemoryModel(Combiner combiner) { Objects.requireNonNull(combiner, "Null combiner argument"); this.combiner = combiner; } /** * Creates new memory model with corresponding combiner. * If combiner argument implements {@link CombinerInPlace} (not only {@link Combiner}), * then the arrays created via this memory model will implement {@link ObjectInPlaceArray} interface * (not only {@link ObjectArray}). * *

Note: if you need to create several instances of {@link CombinedMemoryModel} * with identical combiner, please use the same reference to {@link Combiner} * object for all memory model instances. * If two combined arrays were created with the same combiner, then * some operations with these arrays (namely, * {@link UpdatableArray#copy(Array)} and {@link UpdatableArray#swap(UpdatableArray)} will work faster. * * @param the generic type of array elements. * @param combiner will be used for creation of combined arrays by this memory model. * @return created memory model. * @throws NullPointerException if combiner is {@code null}. */ public static CombinedMemoryModel getInstance(Combiner combiner) { return new CombinedMemoryModel(combiner); } /** * This implementation returns {@link #newEmptyArray(Class, long) newArray}(elementType, 10). * * @param elementType the type of array elements. * @return created array. * @throws NullPointerException if elementType is {@code null}. * @throws IllegalArgumentException if elementType is not supported of void.class. * @throws UnsupportedElementTypeException if elementType is not supported by this memory model. */ public MutableArray newEmptyArray(Class elementType) { return newEmptyArray(elementType, 10); } /** * Constructs an empty array with the specified element type and initial capacity. * The element type can be any non-primitive class (inheritor of Object class). * The created array will always implement the {@link MutableObjectArray} interface * (or {@link ObjectInPlaceArray}, if the combiner, passed while creating the memory model, implements * {@link CombinedMemoryModel.CombinerInPlace}). * * @param elementType the type of array elements (non-primitive). * @param initialCapacity the initial capacity of the array. * @return created array. * @throws NullPointerException if elementType is {@code null}. * @throws IllegalArgumentException if elementType is void.class * or if the specified initial length is negative. * @throws UnsupportedElementTypeException if elementType is a primitive type. * @throws TooLargeArrayException if the specified initial length is too large. * @see #isElementTypeSupported(Class) */ public MutableArray newEmptyArray(Class elementType, long initialCapacity) { Arrays.checkElementTypeForNullAndVoid(elementType); if (initialCapacity < 0) { throw new IllegalArgumentException("Negative initial capacity"); } if (!Object.class.isAssignableFrom(elementType)) { throw new UnsupportedElementTypeException( "Primitive element types are not allowed (passed type: " + elementType + ")"); } Class eType = InternalUtils.cast(elementType); if (combiner instanceof CombinerInPlace) { return new MutableCombinedInPlaceArray(eType, initialCapacity, 0, (CombinerInPlace) combiner); } else { return new MutableCombinedArray(eType, initialCapacity, 0, combiner); } } public MutableArray newArray(Class elementType, long initialLength) { return newEmptyArray(elementType, initialLength).length(initialLength).trim(); } public UpdatableArray newUnresizableArray(Class elementType, long length) { Arrays.checkElementTypeForNullAndVoid(elementType); if (length < 0) { throw new IllegalArgumentException("Negative array length"); } if (!Object.class.isAssignableFrom(elementType)) { throw new UnsupportedElementTypeException( "Primitive element types are not allowed (passed type: " + elementType + ")"); } Class eType = InternalUtils.cast(elementType); if (combiner instanceof CombinerInPlace) { return new UpdatableCombinedInPlaceArray(eType, length, length, (CombinerInPlace) combiner); } else { return new UpdatableCombinedArray(eType, length, length, combiner); } } /** * Returns true if this element type is an inheritor of Object class. * * @param elementType the type of array elements. * @return true if this memory model supports this element type. */ public boolean isElementTypeSupported(Class elementType) { Objects.requireNonNull(elementType, "Null elementType argument"); return Object.class.isAssignableFrom(elementType); } public boolean areAllPrimitiveElementTypesSupported() { return false; } public boolean areAllElementTypesSupported() { return false; } /** * This implementation always returns Long.MAX_VALUE for supported * (non-primitive) element types. * Actual maximal possible array length depends on memory model used by the combiner * and on the number of sequential elements of storage arrays * used for storing one element of the combined array. * * @param elementType the type of array elements. * @return maximal possible length of arrays supported by this memory model. * @throws NullPointerException if elementType is {@code null}. */ public long maxSupportedLength(Class elementType) { Objects.requireNonNull(elementType, "Null elementType argument"); return Long.MAX_VALUE; } public boolean isCreatedBy(Array array) { return array instanceof CombinedArray && ((CombinedArray) array).combiner == this.combiner; } /** * Returns true if the passed instance is a combined array created by some instance of * combined memory model. * Returns false if the passed array is {@code null} * or an AlgART array created by another memory model. * * @param array the checked array. * @return true if this array is a combined array created by a combined memory model. */ public static boolean isCombinedArray(Array array) { return array instanceof CombinedArray; } /** * Returns an immutable combined array backed by the storage, * which consists of immutable views of the passed argument * (storage[0].{@link Array#asImmutable() asImmutable()}, * (storage[1].{@link Array#asImmutable() asImmutable()}, ..., * (storage[storage.length-1].{@link Array#asImmutable() asImmutable()}, * with the current combiner (specified while creating this memory model). * *

If modifications of the passed arrays lead to reallocation * of the internal storage, then the returned array ceases to be a view of passed arrays. * The only possible reasons for reallocation are the following: * calling {@link MutableArray#length(long)}, * {@link MutableArray#ensureCapacity(long)} or {@link MutableArray#trim()} methods * for this array, or any modification of this or returned array in a case when * this array is {@link Array#asCopyOnNextWrite() copy-on-next-write}. * * @param elementType the type of created array elements * @param storage the internal storage where the combined array elements will be stored * @return combined array backed by the specified storage */ public ObjectArray asCombinedArray(Class elementType, Array[] storage) { Array[] storageClone = storage.clone(); // preserves the type of storage elements for (int k = 0; k < storageClone.length; k++) { storageClone[k] = storageClone[k].asImmutable(); } if (combiner instanceof CombinerInPlace) { return new CombinedInPlaceArray(elementType, storageClone, (CombinerInPlace) combiner); } else { return new CombinedArray(elementType, storageClone, combiner); } } /** * Returns an unresizable combined array backed by the storage, * which consists of shallow unresizable copies of the passed argument * (storage[0].{@link UpdatableArray#asUnresizable() asUnresizable()}.{@link * Array#shallowClone() shallowClone()}, * (storage[1].{@link UpdatableArray#asUnresizable() asUnresizable()}.{@link * Array#shallowClone() shallowClone()}, ..., * (storage[storage.length-1].{@link UpdatableArray#asUnresizable() asUnresizable()}.{@link * Array#shallowClone() shallowClone()}, * with the current combiner (specified while creating this memory model). * Changes of elements of the passed arrays * will be reflected in the returned combined array, and vice-versa. * *

Using shallow copies means that any further changes of lengths or capacities * of the passed storage arrays will not affect to the length or capacity of * the returned combined array. However, changes of elements of the passed arrays * will be reflected in the returned combined array, and vice-versa. Also it means that * if modifications of the passed arrays characteristics lead to reallocation * of their internal storage, then the returned array ceases to be a view of passed arrays. * The only possible reasons for reallocation are the following: * calling {@link MutableArray#length(long)}, * {@link MutableArray#ensureCapacity(long)} or {@link MutableArray#trim()} methods * for storage[] elements, or any modification of storage[] elements * in a case when they are {@link Array#asCopyOnNextWrite() copy-on-next-write}. * * @param elementType the type of created array elements * @param storage the internal storage where the combined array elements will be stored * @return combined array backed by the specified storage */ public UpdatableObjectArray asUpdatableCombinedArray(Class elementType, UpdatableArray[] storage) { UpdatableArray[] storageClone = storage.clone(); // preserves the type of storage elements for (int k = 0; k < storageClone.length; k++) { storageClone[k] = storageClone[k].asUnresizable().shallowClone(); } if (combiner instanceof CombinerInPlace) { return new UpdatableCombinedInPlaceArray(elementType, storageClone, (CombinerInPlace) combiner); } else { return new UpdatableCombinedArray(elementType, storageClone, combiner); } } /** * Returns a brief string description of this memory model. * *

The result of this method may depend on implementation. * * @return a brief string description of this object. */ public String toString() { return "Combined memory model [combiner: " + this.combiner + "]"; } /** * Returns Java array of shallow copies (produced by {@link Array#shallowClone()} * of arrays used as internal storage, * where the elements of the combined array are stored. * If this combined array implements {@link UpdatableArray} interface, * than the returned array will be an instance of {@link UpdatableArray}[]. * If this combined array implements {@link MutableArray} interface, * than the returned array will be an instance of {@link MutableArray}[]. * *

Using shallow copies means that you are free to change the lengths or capacities * of the returned arrays: it will not affect to the length or capacity of * the passed combined array. However, changes of elements * of the returned arrays will be reflected in the combined array, and vice-versa. * If modifications of the passed combined array characteristics lead to reallocation * of the internal storage, then the returned arrays ceases to be a view of the passed array. * The only possible reasons for reallocation are the following: * calling {@link MutableArray#length(long)}, * {@link MutableArray#ensureCapacity(long)} or {@link MutableArray#trim()} methods * for this array, or any modification of this or returned array in a case when * this array is {@link Array#asCopyOnNextWrite() copy-on-next-write}. * * @param combinedArray the combined array. * @return array of shallow copies of arrays used as internal storage. * @throws NullPointerException if combinedArray is {@code null}. * @throws IllegalArgumentException if combinedArray is not a combined array * (created by this memory model). * @see Array#shallowClone() */ public static Array[] getStorage(Array combinedArray) { Objects.requireNonNull(combinedArray, "Null combinedArray argument"); if (!(combinedArray instanceof CombinedArray cv)) { throw new IllegalArgumentException("The passed argument is not a combined array"); } Array[] result = cv.storage.clone(); for (int k = 0; k < result.length; k++) { result[k] = cv.storage[k].shallowClone(); } return result; } /** * Fully equivalent to {@link #getStorage(Array)} method. * * @param combinedArray the combined array. * @return array of shallow copies of arrays used as internal storage. * @throws NullPointerException if combinedArray is {@code null}. * @throws IllegalArgumentException if combinedArray is not a combined array * (created by this memory model). * @see Array#shallowClone() */ public static UpdatableArray[] getStorage(UpdatableArray combinedArray) { return (UpdatableArray[]) getStorage((Array) combinedArray); } /** * Fully equivalent to {@link #getStorage(Array)} method. * * @param combinedArray the combined array. * @return array of shallow copies of arrays used as internal storage. * @throws NullPointerException if combinedArray is {@code null}. * @throws IllegalArgumentException if combinedArray is not a combined array * (created by this memory model). * @see Array#shallowClone() */ public static MutableArray[] getStorage(MutableArray combinedArray) { return (MutableArray[]) getStorage((Array) combinedArray); } /** * Returns array of string representations (results of toString() method} * of arrays used as internal storage, * where the elements of the combined array are stored. * * @param combinedArray the combined array. * @return array of string representations of arrays used as internal storage. * @throws NullPointerException if combinedArray is {@code null}. * @throws IllegalArgumentException if combinedArray is not a combined array * (created by this memory model). */ public static String[] getStorageToStrings(Array combinedArray) { Objects.requireNonNull(combinedArray, "Null combinedArray argument"); if (!(combinedArray instanceof CombinedArray cv)) { throw new IllegalArgumentException("The passed argument is not a combined array"); } String[] result = new String[cv.storage.length]; for (int k = 0; k < result.length; k++) { result[k] = cv.storage[k].toString(); } return result; } static class CombinedArray extends AbstractArray implements ObjectArray { final Class elementType; final Combiner combiner; Array[] storage; final int[] numbersOfElements; final boolean allNumbersAre1; CombinedArray(Class elementType, Array[] storage, Combiner combiner) { super(Integer.MAX_VALUE, 0); Objects.requireNonNull(storage, "Null storage argument"); Objects.requireNonNull(combiner, "Null combiner argument"); this.combiner = combiner; this.elementType = elementType; this.allNumbersAre1 = checkStorageAndNumbersOfElements(storage, combiner); this.storage = storage; this.numbersOfElements = new int[storage.length]; for (int k = 0; k < storage.length; k++) { this.numbersOfElements[k] = combiner.numbersOfElementsPerOneCombinedElement(k); } this.length = storage[0].length() / numbersOfElements[0]; recalculateCapacity(); } CombinedArray(Class elementType, long initialCapacity, long initialLength, Combiner combiner) { super(initialCapacity, initialLength); Objects.requireNonNull(combiner, "Null combiner argument"); this.combiner = combiner; this.elementType = elementType; boolean unresizableRequired = this.isUnresizable(); UpdatableArray[] stor = combiner.allocateStorage(initialLength, unresizableRequired); this.allNumbersAre1 = checkStorageAndNumbersOfElements(stor, combiner); this.numbersOfElements = new int[stor.length]; for (int k = 0; k < stor.length; k++) { this.numbersOfElements[k] = combiner.numbersOfElementsPerOneCombinedElement(k); } for (int k = 0; k < stor.length; k++) { if (!unresizableRequired && !(stor[k] instanceof MutableArray)) { throw new IllegalArgumentException("Storage array #" + k + " does not implement MutableArray, " + "though the resizable one is required while calling Combiner.allocateStorage method"); } long correctLength = InternalUtils.longMulAndException(initialLength, numbersOfElements[k]); if (stor[k].length() != correctLength) { throw new IllegalArgumentException("Incorrect length of storage array #" + k + ": length = " + storage[k].length() + ", but the correct one is " + correctLength); } } this.capacity = initialCapacity; if (!unresizableRequired) { for (int k = 0; k < stor.length; k++) { ((MutableArray) stor[k]).ensureCapacity(InternalUtils.longMulAndException( initialCapacity, numbersOfElements[k])); } } else { if (initialCapacity != initialLength) { throw new AssertionError("Unequal length and capacity while creating unresizable array!"); } } if (unresizableRequired) { this.storage = stor; } else { // In this case allocateStorage MAY return UpdatableArray[] instead of necessary MutableArray[] this.storage = new MutableArray[stor.length]; System.arraycopy(stor, 0, this.storage, 0, stor.length); } } final void recalculateCapacity() { long cap = Long.MAX_VALUE; for (int k = 0; k < storage.length; k++) { cap = Math.min(cap, storage[k].capacity() / numbersOfElements[k]); } this.capacity = cap; } private static boolean checkStorageAndNumbersOfElements(Array[] storage, Combiner combiner) { Objects.requireNonNull(storage, "Null Array[] storage"); if (storage.length == 0) { throw new IllegalArgumentException("Storage must contain at least 1 array"); } boolean result = true; long length0 = 157; int ne0 = 28; for (int k = 0; k < storage.length; k++) { Objects.requireNonNull(storage[k], "Null storage[" + k + "] array"); int ne = combiner.numbersOfElementsPerOneCombinedElement(k); if (ne <= 0) { throw new IllegalArgumentException("combiner.numbersOfElementsPerOneCombinedElement(" + k + ") = " + ne + " <= 0"); } result &= ne == 1; if (storage[k].length() % ne != 0) { throw new IllegalArgumentException("Incorrect length of storage array #" + k + ": length = " + storage[k].length() + " and is not divided by " + "combiner.numbersOfElementsPerOneCombinedElement(" + k + ") = " + ne); } long length = storage[k].length() / ne; if (k == 0) { length0 = length; ne0 = ne; } else if (length != length0) { throw new IllegalArgumentException("Storage arrays lengths don't correspond to each other: " + " the length of array # " + k + " = " + storage[k].length() + " = " + length + " * " + ne + " (" + ne + " per one combined object), " + "but the length of array #0 = " + storage[0].length() + " = " + length0 + " * " + ne0 + " (" + ne0 + " per one combined object)"); } } return result; } public final Class elementType() { return elementType; } public Class> type() { return InternalUtils.cast(ObjectArray.class); } public Class> updatableType() { return InternalUtils.cast(UpdatableObjectArray.class); } public Class> mutableType() { return InternalUtils.cast(MutableObjectArray.class); } public final Object getElement(long index) { return combiner.get(index, storage); } public final E get(long index) { return combiner.get(index, storage); } public long indexOf(long lowIndex, long highIndex, E value) { long k = Math.max(lowIndex, 0); long n = Math.min(length(), highIndex); if (value == null) { for (; k < n; k++) { if (get(k) == null) { return k; } } } else { for (; k < n; k++) { if (value.equals(get(k))) { return k; } } } return -1; } public long lastIndexOf(long lowIndex, long highIndex, E value) { long k = Math.min(length(), highIndex); // warning: highIndex-1 can be invalid value Long.MAX_VALUE long low = Math.max(lowIndex, 0); if (value == null) { for (; k > low; ) { if (get(--k) == null) { return k; } } } else { for (; k > low; ) { if (value.equals(get(--k))) { return k; } } } return -1; } public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) { Objects.requireNonNull(destArray, "Null destArray argument"); if (count < 0) { throw new IllegalArgumentException("Negative number of loaded elements (" + count + ")"); } if (arrayPos < 0) { throw rangeException(arrayPos); } if (arrayPos > length - count) { throw rangeException(arrayPos + count - 1); } E[] dest = InternalUtils.cast(destArray); if (combiner instanceof BufferedCombiner) { ((BufferedCombiner) combiner).get(arrayPos, dest, destArrayOffset, count, storage); } else { getDataInternal(arrayPos, dest, destArrayOffset, count); } } public void getData(long arrayPos, Object destArray) { Objects.requireNonNull(destArray, "Null destArray argument"); if (arrayPos < 0 || arrayPos > length) { throw rangeException(arrayPos); } int count = java.lang.reflect.Array.getLength(destArray); if (count > length - arrayPos) { count = (int) (length - arrayPos); } E[] dest = InternalUtils.cast(destArray); if (combiner instanceof BufferedCombiner) { ((BufferedCombiner) combiner).get(arrayPos, dest, 0, count, storage); } else { getDataInternal(arrayPos, dest, 0, count); } } private void getDataInternal(long arrayPos, Object[] dest, int destArrayOffset, int count) { for (int k = destArrayOffset, kMax = destArrayOffset + count; k < kMax; k++, arrayPos++) { dest[k] = combiner.get(arrayPos, storage); } } public Array subArray(long fromIndex, long toIndex) { checkSubArrayArguments(fromIndex, toIndex); Array[] stor = new Array[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = storage[k].subArray( fromIndex * numbersOfElements[k], toIndex * numbersOfElements[k]); } return new CombinedArray(this.elementType, stor, this.combiner); } public Array subArr(long position, long count) { checkSubArrArguments(position, count); Array[] stor = new Array[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = storage[k].subArr( position * numbersOfElements[k], count * numbersOfElements[k]); } return new CombinedArray(this.elementType, stor, this.combiner); } public DataObjectBuffer buffer(DataBuffer.AccessMode mode, long capacity) { return InternalUtils.cast(super.buffer(mode, capacity)); } public DataObjectBuffer buffer(DataBuffer.AccessMode mode) { return InternalUtils.cast(super.buffer(mode)); } public DataObjectBuffer buffer(long capacity) { return InternalUtils.cast(super.buffer(capacity)); } public DataObjectBuffer buffer() { return InternalUtils.cast(super.buffer()); } public final ObjectArray asImmutable() { if (isImmutable()) { return this; } Array[] stor = new Array[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = storage[k].asImmutable(); } return new CombinedArray(this.elementType, stor, this.combiner); } public boolean isImmutable() { return storage[0].isImmutable(); } public final ObjectArray asTrustedImmutable() { if (isImmutable()) { return this; } Array[] stor = new Array[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = storage[k].asTrustedImmutable(); } return new CombinedArray(this.elementType, stor, this.combiner); } public final void checkUnallowedMutation() throws UnallowedMutationError { try { for (Array s : storage) { s.checkUnallowedMutation(); } } catch (UnallowedMutationError ex) { UnallowedMutationError e = new UnallowedMutationError( "Unallowed mutations of trusted immutable array " + " are detected by checkUnallowedMutation() method [" + this + "]"); e.initCause(ex); throw e; } } public Array asCopyOnNextWrite() { if (isImmutable()) { return this; } Array[] stor = new Array[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = storage[k].asCopyOnNextWrite(); } return new CombinedArray(this.elementType, stor, this.combiner); } public boolean isCopyOnNextWrite() { return storage[0].isCopyOnNextWrite(); } public boolean isUnresizable() { return true; } public Array shallowClone() { Array[] stor = new Array[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = storage[k].shallowClone(); } return new CombinedArray(this.elementType, stor, this.combiner); } public MutableObjectArray mutableClone(MemoryModel memoryModel) { return InternalUtils.cast(super.mutableClone(memoryModel)); } public UpdatableObjectArray updatableClone(MemoryModel memoryModel) { return InternalUtils.cast(super.updatableClone(memoryModel)); } public E[] ja() { return toJavaArray(); } public ObjectArray cast(Class elementType) { if (!elementType.isAssignableFrom(this.elementType)) { throw new ClassCastException("Illegal desired element type " + elementType + " for " + this); } return InternalUtils.cast(this); } @Override public final void loadResources(ArrayContext context) { for (int k = 0; k < storage.length; k++) { storage[k].loadResources(context == null ? null : context.part(k, k + 1, storage.length)); } } @Override public final void flushResources(ArrayContext context, boolean forcePhysicalWriting) { for (int k = 0; k < storage.length; k++) { storage[k].flushResources( context == null ? null : context.part(k, k + 1, storage.length), forcePhysicalWriting); } } @Override public final void freeResources(ArrayContext context, boolean forcePhysicalWriting) { for (int k = 0; k < storage.length; k++) { storage[k].freeResources( context == null ? null : context.part(k, k + 1, storage.length), forcePhysicalWriting); } } public String toString() { return (isImmutable() ? "immutable " : "") + "combined array " + elementType.getName() + "[" + length() + "], capacity " + capacity(); } public final boolean equals(Object obj) { if (obj instanceof CombinedArray && ((CombinedArray) obj).combiner == this.combiner) { for (int k = 0; k < storage.length; k++) { CombinedArray a = (CombinedArray) obj; if (!storage[k].equals(a.storage[k])) { return false; } } return true; } return super.equals(obj); } final Object javaArrayInternal() { return null; } final int javaArrayOffsetInternal() { return 0; } } static class UpdatableCombinedArray extends CombinedArray implements UpdatableObjectArray { UpdatableCombinedArray(Class elementType, UpdatableArray[] storage, Combiner combiner) { super(elementType, storage, combiner); } UpdatableCombinedArray(Class elementType, long initialCapacity, long initialLength, Combiner combiner) { super(elementType, initialCapacity, initialLength, combiner); } public final void setElement(long index, Object value) { if (value != null && !elementType.isAssignableFrom(value.getClass())) { throw new ClassCastException("Invalid type of setElement argument"); } combiner.set(index, InternalUtils.cast(value), (UpdatableArray[]) storage); } public final void set(long index, E value) { setElement(index, value); } public UpdatableArray setData(long arrayPos, Object srcArray, int srcArrayOffset, int count) { Objects.requireNonNull(srcArray, "Null srcArray argument"); if (count < 0) { throw new IllegalArgumentException("Negative number of stored elements (" + count + ")"); } if (arrayPos < 0) { throw rangeException(arrayPos); } if (arrayPos > length - count) { throw rangeException(arrayPos + count - 1); } E[] src = InternalUtils.cast(srcArray); if (combiner instanceof BufferedCombiner) { ((BufferedCombiner) combiner).set(arrayPos, src, srcArrayOffset, count, (UpdatableArray[]) storage); } else { setDataInternal(arrayPos, src, srcArrayOffset, count); } return this; } public UpdatableArray setData(long arrayPos, Object srcArray) { Objects.requireNonNull(srcArray, "Null srcArray argument"); if (arrayPos < 0 || arrayPos > length) { throw rangeException(arrayPos); } int count = java.lang.reflect.Array.getLength(srcArray); if (count > length - arrayPos) { count = (int) (length - arrayPos); } E[] src = InternalUtils.cast(srcArray); if (combiner instanceof BufferedCombiner) { ((BufferedCombiner) combiner).set(arrayPos, src, 0, count, (UpdatableArray[]) storage); } else { setDataInternal(arrayPos, src, 0, count); } return this; } private void setDataInternal(long arrayPos, E[] src, int srcArrayOffset, int count) { UpdatableArray[] stor = (UpdatableArray[]) storage; for (int k = srcArrayOffset, kMax = srcArrayOffset + count; k < kMax; k++, arrayPos++) { if (src[k] != null && !elementType.isAssignableFrom(src[k].getClass())) { throw new ClassCastException("Invalid type of setElement argument"); } combiner.set(arrayPos, src[k], stor); } } public final void copy(long destIndex, long srcIndex) { if (srcIndex < 0 || srcIndex >= length) { throw rangeException(srcIndex); } if (destIndex < 0 || destIndex >= length) { throw rangeException(destIndex); } UpdatableArray[] stor = (UpdatableArray[]) storage; if (allNumbersAre1) { for (UpdatableArray s : stor) { s.copy(destIndex, srcIndex); } } else { for (int k = 0; k < storage.length; k++) { int ne = numbersOfElements[k]; stor[k].copy(destIndex * ne, srcIndex * ne, ne); } } } public final void copy(long destIndex, long srcIndex, long count) { if (count < 0) { throw new IndexOutOfBoundsException("Negative number of copied elements (count = " + count + ") in " + getClass()); } if (srcIndex < 0) { throw rangeException(srcIndex); } if (srcIndex > length - count) { throw rangeException(srcIndex + count - 1); } if (destIndex < 0) { throw rangeException(destIndex); } if (destIndex > length - count) { throw rangeException(destIndex + count - 1); } UpdatableArray[] stor = (UpdatableArray[]) storage; if (allNumbersAre1) { for (UpdatableArray s : stor) { s.copy(destIndex, srcIndex, count); } } else { for (int k = 0; k < storage.length; k++) { int ne = numbersOfElements[k]; stor[k].copy(destIndex * ne, srcIndex * ne, count * ne); } } } public final void swap(long firstIndex, long secondIndex) { if (firstIndex < 0 || firstIndex >= length) { throw rangeException(firstIndex); } if (secondIndex < 0 || secondIndex >= length) { throw rangeException(secondIndex); } UpdatableArray[] stor = (UpdatableArray[]) storage; if (allNumbersAre1) { for (int k = 0; k < storage.length; k++) { stor[k].swap(firstIndex, secondIndex); } } else { for (int k = 0; k < storage.length; k++) { int ne = numbersOfElements[k]; stor[k].swap(firstIndex * ne, secondIndex * ne, ne); } } } public final void swap(long firstIndex, long secondIndex, long count) { if (count < 0) { throw new IndexOutOfBoundsException("Negative number of swapped elements (count = " + count + ") in " + getClass()); } if (firstIndex < 0) { throw rangeException(firstIndex); } if (firstIndex > length - count) { throw rangeException(firstIndex + count - 1); } if (secondIndex < 0) { throw rangeException(secondIndex); } if (secondIndex > length - count) { throw rangeException(secondIndex + count - 1); } UpdatableArray[] stor = (UpdatableArray[]) storage; if (allNumbersAre1) { for (int k = 0; k < storage.length; k++) { stor[k].swap(firstIndex, secondIndex, count); } } else { for (int k = 0; k < storage.length; k++) { int ne = numbersOfElements[k]; stor[k].swap(firstIndex * ne, secondIndex * ne, count * ne); } } } public UpdatableArray copy(Array src) { if (src instanceof CombinedArray a && a.combiner == this.combiner) { AbstractArray.checkCopyArguments(this, src); for (int k = 0; k < storage.length; k++) { ((UpdatableArray[]) storage)[k].copy(a.storage[k]); } } else { defaultCopy(this, src); } return this; } public UpdatableArray swap(UpdatableArray another) { if (another instanceof UpdatableCombinedArray a && a.combiner == this.combiner) { AbstractArray.checkSwapArguments(this, another); for (int k = 0; k < storage.length; k++) { ((UpdatableArray) storage[k]).swap((UpdatableArray) a.storage[k]); } } else { defaultSwap(this, another); } return this; } public UpdatableObjectArray fill(Object value) { copy(Arrays.nObjectCopies(length, value, elementType)); return this; } public UpdatableObjectArray fill(long position, long count, Object value) { subArr(position, count).copy(Arrays.nObjectCopies(count, value, elementType)); return this; } public UpdatableObjectArray subArray(long fromIndex, long toIndex) { checkSubArrayArguments(fromIndex, toIndex); UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((UpdatableArray) storage[k]).subArray( fromIndex * numbersOfElements[k], toIndex * numbersOfElements[k]); } return new UpdatableCombinedArray(this.elementType, stor, this.combiner); } public UpdatableObjectArray subArr(long position, long count) { checkSubArrArguments(position, count); UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((UpdatableArray) storage[k]).subArr( position * numbersOfElements[k], count * numbersOfElements[k]); } return new UpdatableCombinedArray(this.elementType, stor, this.combiner); } public UpdatableArray asCopyOnNextWrite() { UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((UpdatableArray[]) storage)[k].asCopyOnNextWrite(); } return new UpdatableCombinedArray(this.elementType, stor, this.combiner); } public UpdatableObjectArray asUnresizable() { return this; } public void setNonNew() { setNewStatus(false); // not necessary in current implementation } public UpdatableObjectArray shallowClone() { UpdatableCombinedArray result = InternalUtils.cast(super.standardObjectClone()); // not call super.clone() here! super.clone may be is already overridden! result.storage = shallowCloneArrays(storage); return result; } public UpdatableObjectArray cast(Class elementType) { return InternalUtils.cast(super.cast(elementType)); } public String toString() { return "unresizable combined array " + elementType.getName() + "[" + length() + "], capacity " + capacity(); } } static class MutableCombinedArray extends UpdatableCombinedArray implements MutableObjectArray { MutableCombinedArray(Class elementType, MutableArray[] storage, Combiner combiner) { super(elementType, storage, combiner); } MutableCombinedArray(Class elementType, long initialCapacity, long initialLength, Combiner combiner) { super(elementType, initialCapacity, initialLength, combiner); } public MutableObjectArray length(long newLength) { if (newLength != this.length) { for (int k = 0; k < storage.length; k++) { InternalUtils.longMulAndException(newLength, numbersOfElements[k]); // check possible overflows } for (int k = 0; k < storage.length; k++) { ((MutableArray[]) storage)[k].length(newLength * numbersOfElements[k]); } this.length = newLength; recalculateCapacity(); } return this; } public MutableObjectArray ensureCapacity(long minCapacity) { if (minCapacity > capacity()) { for (int k = 0; k < storage.length; k++) { InternalUtils.longMulAndException(minCapacity, numbersOfElements[k]); // check possible overflows } for (int k = 0; k < storage.length; k++) { ((MutableArray[]) storage)[k].ensureCapacity(minCapacity * numbersOfElements[k]); } recalculateCapacity(); } return this; } public MutableObjectArray trim() { if (this.length < capacity()) { for (int k = 0; k < storage.length; k++) { ((MutableArray[]) storage)[k].trim(); } recalculateCapacity(); } return this; } public MutableObjectArray append(Array appendedArray) { defaultAppend(this, appendedArray); return this; } public final Object popElement() { return pop(); } public final E pop() { long index = length() - 1; if (index < 0) { throw new EmptyStackException(); } E result = get(index); length(index); return result; } public final void pushElement(Object value) { long index = length(); if (index == Long.MAX_VALUE) { throw new TooLargeArrayException("Too large desired array length (>Long.MAX_VALUE)"); } length(index + 1); setElement(index, value); } public final void push(E value) { pushElement(value); } public void removeTop() { long index = length() - 1; if (index < 0) { throw new EmptyStackException(); } length(index); } public MutableObjectArray setData(long arrayPos, Object srcArray, int srcArrayOffset, int count) { super.setData(arrayPos, srcArray, srcArrayOffset, count); return this; } public MutableObjectArray setData(long arrayPos, Object srcArray) { super.setData(arrayPos, srcArray); return this; } public MutableObjectArray copy(Array src) { super.copy(src); return this; } public MutableObjectArray swap(UpdatableArray another) { super.swap(another); return this; } public boolean isUnresizable() { return false; } public MutableObjectArray asCopyOnNextWrite() { MutableArray[] stor = (MutableArray[]) storage.clone(); for (int k = 0; k < storage.length; k++) { stor[k] = stor[k].asCopyOnNextWrite(); // it is necessary to avoid future changes in the result storage via CombinedMemoryModel.storage method } return new MutableCombinedArray(this.elementType, stor, this.combiner); } public UpdatableObjectArray asUnresizable() { UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((MutableArray[]) storage)[k].asUnresizable(); // it is necessary to avoid future changes in the result storage via CombinedMemoryModel.storage method } return new UpdatableCombinedArray(this.elementType, stor, this.combiner); } public MutableObjectArray shallowClone() { return InternalUtils.cast(super.shallowClone()); } public MutableObjectArray cast(Class elementType) { return InternalUtils.cast(super.cast(elementType)); } public String toString() { return "mutable combined array " + elementType.getName() + "[" + length() + "], capacity " + capacity(); } } private static final class CombinedInPlaceArray extends CombinedArray implements ObjectInPlaceArray { CombinedInPlaceArray( Class elementType, Array[] storage, CombinerInPlace combiner) { super(elementType, storage, combiner); } CombinedInPlaceArray( Class elementType, long initialCapacity, long initialLength, CombinerInPlace combiner) { super(elementType, initialCapacity, initialLength, combiner); } public long indexOf(long lowIndex, long highIndex, E value) { if (value == null) { return super.indexOf(lowIndex, highIndex, value); } else { long k = Math.max(lowIndex, 0); long n = Math.min(length(), highIndex); if (k >= n) { return -1; } E e = allocateElement(); for (; k < n; k++) { if (value.equals(getInPlace(k, e))) { return k; } } } return -1; } public long lastIndexOf(long lowIndex, long highIndex, E value) { if (value == null) { return super.lastIndexOf(lowIndex, highIndex, value); } else { long k = Math.min(length(), highIndex); // warning: highIndex-1 can be invalid value Long.MAX_VALUE long low = Math.max(lowIndex, 0); if (k <= low) { return -1; } E e = allocateElement(); for (; k > low; k--) { if (value.equals(getInPlace(k, e))) { return k; } } } return -1; } public E allocateElement() { return ((CombinerInPlace) combiner).allocateElement(); } public E getInPlace(long index, Object resultValue) { E resValue = InternalUtils.cast(resultValue); ((CombinerInPlace) combiner).getInPlace(index, resValue, storage); return resValue; } } private static final class UpdatableCombinedInPlaceArray extends UpdatableCombinedArray implements UpdatableObjectInPlaceArray { UpdatableCombinedInPlaceArray( Class elementType, UpdatableArray[] storage, CombinerInPlace combiner) { super(elementType, storage, combiner); } UpdatableCombinedInPlaceArray( Class elementType, long initialCapacity, long initialLength, CombinerInPlace combiner) { super(elementType, initialCapacity, initialLength, combiner); } public E allocateElement() { return ((CombinerInPlace) combiner).allocateElement(); } public E getInPlace(long index, Object resultValue) { E resValue = InternalUtils.cast(resultValue); ((CombinerInPlace) combiner).getInPlace(index, resValue, storage); return resValue; } public UpdatableObjectInPlaceArray subArray(long fromIndex, long toIndex) { checkSubArrayArguments(fromIndex, toIndex); UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((UpdatableArray) storage[k]).subArray( fromIndex * numbersOfElements[k], toIndex * numbersOfElements[k]); } return new UpdatableCombinedInPlaceArray(this.elementType, stor, (CombinerInPlace) this.combiner); } public UpdatableObjectInPlaceArray subArr(long position, long count) { checkSubArrArguments(position, count); UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((UpdatableArray) storage[k]).subArr( position * numbersOfElements[k], count * numbersOfElements[k]); } return new UpdatableCombinedInPlaceArray(this.elementType, stor, (CombinerInPlace) this.combiner); } public UpdatableObjectInPlaceArray asUnresizable() { return this; } } private static final class MutableCombinedInPlaceArray extends MutableCombinedArray implements MutableObjectInPlaceArray { MutableCombinedInPlaceArray( Class elementType, MutableArray[] storage, CombinerInPlace combiner) { super(elementType, storage, combiner); } MutableCombinedInPlaceArray( Class elementType, long initialCapacity, long initialLength, CombinerInPlace combiner) { super(elementType, initialCapacity, initialLength, combiner); } public E allocateElement() { return ((CombinerInPlace) combiner).allocateElement(); } public E getInPlace(long index, Object resultValue) { E resValue = InternalUtils.cast(resultValue); ((CombinerInPlace) combiner).getInPlace(index, resValue, storage); return resValue; } public MutableObjectInPlaceArray setData(long arrayPos, Object srcArray, int srcArrayOffset, int count) { super.setData(arrayPos, srcArray, srcArrayOffset, count); return this; } public MutableObjectInPlaceArray setData(long arrayPos, Object srcArray) { super.setData(arrayPos, srcArray); return this; } public MutableObjectInPlaceArray copy(Array src) { super.copy(src); return this; } public MutableObjectInPlaceArray swap(UpdatableArray another) { super.swap(another); return this; } public MutableObjectInPlaceArray length(long newLength) { super.length(newLength); return this; } public MutableObjectInPlaceArray ensureCapacity(long minCapacity) { super.ensureCapacity(minCapacity); return this; } public MutableObjectInPlaceArray trim() { super.trim(); return this; } public MutableObjectInPlaceArray append(Array appendedArray) { super.append(appendedArray); return this; } public UpdatableObjectInPlaceArray subArray(long fromIndex, long toIndex) { checkSubArrayArguments(fromIndex, toIndex); UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((UpdatableArray) storage[k]).subArray( fromIndex * numbersOfElements[k], toIndex * numbersOfElements[k]); } return new UpdatableCombinedInPlaceArray(this.elementType, stor, (CombinerInPlace) this.combiner); } public UpdatableObjectInPlaceArray subArr(long position, long count) { checkSubArrArguments(position, count); UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((UpdatableArray) storage[k]).subArr( position * numbersOfElements[k], count * numbersOfElements[k]); } return new UpdatableCombinedInPlaceArray(this.elementType, stor, (CombinerInPlace) this.combiner); } public MutableObjectInPlaceArray asCopyOnNextWrite() { MutableArray[] stor = (MutableArray[]) storage.clone(); for (int k = 0; k < storage.length; k++) { stor[k] = stor[k].asCopyOnNextWrite(); // it is necessary to avoid future changes in the result storage via CombinedMemoryModel.storage method } return new MutableCombinedInPlaceArray(this.elementType, stor, (CombinerInPlace) this.combiner); } public UpdatableObjectInPlaceArray asUnresizable() { UpdatableArray[] stor = new UpdatableArray[storage.length]; for (int k = 0; k < storage.length; k++) { stor[k] = ((MutableArray[]) storage)[k].asUnresizable(); // it is necessary to avoid future changes in the result storage via CombinedMemoryModel.storage method } return new UpdatableCombinedInPlaceArray(this.elementType, stor, (CombinerInPlace) this.combiner); } public MutableObjectInPlaceArray shallowClone() { return InternalUtils.cast(super.shallowClone()); } } private static Array[] shallowCloneArrays(Array[] src) { Objects.requireNonNull(src, "Cannot make shallow clones of arrays: null Array[] src"); Array[] result = src.clone(); // preserves the type of src elements for (int k = 0; k < result.length; k++) { Objects.requireNonNull(src[k], "Cannot make shallow clones of arrays: array #" + k + " is null"); result[k] = src[k].shallowClone(); } return result; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy