/*
* %W% %E%
*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.nio;
/**
* A container for data of a specific primitive type.
*
* <p>
* A buffer is a linear, finite sequence of elements of a specific primitive
* type. Aside from its content, the essential properties of a buffer are its
* capacity, limit, and position:
* </p>
*
* <blockquote>
*
* <p>
* A buffer's <i>capacity</i> is the number of elements it contains. The
* capacity of a buffer is never negative and never changes.
* </p>
*
* <p>
* A buffer's <i>limit</i> is the index of the first element that should not be
* read or written. A buffer's limit is never negative and is never greater than
* its capacity.
* </p>
*
* <p>
* A buffer's <i>position</i> is the index of the next element to be read or
* written. A buffer's position is never negative and is never greater than its
* limit.
* </p>
*
* </blockquote>
*
* <p>
* There is one subclass of this class for each non-boolean primitive type.
*
*
* <h4>Transferring data</h4>
*
* <p>
* Each subclass of this class defines two categories of <i>get</i> and
* <i>put</i> operations:
* </p>
*
* <blockquote>
*
* <p>
* <i>Relative</i> operations read or write one or more elements starting at the
* current position and then increment the position by the number of elements
* transferred. If the requested transfer exceeds the limit then a relative
* <i>get</i> operation throws a {@link BufferUnderflowException} and a relative
* <i>put</i> operation throws a {@link BufferOverflowException}; in either
* case, no data is transferred.
* </p>
*
* <p>
* <i>Absolute</i> operations take an explicit element index and do not affect
* the position. Absolute <i>get</i> and <i>put</i> operations throw an
* {@link IndexOutOfBoundsException} if the index argument exceeds the limit.
* </p>
*
* </blockquote>
*
* <p>
* Data may also, of course, be transferred in to or out of a buffer by the I/O
* operations of an appropriate channel, which are always relative to the
* current position.
*
*
* <h4>Marking and resetting</h4>
*
* <p>
* A buffer's <i>mark</i> is the index to which its position will be reset when
* the {@link #reset reset} method is invoked. The mark is not always defined,
* but when it is defined it is never negative and is never greater than the
* position. If the mark is defined then it is discarded when the position or
* the limit is adjusted to a value smaller than the mark. If the mark is not
* defined then invoking the {@link #reset reset} method causes an
* {@link InvalidMarkException} to be thrown.
*
*
* <h4>Invariants</h4>
*
* <p>
* The following invariant holds for the mark, position, limit, and capacity
* values:
*
* <blockquote> <tt>0</tt> <tt><=</tt> <i>mark</i> <tt><=</tt>
* <i>position</i> <tt><=</tt> <i>limit</i> <tt><=</tt> <i>capacity</i>
* </blockquote>
*
* <p>
* A newly-created buffer always has a position of zero and a mark that is
* undefined. The initial limit may be zero, or it may be some other value that
* depends upon the type of the buffer and the manner in which it is
* constructed. The initial content of a buffer is, in general, undefined.
*
*
* <h4>Clearing, flipping, and rewinding</h4>
*
* <p>
* In addition to methods for accessing the position, limit, and capacity values
* and for marking and resetting, this class also defines the following
* operations upon buffers:
*
* <ul>
*
* <li>
* <p>
* {@link #clear} makes a buffer ready for a new sequence of channel-read or
* relative <i>put</i> operations: It sets the limit to the capacity and the
* position to zero.
* </p>
* </li>
*
* <li>
* <p>
* {@link #flip} makes a buffer ready for a new sequence of channel-write or
* relative <i>get</i> operations: It sets the limit to the current position and
* then sets the position to zero.
* </p>
* </li>
*
* <li>
* <p>
* {@link #rewind} makes a buffer ready for re-reading the data that it already
* contains: It leaves the limit unchanged and sets the position to zero.
* </p>
* </li>
*
* </ul>
*
*
* <h4>Read-only buffers</h4>
*
* <p>
* Every buffer is readable, but not every buffer is writable. The mutation
* methods of each buffer class are specified as <i>optional operations</i> that
* will throw a {@link ReadOnlyBufferException} when invoked upon a read-only
* buffer. A read-only buffer does not allow its content to be changed, but its
* mark, position, and limit values are mutable. Whether or not a buffer is
* read-only may be determined by invoking its {@link #isReadOnly isReadOnly}
* method.
*
*
* <h4>Thread safety</h4>
*
* <p>
* Buffers are not safe for use by multiple concurrent threads. If a buffer is
* to be used by more than one thread then access to the buffer should be
* controlled by appropriate synchronization.
*
*
* <h4>Invocation chaining</h4>
*
* <p>
* Methods in this class that do not otherwise have a value to return are
* specified to return the buffer upon which they are invoked. This allows
* method invocations to be chained; for example, the sequence of statements
*
* <blockquote>
*
* <pre>
* b.flip();
* b.position(23);
* b.limit(42);
* </pre>
*
* </blockquote>
*
* can be replaced by the single, more compact statement
*
* <blockquote>
*
* <pre>
* b.flip().position(23).limit(42);
* </pre>
*
* </blockquote>
*
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @version %I%, %E%
* @since 1.4
*/
public abstract class Buffer {
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
// Used only by direct buffers
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;
// Creates a new buffer with the given mark, position, limit, and capacity,
// after checking invariants.
//
Buffer(int mark, int pos, int lim, int cap) { // package-private
if (cap < 0)
throw new IllegalArgumentException();
this.capacity = cap;
limit(lim);
position(pos);
if (mark >= 0) {
if (mark > pos)
throw new IllegalArgumentException();
this.mark = mark;
}
}
/**
* Returns this buffer's capacity. </p>
*
* @return The capacity of this buffer
*/
public final int capacity() {
return capacity;
}
/**
* Returns this buffer's position. </p>
*
* @return The position of this buffer
*/
public final int position() {
return position;
}
/**
* Sets this buffer's position. If the mark is defined and larger than the
* new position then it is discarded. </p>
*
* @param newPosition
* The new position value; must be non-negative and no larger
* than the current limit
*
* @return This buffer
*
* @throws IllegalArgumentException
* If the preconditions on <tt>newPosition</tt> do not hold
*/
public final Buffer position(int newPosition) {
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
if (mark > position)
mark = -1;
return this;
}
/**
* Returns this buffer's limit. </p>
*
* @return The limit of this buffer
*/
public final int limit() {
return limit;
}
/**
* Sets this buffer's limit. If the position is larger than the new limit
* then it is set to the new limit. If the mark is defined and larger than
* the new limit then it is discarded. </p>
*
* @param newLimit
* The new limit value; must be non-negative and no larger than
* this buffer's capacity
*
* @return This buffer
*
* @throws IllegalArgumentException
* If the preconditions on <tt>newLimit</tt> do not hold
*/
public final Buffer limit(int newLimit) {
if ((newLimit > capacity) || (newLimit < 0))
throw new IllegalArgumentException();
limit = newLimit;
if (position > limit)
position = limit;
if (mark > limit)
mark = -1;
return this;
}
/**
* Sets this buffer's mark at its position. </p>
*
* @return This buffer
*/
public final Buffer mark() {
mark = position;
return this;
}
/**
* Resets this buffer's position to the previously-marked position.
*
* <p>
* Invoking this method neither changes nor discards the mark's value.
* </p>
*
* @return This buffer
*
* @throws InvalidMarkException
* If the mark has not been set
*/
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
/**
* Clears this buffer. The position is set to zero, the limit is set to the
* capacity, and the mark is discarded.
*
* <p>
* Invoke this method before using a sequence of channel-read or <i>put</i>
* operations to fill this buffer. For example:
*
* <blockquote>
*
* <pre>
* buf.clear(); // Prepare buffer for reading
* in.read(buf); // Read data
* </pre>
*
* </blockquote>
*
* <p>
* This method does not actually erase the data in the buffer, but it is
* named as if it did because it will most often be used in situations in
* which that might as well be the case.
* </p>
*
* @return This buffer
*/
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
/**
* Flips this buffer. The limit is set to the current position and then the
* position is set to zero. If the mark is defined then it is discarded.
*
* <p>
* After a sequence of channel-read or <i>put</i> operations, invoke this
* method to prepare for a sequence of channel-write or relative <i>get</i>
* operations. For example:
*
* <blockquote>
*
* <pre>
* buf.put(magic); // Prepend header
* in.read(buf); // Read data into rest of buffer
* buf.flip(); // Flip buffer
* out.write(buf); // Write header + data to channel
* </pre>
*
* </blockquote>
*
* <p>
* This method is often used in conjunction with the
* {@link java.nio.ByteBuffer#compact compact} method when transferring data
* from one place to another.
* </p>
*
* @return This buffer
*/
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
/**
* Rewinds this buffer. The position is set to zero and the mark is
* discarded.
*
* <p>
* Invoke this method before a sequence of channel-write or <i>get</i>
* operations, assuming that the limit has already been set appropriately.
* For example:
*
* <blockquote>
*
* <pre>
* out.write(buf); // Write remaining data
* buf.rewind(); // Rewind buffer
* buf.get(array); // Copy data into array
* </pre>
*
* </blockquote>
*
* @return This buffer
*/
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
/**
* Returns the number of elements between the current position and the
* limit. </p>
*
* @return The number of elements remaining in this buffer
*/
public final int remaining() {
return limit - position;
}
/**
* Tells whether there are any elements between the current position and the
* limit. </p>
*
* @return <tt>true</tt> if, and only if, there is at least one element
* remaining in this buffer
*/
public final boolean hasRemaining() {
return position < limit;
}
/**
* Tells whether or not this buffer is read-only. </p>
*
* @return <tt>true</tt> if, and only if, this buffer is read-only
*/
public abstract boolean isReadOnly();
/**
* Tells whether or not this buffer is backed by an accessible array.
*
* <p>
* If this method returns <tt>true</tt> then the {@link #array() array} and
* {@link #arrayOffset() arrayOffset} methods may safely be invoked.
* </p>
*
* @return <tt>true</tt> if, and only if, this buffer is backed by an array
* and is not read-only
*
* @since 1.6
*/
public abstract boolean hasArray();
/**
* Returns the array that backs this buffer <i>(optional
* operation)</i>.
*
* <p>
* This method is intended to allow array-backed buffers to be passed to
* native code more efficiently. Concrete subclasses provide more
* strongly-typed return values for this method.
*
* <p>
* Modifications to this buffer's content will cause the returned array's
* content to be modified, and vice versa.
*
* <p>
* Invoke the {@link #hasArray hasArray} method before invoking this method
* in order to ensure that this buffer has an accessible backing array.
* </p>
*
* @return The array that backs this buffer
*
* @throws ReadOnlyBufferException
* If this buffer is backed by an array but is read-only
*
* @throws UnsupportedOperationException
* If this buffer is not backed by an accessible array
*
* @since 1.6
*/
public abstract Object array();
/**
* Returns the offset within this buffer's backing array of the first
* element of the buffer <i>(optional operation)</i>.
*
* <p>
* If this buffer is backed by an array then buffer position <i>p</i>
* corresponds to array index <i>p</i> + <tt>arrayOffset()</tt>.
*
* <p>
* Invoke the {@link #hasArray hasArray} method before invoking this method
* in order to ensure that this buffer has an accessible backing array.
* </p>
*
* @return The offset within this buffer's array of the first element of the
* buffer
*
* @throws ReadOnlyBufferException
* If this buffer is backed by an array but is read-only
*
* @throws UnsupportedOperationException
* If this buffer is not backed by an accessible array
*
* @since 1.6
*/
public abstract int arrayOffset();
/**
* Tells whether or not this buffer is <a
* href="ByteBuffer.html#direct"><i>direct</i></a>. </p>
*
* @return <tt>true</tt> if, and only if, this buffer is direct
*
* @since 1.6
*/
public abstract boolean isDirect();
// -- Package-private methods for bounds checking, etc. --
/**
* Checks the current position against the limit, throwing a
* {@link BufferUnderflowException} if it is not smaller than the limit, and
* then increments the position. </p>
*
* @return The current position value, before it is incremented
*/
final int nextGetIndex() { // package-private
if (position >= limit)
throw new BufferUnderflowException();
return position++;
}
final int nextGetIndex(int nb) { // package-private
if (limit - position < nb)
throw new BufferUnderflowException();
int p = position;
position += nb;
return p;
}
/**
* Checks the current position against the limit, throwing a
* {@link BufferOverflowException} if it is not smaller than the limit, and
* then increments the position. </p>
*
* @return The current position value, before it is incremented
*/
final int nextPutIndex() { // package-private
if (position >= limit)
throw new BufferOverflowException();
return position++;
}
final int nextPutIndex(int nb) { // package-private
if (limit - position < nb)
throw new BufferOverflowException();
int p = position;
position += nb;
return p;
}
/**
* Checks the given index against the limit, throwing an
* {@link IndexOutOfBoundsException} if it is not smaller than the limit or
* is smaller than zero.
*/
final int checkIndex(int i) { // package-private
if ((i < 0) || (i >= limit))
throw new IndexOutOfBoundsException();
return i;
}
final int checkIndex(int i, int nb) { // package-private
if ((i < 0) || (nb > limit - i))
throw new IndexOutOfBoundsException();
return i;
}
final int markValue() { // package-private
return mark;
}
static void checkBounds(int off, int len, int size) { // package-private
if ((off | len | (off + len) | (size - (off + len))) < 0)
throw new IndexOutOfBoundsException();
}
}