/*
* ============================================================================
* GNU Lesser General Public License
* ============================================================================
*
* Square Brackets - Java Array Framework.
* Copyright (C) 2013 Beatgrid Media B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Beatgrid Media B.V.
* Sisalbaan 5-A
* 2352 AZ Leiderdorp
* The Netherlands
*
* info@squarebrackets.org
* http://squarebrackets.org
*/
package org.squarebrackets;
import org.squarebrackets.function.CharConsumer;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;
import java.io.File;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Path;
import java.util.ConcurrentModificationException;
import java.util.NoSuchElementException;
import static java.util.Objects.requireNonNull;
/**
* <p>Bounded-size mutable wrapper for {@code CharBuffer}s.</p>
*
* <p>This bounded char array is constructed by the static interface method of {@code BoundedCharArray}. The
* {@code valueOf} method wraps a new {@code BoundedCharArray} around a char buffer.</p>
*
* <p>Arrays express characteristics to indicate that, for instance, elements are listed in sorted order, or no
* duplicate elements exist. These characteristics allow array implementations to optimize their algorithms based
* on the contents of the array. For example, operations that lookup elements can use a binary search algorithm if
* the array is marked as being sorted. These optimizations may reduce the time complexity of such operations by
* several orders of magnitude.
* Bounded primitive arrays, such as this class, always report {@code RESIZABLE}, {@code MUTABLE} and {@code NONNULL}.</p>
*
* <p>This class attempts to establish these characteristics to accommodate these optimizations at best effort. This
* means that this implementation determines which set of characteristics apply to its elements without traversing these
* elements, in other words, without incurring a performance penalty. Characteristics are always invalidated if
* conditions of these characteristics are broken as result of array modification. Characteristics are only added in
* scenarios in which their conditions can be 'tested' without traversal of elements. For example, the {@code SORTED}
* characteristic is added if the length of an array is reduced to zero or one element, or if all elements are replaced
* by an array that is marked as being sorted. The semantics of characteristics processing is documented for each
* "destructive" method. Users can take full control of characteristics assignment by means of the
* {@link org.squarebrackets.MutableArray#doAction(org.squarebrackets.ArrayAction)} method.</p>
*
* @author Leon van Zantvoort
*/
@NotThreadSafe
class BoundedCharBufferArrayImpl extends MutableCharBufferArrayImpl implements BoundedCharArray {
/** Specifies if reserved elements need to be reset to default values. */
private boolean clearReservedElements = false;
/** Counter for fail-fast error detection. */
protected transient int modCount = 0;
/** Memory mapped file, or {@code null} if array is not backed by a memory mapped file. */
private final File file;
/** Memory mapped buffer, or {@code null} if array is not backed by a memory mapped file. */
private final transient MappedByteBuffer buffer;
/**
* Package visible constructor.
*
* @param buffer backing buffer.
* @param characteristics array characteristics.
* @param duplicate {@code true} to duplicate the {@code buffer}, {@code false} to use {@code buffer} reference
* as backing buffer.
* @throws IllegalArgumentException if buffer is read only.
*/
BoundedCharBufferArrayImpl(@Nonnull CharBuffer buffer, int characteristics, boolean duplicate) {
super(buffer, characteristics, duplicate);
this.file = null;
this.buffer = null;
}
/**
* Package visible constructor.
*/
BoundedCharBufferArrayImpl(@Nonnull Path file, int capacity) {
this(file.toFile(), createMappedBuffer(file.toFile(), capacity, true), 0);
}
/**
* Private constructor.
*/
private BoundedCharBufferArrayImpl(@Nonnull File file, @Nonnull MappedByteBuffer buffer, int length) {
super((CharBuffer) buffer.asCharBuffer().limit(length), 0, false);
this.file = file;
this.buffer = buffer;
}
/**
* Allow array implementation to filter characteristics based on array specific logic.
*
* <p>Default implementation passes specified value straight through.</p>
*
* @param characteristics characteristics to be filtered.
* @return the filtered characteristics.
*/
@Override
protected int filterCharacteristics(int characteristics) {
return (characteristics | NONNULL | MUTABLE | RESIZABLE) & ~REPLACEABLE;
}
/**
* Specifies if reserved elements need to be reset to default values. This flag is set to {@code false} by default
* for primitive arrays.
*
* @param flag {@code true} if reserved elements need to be reset, {@code false} otherwise.
*/
@Override
public void setClearReservedElements(boolean flag) {
// Must not be invoked as sub array.
clearReservedElements = flag;
}
/**
* Returns {@code true} if reserved elements need to be reset to default values, {@code false} otherwise.
*
* <p>Sub arrays must override this method and delegate the invocation to the parent.</p>
*
* @return {@code true} if reserved elements need to be reset, {@code false} otherwise.
*/
protected boolean clearReservedElements() {
return clearReservedElements;
}
/**
* Fail-fast error detection. Sub classes (such as sub arrays) may override this method if they wish to implement
* concurrent modification checking.
*
* @throws ConcurrentModificationException if parent array has been structurally modified.
*/
protected void checkForComodification() {
}
/**
* Updates length of this array and increases modCount.
*
* <p>Sub arrays must override this method and delegate the invocation this implementation by calling
* {@code super.updateLength()}, next they must delegate the invocation to the parent array.</p>
*
* @param diff number of elements that are added / removed.
*/
protected void updateLength(int diff) {
modCount++; // Sub arrays must increment modCount of parent array as well.
int newLimit = elements.limit() + diff;
if (clearReservedElements && diff < 0) {
elements.mark().position(newLimit);
while (elements.hasRemaining()) {
elements.put((char) 0);
}
elements.reset();
}
elements.limit(newLimit);
}
/**
* Returns the number of additional elements that this array can store (in the absence of memory or resource
* constraints), or {@code Integer.MAX_VALUE} if there is no intrinsic limit.
*
* @return the remaining capacity.
*/
@Override
public int remainingCapacity() {
return elements.capacity() - elements.limit();
}
/**
* Iterates through the array and invokes the consumer's {@code accept} method for each value within this array.
*
* @param consumer consumer that is called for each value within this array.
* @throws NullPointerException if the specified consumer is null.
*/
@SuppressWarnings("ConstantConditions")
@Override
public void forEachChar(@Nonnull CharConsumer consumer) {
requireNonNull(consumer);
final int expectedModCount = modCount;
if (elements.hasArray()) {
final char[] a = elements.array();
final int fromIndex = offset();
final int toIndex = fromIndex + elements.limit();
for (int index = fromIndex; modCount == expectedModCount && index < toIndex; index++) {
consumer.accept(a[index]);
}
} else {
final int fromIndex = elements.position();
final int toIndex = elements.limit();
for (int index = fromIndex; modCount == expectedModCount && index < toIndex; index++) {
consumer.accept(elements.get(index));
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Replaces all elements of this array by the elements of the specified array. The semantics of this method are less
* strict than the ones expressed by {@code MutableArray}; The length of the array that is passed as argument needs
* to be less than or equal to this array's capacity.
*
* <p>The length of {@code this} array is increased or decreased by length difference between {@code this} array and
* {@code array} argument.</p>
*
* <p><strong>Characteristics:</strong></p>
*
* <p>Modification of this array may affect the array's characteristics. The array implementations of the
* Java Array Framework, as returned by the static factory methods, adhere to the characteristics contract:</p>
*
* <ul>
* <li>if the specified {@code array} reports {@code SORTED}, {@code SORTED} is reported after this invocation if
* the comparator of the specified {@code array} is equal to this array's comparator;</li>
* <li>if the specified {@code array} reports {@code SORTED} and {@code DISTINCT}, {@code DISTINCT} is reported
* after this invocation if the comparator of the specified {@code array} is equal to this array's comparator.
* </ul>
*
* @param array array to replace elements of this array.
* @throws NullPointerException if the specified array is null or if the element is replaced with a null value.
* @throws IllegalStateException if elements cannot be replaced due to capacity issues.
*/
@SuppressWarnings("InconsistentJavaDoc")
@Override
public void setAll(@Nonnull Array<? extends Character> array) {
int offset = elements.position();
int arrayLength = array.length();
if (arrayLength <= elements.capacity() - offset) {
int length = elements.remaining();
CharArray cast = ArrayCast.toCharArray(array);
if (cast != null && elements.hasArray()) {
cast.toArray(elements.array(), offset, arrayLength);
updateLength(arrayLength - length); // Invoke updateLength after addition to provide failure atomicity.
} else {
char[] a = NativeArrays.toCharArray(array);
updateLength(arrayLength - length); // Invoke updateLength after addition to provide failure atomicity.
elements.mark();
elements.put(a, 0, arrayLength).reset();
}
characteristics(array.characteristics()); // All elements are replaced by new array; Copy characteristics.
if (hasCharacteristics(SORTED)) {
if (array.getComparator() != null) {
removeCharacteristics(SORTED | DISTINCT);
} else {
updateCharacteristics(offset, array);
}
}
} else {
throw new IllegalStateException("Array full.");
}
}
/**
* Appends the specified object to the end of this array. Length of this array is increased by one.
*
* <p><strong>Characteristics:</strong></p>
*
* <p>Modification of this array may affect this array's characteristics. The array implementations of the
* Java Array Framework, as returned by the static factory methods, adhere to the characteristics contract:</p>
*
* <ul>
* <li>if this array reports {@code SORTED} prior to this call, {@code SORTED} is reported after this
* invocation if the specified {@code object} does not break the sorted order of this array;</li>
* <li>if this array reports {@code SORTED} and {@code DISTINCT} prior to this call, {@code DISTINCT} is
* reported after this invocation if the specified {@code object} was not already present in this array.</li>
* </ul>
*
* @param object object to to be added.
* @throws NullPointerException if the specified {@code object} is null.
* @throws IllegalStateException if element cannot be inserted due to capacity issues.
*/
@SuppressWarnings("InconsistentJavaDoc")
@Override
public void add(Character object) {
if (Tripwire.ENABLED) {
Tripwire.trip(getClass());
}
addChar(object);
}
/**
* Appends the specified char value to the end of this array. Length of this array is increased by one.
*
* <p><strong>Characteristics:</strong></p>
*
* <p>Modification of this array may affect this array's characteristics. The array implementations of the
* Java Array Framework, as returned by the static factory methods, adhere to the characteristics contract:</p>
*
* <ul>
* <li>if this array reports {@code SORTED} prior to this call, {@code SORTED} is reported after this
* invocation if the specified {@code object} does not break the sorted order of this array;</li>
* <li>if this array reports {@code SORTED} and {@code DISTINCT} prior to this call, {@code DISTINCT} is
* reported after this invocation if the specified {@code object} was not already present in this array.</li>
* </ul>
*
* @param value char value to to be added.
* @throws IllegalStateException if element cannot be inserted due to capacity issues.
*/
@Override
public void addChar(char value) {
int elementIndex = elements.limit();
if (elementIndex < elements.capacity()) {
updateLength(1);
elements.mark().position(elementIndex);
elements.put(value).reset();
if (hasCharacteristics(SORTED)) {
updateCharacteristics(elementIndex, value);
}
} else {
throw new IllegalStateException("Array full.");
}
}
/**
* Appends the elements of the specified array to the end of this array. Length of {@code this} array is increased
* by {@code array.length()}.
*
* <p><strong>Characteristics:</strong></p>
*
* <p>Modification of this array may affect the array's characteristics. The array implementations of the
* Java Array Framework, as returned by the static factory methods, adhere to the characteristics contract:</p>
*
* <ul>
* <li>if this array reports {@code SORTED} prior to this call, {@code SORTED} is reported after this
* invocation if the specified {@code array} reports {@code SORTED} and if it does not break the sorted order of
* this array;</li>
* <li>if this array reports {@code SORTED} and {@code DISTINCT} prior to this call, {@code DISTINCT} is reported
* after this invocation if the specified {@code array} reports {@code SORTED} and {@code DISTINCT} and if the
* specified {@code array} does not contain any objects that were already present in this array.</li>
* </ul>
*
* @param array array to be added.
* @throws NullPointerException if the specified array is null or if the array contains null elements.
* @throws IllegalStateException if elements cannot be inserted due to capacity issues.
*/
@SuppressWarnings("InconsistentJavaDoc")
@Override
public void addAll(@Nonnull Array<? extends Character> array) {
int arrayLength = array.length();
int elementIndex = elements.limit();
if (arrayLength <= elements.capacity() - elementIndex) {
CharArray cast = ArrayCast.toCharArray(array);
if (cast != null && elements.hasArray()) {
cast.toArray(elements.array(), elementIndex, arrayLength);
updateLength(arrayLength); // Invoke updateLength after addition to provide failure atomicity.
} else {
char[] a = NativeArrays.toCharArray(array);
updateLength(arrayLength); // Invoke updateLength after obtaining native array to provide failure atomicity.
elements.mark().position(elementIndex);
elements.put(a, 0, arrayLength).reset();
}
retainCharacteristics(array.characteristics());
if (hasCharacteristics(SORTED)) {
updateCharacteristics(elementIndex, array);
}
} else {
throw new IllegalStateException("Array full.");
}
}
/**
* Removes last element. Length of this array is decreased by one.
*
* <p><strong>Characteristics:</strong></p>
*
* <p>Modification of this array may affect the array's characteristics. The array implementations of the
* Java Array Framework, as returned by the static factory methods, adhere to the characteristics contract:</p>
*
* <ul>
* <li>if the array contains no elements after this invocation, {@code NONNULL}, {@code SORTED} and
* {@code DISTINCT} are reported.
* </ul>
*
* @return element that is removed.
* @throws NoSuchElementException if array is already empty.
*/
@Override
public Character remove() {
if (Tripwire.ENABLED) {
Tripwire.trip(getClass());
}
return charRemove();
}
/**
* Removes last element. Length of this array is decreased by one.
*
* <p><strong>Characteristics:</strong></p>
*
* <p>Modification of this array may affect the array's characteristics. The array implementations of the
* Java Array Framework, as returned by the static factory methods, adhere to the characteristics contract:</p>
*
* <ul>
* <li>if the array contains one element after this invocation, {@code SORTED} and {@code DISTINCT} are reported.
* </ul>
*
* @return element that is removed.
* @throws NoSuchElementException if array is already empty.
*/
@Override
public char charRemove() {
int position = elements.position();
int elementIndex = elements.limit() - 1;
if (elementIndex >= position) {
char oldValue = elements.get(elementIndex);
updateLength(-1);
if (elements.remaining() <= 1) {
addCharacteristics(SORTED | DISTINCT);
}
return oldValue;
}
throw new NoSuchElementException();
}
/**
* Removes all elements.
*
* <p><strong>Characteristics:</strong></p>
*
* <p>Modification of this array may affect the array's characteristics. The array implementations of the
* Java Array Framework, as returned by the static factory methods, adhere to the characteristics contract:</p>
*
* <ul>
* <li>After this invocation, {@code NONNULL}, {@code SORTED} and {@code DISTINCT} are reported.
* </ul>
*/
@Override
public void clear() {
int length = elements.remaining();
updateLength(-length);
addCharacteristics(SORTED | DISTINCT);
}
@SuppressWarnings({"MissingMethodJavaDoc", "MissingFieldJavaDoc"})
private static class SubBoundedCharBufferArrayImpl extends BoundedCharBufferArrayImpl {
final BoundedCharBufferArrayImpl parent;
SubBoundedCharBufferArrayImpl(@Nonnull BoundedCharBufferArrayImpl parent, @Nonnull CharBuffer buffer) {
super(buffer, parent.characteristics, false);
this.parent = parent;
this.modCount = parent.modCount;
}
@Override
public void setClearReservedElements(boolean flag) {
parent.setClearReservedElements(flag); // super.setClearReservedElements() must not be called.
}
@Override
protected boolean clearReservedElements() {
return parent.clearReservedElements();
}
@Override
protected void checkForComodification() {
if (modCount != parent.modCount) {
throw new ConcurrentModificationException();
}
parent.checkForComodification();
}
@Override
protected ArrayContext<Character> getArrayContext() {
return parent.inAction ? null : super.getArrayContext();
}
@Override
protected void updateLength(int diff) {
super.updateLength(diff);
parent.updateLength(diff);
}
@Override
protected void updateCharacteristics(int elementIndex, char value) {
super.updateCharacteristics(elementIndex, value);
if (hasCharacteristics(SORTED)) {
parent.updateCharacteristics(elementIndex, value);
}
}
@Override
protected void updateCharacteristics(int elementIndex, @Nonnull Array<? extends Character> array) {
super.updateCharacteristics(elementIndex, array);
if (hasCharacteristics(SORTED)) {
parent.updateCharacteristics(elementIndex, array);
}
}
@Override
public int characteristics(int characteristics) {
// Parent array retains characteristics that are shared with sub array. Additional characteristics
// assigned to sub array are not set for parent array, as it is not guaranteed that these
// characteristics hold for parent array.
parent.retainCharacteristics(characteristics);
return super.characteristics(characteristics);
}
@Override
public ArrayIterator.OfChar iterator() {
checkForComodification();
return super.iterator();
}
@Override
public ArrayIterator.OfChar iterator(int index) {
checkForComodification();
return super.iterator(index);
}
@Override
public void forEachChar(@Nonnull CharConsumer consumer) {
checkForComodification();
super.forEachChar(consumer);
}
@Override
public char getChar(int index) {
checkForComodification();
return super.getChar(index);
}
@Override
public char setChar(int index, char value) {
checkForComodification();
return super.setChar(index, value);
}
@Override
public void setAll(@Nonnull Array<? extends Character> array) {
checkForComodification();
super.setAll(array);
}
@Override
public void addChar(char value) {
checkForComodification();
super.addChar(value);
}
@Override
public void addAll(@Nonnull Array<? extends Character> array) {
checkForComodification();
super.addAll(array);
}
@Override
public char charRemove() {
checkForComodification();
return super.charRemove();
}
@Override
public void clear() {
checkForComodification();
super.clear();
}
@Override
public char[] toArray() {
checkForComodification();
return super.toArray();
}
@Override
public char[] toArray(@Nonnull char[] array) {
checkForComodification();
return super.toArray(array);
}
@Override
public void toArray(@Nonnull char[] array, int offset, int length) {
checkForComodification();
super.toArray(array, offset, length);
}
// All default implementations (defender methods) will be checked for concurrent modification either through
// methods listed above, or through backing buffer access below.
@Override
public CharBuffer buffer() {
checkForComodification();
return super.buffer();
}
}
/**
* <p>Returns a new sub array object for the specified {@code offset}. The specified {@code offset} is relative to
* the existing offset, as returned by {@link #offset()}. The length of the resulting array is reduced by
* the number of elements that are added to the current offset:</p>
*
* <pre>int newLength = length() - offset;</pre>
*
* <p>Effectively, the virtual {@code toIndex} of the array remains the same.</p>
*
* @param offset new relative offset.
* @return a new sub array object with the new {@code offset}.
*/
@Override
public BoundedCharArray offset(int offset) {
int newOffset = elements.position() + offset;
int newLength = elements.remaining() - offset;
subArrayCheck(newOffset, newLength);
CharBuffer copy = elements.duplicate();
copy.position(newOffset);
return new SubBoundedCharBufferArrayImpl(this, copy);
}
/**
* <p>Returns a new sub array object for the specified {@code length}. Note that {@code length} must be smaller than
* or equal to the current length, as returned by {@code #length}.</p>
*
* @param length new length, must be smaller than or equal to current length.
* @return a new sub array object with the new {@code length}.
*/
@Override
public MutableCharArray length(int length) {
int offset = elements.position();
subArrayCheck(offset, length);
CharBuffer copy = elements.duplicate();
copy.limit(offset + length);
return new SubBoundedCharBufferArrayImpl(this, copy);
}
/**
* Convenience method, combining {@code offset(int)} and {@code length(int)}.
*
* @param fromIndex low endpoint (inclusive) of the sub array.
* @param toIndex high endpoint (exclusive) of the sub array.
* @return a view of the specified range within this array.
* @throws IndexOutOfBoundsException for an illegal endpoint index value ({@code fromIndex < 0 || toIndex > length ||
* fromIndex > toIndex}).
*/
@Override
public MutableCharArray subArray(int fromIndex, int toIndex) {
int offset = elements.position();
int newOffset = offset + fromIndex;
int newLength = toIndex - fromIndex;
subArrayCheck(newOffset, newLength);
CharBuffer copy = elements.duplicate();
copy.position(newOffset);
copy.limit(newOffset + newLength);
return new SubBoundedCharBufferArrayImpl(this, copy);
}
/**
* Tells whether or not the array's content is resident in physical memory.
*
* <p>A return value of {@code true} implies that it is highly likely that all of the data in the array is
* resident in physical memory and may therefore be accessed without incurring any virtual-memory page
* faults or I/O operations. A return value of {@code false} does not necessarily imply that the array's content
* is not resident in physical memory.</p>
*
* <p>The returned value is a hint, rather than a guarantee, because the underlying operating system may have paged
* out some of the array's data by the time that an invocation of this method returns.</p>
*
* <p>If this array is not constructed through {@link #newInstance(java.nio.file.Path, int)} then this method always
* returns {@code true}.</p>
*
* @return {@code true} if it is likely that the array's content is resident in physical memory.
*/
@Override
public boolean isLoaded() {
return buffer == null || buffer.isLoaded();
}
/**
* Loads the array's content into physical memory.
*
* <p>This method makes a best effort to ensure that, when it returns, the array's content is resident in physical
* memory. Invoking this method may cause some number of page faults and I/O operations to occur.</p>
*
* <p>If this array is not constructed through {@link #newInstance(java.nio.file.Path, int)} then then invoking this
* method has no effect.</p>
*/
@Override
public void load() {
if (buffer != null) {
buffer.load();
}
}
/**
* Forces any changes made to the array's content to be written to the storage device containing the mapped file.
*
* <p>If the file mapped into this array resides on a local storage device then when this method returns it is
* guaranteed that all changes made to the array since it was created, or since this method was last invoked, will
* have been written to that device.</p>
*
* <p>If the file does not reside on a local device then no such guarantee is made.</p>
*
* <p>If this array is not constructed through {@link #newInstance(java.nio.file.Path, int)} then then invoking this
* method has no effect.</p>
*/
@Override
public void force() {
if (buffer != null) {
buffer.force();
}
}
/**
* Constructs a mapped byte buffer for the specified capacity.
*
* @param file non existing or zero-length file to which memory must be mapped.
* @param capacity capacity of array.
* @param newFile true if mapped buffer represents a new file.
* @return new mapped byte buffer.
* @throws UncheckedIOException indicates an error with underlying file system, or {@code newFile} is true and
* file already exists.
*/
private static MappedByteBuffer createMappedBuffer(@Nonnull File file, int capacity, boolean newFile) {
try {
if (newFile && file.exists() && file.length() > 0) {
throw new FileAlreadyExistsException(file.getPath());
}
return new RandomAccessFile(file, "rw").getChannel().
map(FileChannel.MapMode.READ_WRITE, 0, Character.SIZE / Byte.SIZE * capacity);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* Write replace method for the serialization proxy pattern. This method must be overwritten by sub classes if their
* serialized form differs from this class.
*
* @return serialization proxy.
*/
@Override
Object writeReplace() {
return new SerializationProxy(this);
}
@SuppressWarnings("MissingMethodJavaDoc")
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
@SuppressWarnings({"MissingMethodJavaDoc", "MissingFieldJavaDoc"})
private static class SerializationProxy implements Serializable {
static final long serialVersionUID = -2929127243624957465L;
final File file;
final int length;
final int capacity;
final boolean direct;
final int characteristics;
final boolean clearReservedElements;
transient CharBuffer elements;
transient MappedByteBuffer buffer;
SerializationProxy(BoundedCharBufferArrayImpl array) {
this.file = array.file;
this.length = array.elements.remaining();
this.capacity = array.elements.capacity() - array.elements.position();
this.direct = array.elements.isDirect();
this.characteristics = array.characteristics;
this.clearReservedElements = array.clearReservedElements();
this.elements = array.elements;
this.buffer = array.buffer;
}
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
if (buffer == null) {
elements.mark();
for (int i = 0; i < length; i++) {
s.writeChar(elements.get());
}
elements.reset();
} else {
buffer.force();
}
}
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
if (file != null) {
buffer = createMappedBuffer(file, capacity, false);
} else {
if (direct) {
elements = ByteBuffer.allocateDirect(Character.SIZE / Byte.SIZE * capacity).asCharBuffer();
} else {
elements = CharBuffer.allocate(capacity);
}
elements.limit(length);
for (int i = 0; i < length; i++) {
elements.put(s.readChar());
}
elements.rewind();
}
}
private Object readResolve() {
BoundedCharBufferArrayImpl array = file == null
? new BoundedCharBufferArrayImpl(elements, characteristics, false)
: new BoundedCharBufferArrayImpl(file, buffer, length);
array.setClearReservedElements(clearReservedElements);
return array;
}
}
}