/* |
|
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code 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 General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package java.nio; |
|
import jdk.internal.access.JavaNioAccess; |
|
import jdk.internal.access.SharedSecrets; |
|
import jdk.internal.access.foreign.MemorySegmentProxy; |
|
import jdk.internal.access.foreign.UnmapperProxy; |
|
import jdk.internal.misc.ScopedMemoryAccess; |
|
import jdk.internal.misc.ScopedMemoryAccess.Scope; |
|
import jdk.internal.misc.Unsafe; |
|
import jdk.internal.misc.VM.BufferPool; |
|
import jdk.internal.vm.annotation.ForceInline; |
|
import jdk.internal.vm.annotation.IntrinsicCandidate; |
|
import java.io.FileDescriptor; |
|
import java.util.Spliterator; |
|
/** |
|
* 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. |
|
* |
|
* |
|
* <h2> Transferring data </h2> |
|
* |
|
* <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. |
|
* |
|
* |
|
* <h2> Marking and resetting </h2> |
|
* |
|
* <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. |
|
* |
|
* |
|
* <h2> Invariants </h2> |
|
* |
|
* <p> The following invariant holds for the mark, position, limit, and |
|
* capacity values: |
|
* |
|
* <blockquote> |
|
* {@code 0} {@code <=} |
|
* <i>mark</i> {@code <=} |
|
* <i>position</i> {@code <=} |
|
* <i>limit</i> {@code <=} |
|
* <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. Each element of a newly-allocated buffer is initialized |
|
* to zero. |
|
* |
|
* |
|
* <h2> Additional operations </h2> |
|
* |
|
* <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> |
|
* |
|
* <li><p> The {@link #slice} and {@link #slice(int,int) slice(index,length)} |
|
* methods create a subsequence of a buffer: They leave the limit and the |
|
* position unchanged. </p></li> |
|
* |
|
* <li><p> {@link #duplicate} creates a shallow copy of a buffer: It leaves |
|
* the limit and the position unchanged. </p></li> |
|
* |
|
* </ul> |
|
* |
|
* |
|
* <h2> Read-only buffers </h2> |
|
* |
|
* <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. |
|
* |
|
* |
|
* <h2> Thread safety </h2> |
|
* |
|
* <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. |
|
* |
|
* |
|
* <h2> Invocation chaining </h2> |
|
* |
|
* <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 |
|
* @since 1.4 |
|
*/ |
|
public abstract class Buffer { |
|
// Cached unsafe-access object |
|
static final Unsafe UNSAFE = Unsafe.getUnsafe(); |
|
static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); |
|
/** |
|
* The characteristics of Spliterators that traverse and split elements |
|
* maintained in Buffers. |
|
*/ |
|
static final int SPLITERATOR_CHARACTERISTICS = |
|
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED; |
|
// Invariants: mark <= position <= limit <= capacity |
|
private int mark = -1; |
|
private int position = 0; |
|
private int limit; |
|
private int capacity; |
|
// Used by heap byte buffers or direct buffers with Unsafe access |
|
// For heap byte buffers this field will be the address relative to the |
|
// array base address and offset into that array. The address might |
|
// not align on a word boundary for slices, nor align at a long word |
|
// (8 byte) boundary for byte[] allocations on 32-bit systems. |
|
// For direct buffers it is the start address of the memory region. The |
|
// address might not align on a word boundary for slices, nor when created |
|
// using JNI, see NewDirectByteBuffer(void*, long). |
|
// Should ideally be declared final |
|
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress |
|
long address; |
|
// Used by buffers generated by the memory access API (JEP-370) |
|
final MemorySegmentProxy segment; |
|
// Creates a new buffer with given address and capacity. |
|
// |
|
Buffer(long addr, int cap, MemorySegmentProxy segment) { |
|
this.address = addr; |
|
this.capacity = cap; |
|
this.segment = segment; |
|
} |
|
// Creates a new buffer with the given mark, position, limit, and capacity, |
|
// after checking invariants. |
|
// |
|
Buffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private |
|
if (cap < 0) |
|
throw createCapacityException(cap); |
|
this.capacity = cap; |
|
this.segment = segment; |
|
limit(lim); |
|
position(pos); |
|
if (mark >= 0) { |
|
if (mark > pos) |
|
throw new IllegalArgumentException("mark > position: (" |
|
+ mark + " > " + pos + ")"); |
|
this.mark = mark; |
|
} |
|
} |
|
/** |
|
* Returns an {@code IllegalArgumentException} indicating that the source |
|
* and target are the same {@code Buffer}. Intended for use in |
|
* {@code put(src)} when the parameter is the {@code Buffer} on which the |
|
* method is being invoked. |
|
* |
|
* @return IllegalArgumentException |
|
* With a message indicating equal source and target buffers |
|
*/ |
|
static IllegalArgumentException createSameBufferException() { |
|
return new IllegalArgumentException("The source buffer is this buffer"); |
|
} |
|
/** |
|
* Verify that the capacity is nonnegative. |
|
* |
|
* @param capacity |
|
* The new buffer's capacity, in $type$s |
|
* |
|
* @throws IllegalArgumentException |
|
* If the {@code capacity} is a negative integer |
|
*/ |
|
static IllegalArgumentException createCapacityException(int capacity) { |
|
assert capacity < 0 : "capacity expected to be negative"; |
|
return new IllegalArgumentException("capacity < 0: (" |
|
+ capacity + " < 0)"); |
|
} |
|
/** |
|
* Returns this buffer's capacity. |
|
* |
|
* @return The capacity of this buffer |
|
*/ |
|
public final int capacity() { |
|
return capacity; |
|
} |
|
/** |
|
* Returns this buffer's position. |
|
* |
|
* @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. |
|
* |
|
* @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 {@code newPosition} do not hold |
|
*/ |
|
public Buffer position(int newPosition) { |
|
if (newPosition > limit | newPosition < 0) |
|
throw createPositionException(newPosition); |
|
if (mark > newPosition) mark = -1; |
|
position = newPosition; |
|
return this; |
|
} |
|
/** |
|
* Verify that {@code 0 < newPosition <= limit} |
|
* |
|
* @param newPosition |
|
* The new position value |
|
* |
|
* @throws IllegalArgumentException |
|
* If the specified position is out of bounds. |
|
*/ |
|
private IllegalArgumentException createPositionException(int newPosition) { |
|
String msg = null; |
|
if (newPosition > limit) { |
|
msg = "newPosition > limit: (" + newPosition + " > " + limit + ")"; |
|
} else { // assume negative |
|
assert newPosition < 0 : "newPosition expected to be negative"; |
|
msg = "newPosition < 0: (" + newPosition + " < 0)"; |
|
} |
|
return new IllegalArgumentException(msg); |
|
} |
|
/** |
|
* Returns this buffer's limit. |
|
* |
|
* @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. |
|
* |
|
* @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 {@code newLimit} do not hold |
|
*/ |
|
public Buffer limit(int newLimit) { |
|
if (newLimit > capacity | newLimit < 0) |
|
throw createLimitException(newLimit); |
|
limit = newLimit; |
|
if (position > newLimit) position = newLimit; |
|
if (mark > newLimit) mark = -1; |
|
return this; |
|
} |
|
/** |
|
* Verify that {@code 0 < newLimit <= capacity} |
|
* |
|
* @param newLimit |
|
* The new limit value |
|
* |
|
* @throws IllegalArgumentException |
|
* If the specified limit is out of bounds. |
|
*/ |
|
private IllegalArgumentException createLimitException(int newLimit) { |
|
String msg = null; |
|
if (newLimit > capacity) { |
|
msg = "newLimit > capacity: (" + newLimit + " > " + capacity + ")"; |
|
} else { // assume negative |
|
assert newLimit < 0 : "newLimit expected to be negative"; |
|
msg = "newLimit < 0: (" + newLimit + " < 0)"; |
|
} |
|
return new IllegalArgumentException(msg); |
|
} |
|
/** |
|
* Sets this buffer's mark at its position. |
|
* |
|
* @return This buffer |
|
*/ |
|
public 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 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 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 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 Buffer rewind() { |
|
position = 0; |
|
mark = -1; |
|
return this; |
|
} |
|
/** |
|
* Returns the number of elements between the current position and the |
|
* limit. |
|
* |
|
* @return The number of elements remaining in this buffer |
|
*/ |
|
public final int remaining() { |
|
int rem = limit - position; |
|
return rem > 0 ? rem : 0; |
|
} |
|
/** |
|
* Tells whether there are any elements between the current position and |
|
* the limit. |
|
* |
|
* @return {@code true} 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. |
|
* |
|
* @return {@code true} 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 {@code true} then the {@link #array() array} |
|
* and {@link #arrayOffset() arrayOffset} methods may safely be invoked. |
|
* </p> |
|
* |
|
* @return {@code true} 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> + {@code arrayOffset()}. |
|
* |
|
* <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>. |
|
* |
|
* @return {@code true} if, and only if, this buffer is direct |
|
* |
|
* @since 1.6 |
|
*/ |
|
public abstract boolean isDirect(); |
|
/** |
|
* Creates a new buffer whose content is a shared subsequence of |
|
* this buffer's content. |
|
* |
|
* <p> The content of the new buffer will start at this buffer's current |
|
* position. Changes to this buffer's content will be visible in the new |
|
* buffer, and vice versa; the two buffers' position, limit, and mark |
|
* values will be independent. |
|
* |
|
* <p> The new buffer's position will be zero, its capacity and its limit |
|
* will be the number of elements remaining in this buffer, its mark will be |
|
* undefined. The new buffer will be direct if, and only if, this buffer is |
|
* direct, and it will be read-only if, and only if, this buffer is |
|
* read-only. </p> |
|
* |
|
* @return The new buffer |
|
* |
|
* @since 9 |
|
*/ |
|
public abstract Buffer slice(); |
|
/** |
|
* Creates a new buffer whose content is a shared subsequence of |
|
* this buffer's content. |
|
* |
|
* <p> The content of the new buffer will start at position {@code index} |
|
* in this buffer, and will contain {@code length} elements. Changes to |
|
* this buffer's content will be visible in the new buffer, and vice versa; |
|
* the two buffers' position, limit, and mark values will be independent. |
|
* |
|
* <p> The new buffer's position will be zero, its capacity and its limit |
|
* will be {@code length}, its mark will be undefined. The new buffer will |
|
* be direct if, and only if, this buffer is direct, and it will be |
|
* read-only if, and only if, this buffer is read-only. </p> |
|
* |
|
* @param index |
|
* The position in this buffer at which the content of the new |
|
* buffer will start; must be non-negative and no larger than |
|
* {@link #limit() limit()} |
|
* |
|
* @param length |
|
* The number of elements the new buffer will contain; must be |
|
* non-negative and no larger than {@code limit() - index} |
|
* |
|
* @return The new buffer |
|
* |
|
* @throws IndexOutOfBoundsException |
|
* If {@code index} is negative or greater than {@code limit()}, |
|
* {@code length} is negative, or {@code length > limit() - index} |
|
* |
|
* @since 13 |
|
*/ |
|
public abstract Buffer slice(int index, int length); |
|
/** |
|
* Creates a new buffer that shares this buffer's content. |
|
* |
|
* <p> The content of the new buffer will be that of this buffer. Changes |
|
* to this buffer's content will be visible in the new buffer, and vice |
|
* versa; the two buffers' position, limit, and mark values will be |
|
* independent. |
|
* |
|
* <p> The new buffer's capacity, limit, position and mark values will be |
|
* identical to those of this buffer. The new buffer will be direct if, and |
|
* only if, this buffer is direct, and it will be read-only if, and only if, |
|
* this buffer is read-only. </p> |
|
* |
|
* @return The new buffer |
|
* |
|
* @since 9 |
|
*/ |
|
public abstract Buffer duplicate(); |
|
// -- Package-private methods for bounds checking, etc. -- |
|
/** |
|
* |
|
* @return the base reference, paired with the address |
|
* field, which in combination can be used for unsafe access into a heap |
|
* buffer or direct byte buffer (and views of). |
|
*/ |
|
abstract Object base(); |
|
/** |
|
* Checks the current position against the limit, throwing a {@link |
|
* BufferUnderflowException} if it is not smaller than the limit, and then |
|
* increments the position. |
|
* |
|
* @return The current position value, before it is incremented |
|
*/ |
|
final int nextGetIndex() { // package-private |
|
int p = position; |
|
if (p >= limit) |
|
throw new BufferUnderflowException(); |
|
position = p + 1; |
|
return p; |
|
} |
|
final int nextGetIndex(int nb) { // package-private |
|
int p = position; |
|
if (limit - p < nb) |
|
throw new BufferUnderflowException(); |
|
position = p + 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. |
|
* |
|
* @return The current position value, before it is incremented |
|
*/ |
|
final int nextPutIndex() { // package-private |
|
int p = position; |
|
if (p >= limit) |
|
throw new BufferOverflowException(); |
|
position = p + 1; |
|
return p; |
|
} |
|
final int nextPutIndex(int nb) { // package-private |
|
int p = position; |
|
if (limit - p < nb) |
|
throw new BufferOverflowException(); |
|
position = p + 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. |
|
*/ |
|
@IntrinsicCandidate |
|
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; |
|
} |
|
final void discardMark() { // package-private |
|
mark = -1; |
|
} |
|
@ForceInline |
|
final ScopedMemoryAccess.Scope scope() { |
|
if (segment != null) { |
|
return segment.scope(); |
|
} else { |
|
return null; |
|
} |
|
} |
|
final void checkScope() { |
|
ScopedMemoryAccess.Scope scope = scope(); |
|
if (scope != null) { |
|
scope.checkValidState(); |
|
} |
|
} |
|
static { |
|
// setup access to this package in SharedSecrets |
|
SharedSecrets.setJavaNioAccess( |
|
new JavaNioAccess() { |
|
@Override |
|
public BufferPool getDirectBufferPool() { |
|
return Bits.BUFFER_POOL; |
|
} |
|
@Override |
|
public ByteBuffer newDirectByteBuffer(long addr, int cap, Object obj, MemorySegmentProxy segment) { |
|
return new DirectByteBuffer(addr, cap, obj, segment); |
|
} |
|
@Override |
|
public ByteBuffer newMappedByteBuffer(UnmapperProxy unmapperProxy, long address, int cap, Object obj, MemorySegmentProxy segment) { |
|
return new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment); |
|
} |
|
@Override |
|
public ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment) { |
|
return new HeapByteBuffer(hb, -1, 0, capacity, capacity, offset, segment); |
|
} |
|
@Override |
|
public Object getBufferBase(ByteBuffer bb) { |
|
return bb.base(); |
|
} |
|
@Override |
|
public long getBufferAddress(ByteBuffer bb) { |
|
return bb.address; |
|
} |
|
@Override |
|
public UnmapperProxy unmapper(ByteBuffer bb) { |
|
if (bb instanceof MappedByteBuffer) { |
|
return ((MappedByteBuffer)bb).unmapper(); |
|
} else { |
|
return null; |
|
} |
|
} |
|
@Override |
|
public MemorySegmentProxy bufferSegment(Buffer buffer) { |
|
return buffer.segment; |
|
} |
|
@Override |
|
public Scope.Handle acquireScope(Buffer buffer, boolean async) { |
|
var scope = buffer.scope(); |
|
if (scope == null) { |
|
return null; |
|
} |
|
if (async && scope.ownerThread() != null) { |
|
throw new IllegalStateException("Confined scope not supported"); |
|
} |
|
return scope.acquire(); |
|
} |
|
@Override |
|
public void force(FileDescriptor fd, long address, boolean isSync, long offset, long size) { |
|
MappedMemoryUtils.force(fd, address, isSync, offset, size); |
|
} |
|
@Override |
|
public void load(long address, boolean isSync, long size) { |
|
MappedMemoryUtils.load(address, isSync, size); |
|
} |
|
@Override |
|
public void unload(long address, boolean isSync, long size) { |
|
MappedMemoryUtils.unload(address, isSync, size); |
|
} |
|
@Override |
|
public boolean isLoaded(long address, boolean isSync, long size) { |
|
return MappedMemoryUtils.isLoaded(address, isSync, size); |
|
} |
|
@Override |
|
public void reserveMemory(long size, long cap) { |
|
Bits.reserveMemory(size, cap); |
|
} |
|
@Override |
|
public void unreserveMemory(long size, long cap) { |
|
Bits.unreserveMemory(size, cap); |
|
} |
|
@Override |
|
public int pageSize() { |
|
return Bits.pageSize(); |
|
} |
|
}); |
|
} |
|
} |