563 lines
16 KiB
Plaintext
563 lines
16 KiB
Plaintext
/*
|
|
* Copyright (c) 2000, 2025, 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.
|
|
*/
|
|
|
|
#warn This file is preprocessed before being compiled
|
|
|
|
package java.nio;
|
|
|
|
import java.io.FileDescriptor;
|
|
import java.lang.foreign.MemorySegment;
|
|
import java.lang.ref.Reference;
|
|
import java.util.Objects;
|
|
import jdk.internal.foreign.AbstractMemorySegmentImpl;
|
|
import jdk.internal.foreign.MemorySessionImpl;
|
|
import jdk.internal.foreign.SegmentFactories;
|
|
import jdk.internal.vm.annotation.ForceInline;
|
|
import jdk.internal.misc.ScopedMemoryAccess.ScopedAccessError;
|
|
import jdk.internal.misc.VM;
|
|
import jdk.internal.ref.Cleaner;
|
|
import sun.nio.ch.DirectBuffer;
|
|
|
|
|
|
#if[rw]
|
|
sealed
|
|
#else[rw]
|
|
final
|
|
#end[rw]
|
|
class Direct$Type$Buffer$RW$$BO$
|
|
#if[rw]
|
|
extends {#if[byte]?Mapped$Type$Buffer:$Type$Buffer}
|
|
#else[rw]
|
|
extends Direct$Type$Buffer$BO$
|
|
#end[rw]
|
|
implements DirectBuffer
|
|
#if[rw]
|
|
permits Direct$Type$BufferR$BO$
|
|
#end[rw]
|
|
{
|
|
|
|
#if[rw]
|
|
// An object attached to this buffer. If this buffer is a view of another
|
|
// buffer then we use this field to keep a reference to that buffer to
|
|
// ensure that its memory isn't freed before we are done with it.
|
|
private final Object att;
|
|
|
|
public Object attachment() {
|
|
return att;
|
|
}
|
|
|
|
#if[byte]
|
|
// Cached unaligned-access capability
|
|
static final boolean UNALIGNED = Bits.unaligned();
|
|
|
|
private record Deallocator(long address, long size, int capacity) implements Runnable {
|
|
private Deallocator {
|
|
assert address != 0;
|
|
}
|
|
|
|
public void run() {
|
|
UNSAFE.freeMemory(address);
|
|
Bits.unreserveMemory(size, capacity);
|
|
}
|
|
}
|
|
|
|
private final Cleaner cleaner;
|
|
|
|
public Cleaner cleaner() { return cleaner; }
|
|
|
|
#else[byte]
|
|
|
|
public Cleaner cleaner() { return null; }
|
|
|
|
#end[byte]
|
|
|
|
#end[rw]
|
|
|
|
#if[byte]
|
|
|
|
// Primary constructor
|
|
//
|
|
Direct$Type$Buffer$RW$(int cap) { // package-private
|
|
#if[rw]
|
|
super(-1, 0, cap, cap, null);
|
|
boolean pa = VM.isDirectMemoryPageAligned();
|
|
int ps = Bits.pageSize();
|
|
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
|
|
Bits.reserveMemory(size, cap);
|
|
|
|
long base = 0;
|
|
try {
|
|
base = UNSAFE.allocateMemory(size);
|
|
} catch (OutOfMemoryError x) {
|
|
Bits.unreserveMemory(size, cap);
|
|
throw x;
|
|
}
|
|
Bits.setMemory(base, size, (byte) 0);
|
|
if (pa && (base % ps != 0)) {
|
|
// Round up to page boundary
|
|
address = base + ps - (base & (ps - 1));
|
|
} else {
|
|
address = base;
|
|
}
|
|
try {
|
|
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
|
|
} catch (Throwable t) {
|
|
// Prevent leak if the Deallocator or Cleaner fail for any reason
|
|
UNSAFE.freeMemory(base);
|
|
Bits.unreserveMemory(size, cap);
|
|
throw t;
|
|
}
|
|
att = null;
|
|
#else[rw]
|
|
super(cap);
|
|
this.isReadOnly = true;
|
|
#end[rw]
|
|
}
|
|
|
|
#if[rw]
|
|
|
|
// Invoked to construct a direct ByteBuffer referring to the block of
|
|
// memory. A given arbitrary object may also be attached to the buffer.
|
|
//
|
|
Direct$Type$Buffer(long addr, int cap, Object ob, MemorySegment segment) {
|
|
super(-1, 0, cap, cap, segment);
|
|
address = addr;
|
|
cleaner = null;
|
|
att = ob;
|
|
}
|
|
|
|
// Invoked to construct a direct ByteBuffer referring to the block of
|
|
// memory. A given arbitrary object may also be attached to the buffer.
|
|
//
|
|
Direct$Type$Buffer(long addr, int cap, Object ob, FileDescriptor fd, boolean isSync, MemorySegment segment) {
|
|
super(-1, 0, cap, cap, fd, isSync, segment);
|
|
address = addr;
|
|
cleaner = null;
|
|
att = ob;
|
|
}
|
|
|
|
// Invoked only by JNI: NewDirectByteBuffer(void*, long)
|
|
// and JavaNioAccess.newDirectByteBuffer(int).
|
|
// The long-valued capacity is restricted to int range.
|
|
//
|
|
Direct$Type$Buffer(long addr, long cap) {
|
|
super(-1, 0, checkCapacity(cap), (int)cap, null);
|
|
address = addr;
|
|
cleaner = null;
|
|
att = null;
|
|
}
|
|
|
|
// Throw an IllegalArgumentException if the capacity is not in
|
|
// the range [0, Integer.MAX_VALUE]
|
|
//
|
|
private static int checkCapacity(long capacity) {
|
|
if (capacity < 0) {
|
|
throw new IllegalArgumentException
|
|
("JNI NewDirectByteBuffer passed capacity < 0: ("
|
|
+ capacity + ")");
|
|
} else if (capacity > Integer.MAX_VALUE) {
|
|
throw new IllegalArgumentException
|
|
("JNI NewDirectByteBuffer passed capacity > Integer.MAX_VALUE: ("
|
|
+ capacity + ")");
|
|
}
|
|
return (int)capacity;
|
|
}
|
|
|
|
#end[rw]
|
|
|
|
// For memory-mapped buffers -- invoked by FileChannelImpl via reflection
|
|
//
|
|
protected Direct$Type$Buffer$RW$(int cap, long addr,
|
|
FileDescriptor fd,
|
|
Runnable unmapper,
|
|
boolean isSync, MemorySegment segment)
|
|
{
|
|
#if[rw]
|
|
super(-1, 0, cap, cap, fd, isSync, segment);
|
|
address = addr;
|
|
cleaner = Cleaner.create(this, unmapper);
|
|
att = null;
|
|
#else[rw]
|
|
super(cap, addr, fd, unmapper, isSync, segment);
|
|
this.isReadOnly = true;
|
|
#end[rw]
|
|
}
|
|
|
|
#end[byte]
|
|
|
|
// For duplicates and slices
|
|
//
|
|
Direct$Type$Buffer$RW$$BO$(DirectBuffer db, // package-private
|
|
int mark, int pos, int lim, int cap, int off,
|
|
#if[byte]
|
|
FileDescriptor fd, boolean isSync,
|
|
#end[byte]
|
|
MemorySegment segment)
|
|
{
|
|
#if[rw]
|
|
super(mark, pos, lim, cap,
|
|
#if[byte]
|
|
fd, isSync,
|
|
#end[byte]
|
|
segment);
|
|
address = ((Buffer)db).address + off;
|
|
#if[byte]
|
|
cleaner = null;
|
|
#end[byte]
|
|
Object attachment = db.attachment();
|
|
att = (attachment == null ? db : attachment);
|
|
#else[rw]
|
|
super(db, mark, pos, lim, cap, off,
|
|
#if[byte]
|
|
fd, isSync,
|
|
#end[byte]
|
|
segment);
|
|
this.isReadOnly = true;
|
|
#end[rw]
|
|
}
|
|
|
|
@Override
|
|
Object base() {
|
|
return null;
|
|
}
|
|
|
|
public {#if[byte]?Mapped$Type$Buffer:$Type$Buffer} slice() {
|
|
int pos = this.position();
|
|
int lim = this.limit();
|
|
int rem = (pos <= lim ? lim - pos : 0);
|
|
int off = (pos << $LG_BYTES_PER_VALUE$);
|
|
assert (off >= 0);
|
|
return new Direct$Type$Buffer$RW$$BO$(this,
|
|
-1,
|
|
0,
|
|
rem,
|
|
rem,
|
|
off,
|
|
#if[byte]
|
|
fileDescriptor(),
|
|
isSync(),
|
|
#end[byte]
|
|
segment);
|
|
}
|
|
|
|
@Override
|
|
public {#if[byte]?Mapped$Type$Buffer:$Type$Buffer} slice(int index, int length) {
|
|
Objects.checkFromIndexSize(index, length, limit());
|
|
return new Direct$Type$Buffer$RW$$BO$(this,
|
|
-1,
|
|
0,
|
|
length,
|
|
length,
|
|
index << $LG_BYTES_PER_VALUE$,
|
|
#if[byte]
|
|
fileDescriptor(),
|
|
isSync(),
|
|
#end[byte]
|
|
segment);
|
|
}
|
|
|
|
public {#if[byte]?Mapped$Type$Buffer:$Type$Buffer} duplicate() {
|
|
return new Direct$Type$Buffer$RW$$BO$(this,
|
|
this.markValue(),
|
|
this.position(),
|
|
this.limit(),
|
|
this.capacity(),
|
|
0,
|
|
#if[byte]
|
|
fileDescriptor(),
|
|
isSync(),
|
|
#end[byte]
|
|
segment);
|
|
}
|
|
|
|
public $Type$Buffer asReadOnlyBuffer() {
|
|
#if[rw]
|
|
return new Direct$Type$BufferR$BO$(this,
|
|
this.markValue(),
|
|
this.position(),
|
|
this.limit(),
|
|
this.capacity(),
|
|
0,
|
|
#if[byte]
|
|
fileDescriptor(),
|
|
isSync(),
|
|
#end[byte]
|
|
segment);
|
|
#else[rw]
|
|
return duplicate();
|
|
#end[rw]
|
|
}
|
|
|
|
#if[rw]
|
|
|
|
public long address() {
|
|
MemorySessionImpl session = session();
|
|
if (session != null) {
|
|
if (session.ownerThread() == null && session.isCloseable()) {
|
|
throw new UnsupportedOperationException("ByteBuffer derived from closeable shared sessions not supported");
|
|
}
|
|
session.checkValidState();
|
|
}
|
|
return address;
|
|
}
|
|
|
|
private long ix(int i) {
|
|
return address + ((long)i << $LG_BYTES_PER_VALUE$);
|
|
}
|
|
|
|
public $type$ get() {
|
|
try {
|
|
return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(session(), null, ix(nextGetIndex()))));
|
|
} finally {
|
|
Reference.reachabilityFence(this);
|
|
}
|
|
}
|
|
|
|
public $type$ get(int i) {
|
|
try {
|
|
return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(session(), null, ix(checkIndex(i)))));
|
|
} finally {
|
|
Reference.reachabilityFence(this);
|
|
}
|
|
}
|
|
|
|
#if[streamableType]
|
|
$type$ getUnchecked(int i) {
|
|
try {
|
|
return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(null, null, ix(i))));
|
|
} finally {
|
|
Reference.reachabilityFence(this);
|
|
}
|
|
}
|
|
#end[streamableType]
|
|
#end[rw]
|
|
|
|
public $Type$Buffer put($type$ x) {
|
|
#if[rw]
|
|
try {
|
|
SCOPED_MEMORY_ACCESS.put$Swaptype$(session(), null, ix(nextPutIndex()), $swap$($toBits$(x)));
|
|
} finally {
|
|
Reference.reachabilityFence(this);
|
|
}
|
|
return this;
|
|
#else[rw]
|
|
throw new ReadOnlyBufferException();
|
|
#end[rw]
|
|
}
|
|
|
|
public $Type$Buffer put(int i, $type$ x) {
|
|
#if[rw]
|
|
try {
|
|
SCOPED_MEMORY_ACCESS.put$Swaptype$(session(), null, ix(checkIndex(i)), $swap$($toBits$(x)));
|
|
} finally {
|
|
Reference.reachabilityFence(this);
|
|
}
|
|
return this;
|
|
#else[rw]
|
|
throw new ReadOnlyBufferException();
|
|
#end[rw]
|
|
}
|
|
|
|
public {#if[byte]?Mapped$Type$Buffer:$Type$Buffer} compact() {
|
|
#if[rw]
|
|
int pos = position();
|
|
int lim = limit();
|
|
assert (pos <= lim);
|
|
int rem = (pos <= lim ? lim - pos : 0);
|
|
try {
|
|
// null is passed as destination MemorySession to avoid checking session() twice
|
|
SCOPED_MEMORY_ACCESS.copyMemory(session(), null, null,
|
|
ix(pos), null, ix(0), (long)rem << $LG_BYTES_PER_VALUE$);
|
|
} finally {
|
|
Reference.reachabilityFence(this);
|
|
}
|
|
position(rem);
|
|
limit(capacity());
|
|
discardMark();
|
|
return this;
|
|
#else[rw]
|
|
throw new ReadOnlyBufferException();
|
|
#end[rw]
|
|
}
|
|
|
|
public boolean isDirect() {
|
|
return true;
|
|
}
|
|
|
|
public boolean isReadOnly() {
|
|
return {#if[rw]?false:true};
|
|
}
|
|
|
|
|
|
#if[char]
|
|
|
|
public String toString(int start, int end) {
|
|
Objects.checkFromToIndex(start, end, limit());
|
|
int len = end - start;
|
|
char[] ca = new char[len];
|
|
CharBuffer cb = CharBuffer.wrap(ca);
|
|
CharBuffer db = this.duplicate();
|
|
db.position(start);
|
|
db.limit(end);
|
|
cb.put(db);
|
|
return new String(ca);
|
|
}
|
|
|
|
|
|
// --- Methods to support CharSequence ---
|
|
|
|
#if[rw]
|
|
private static final int APPEND_BUF_SIZE = 1024;
|
|
|
|
private $Type$Buffer appendChars(CharSequence csq, int start, int end) {
|
|
Objects.checkFromToIndex(start, end, csq.length());
|
|
|
|
int pos = position();
|
|
int lim = limit();
|
|
int rem = (pos <= lim) ? lim - pos : 0;
|
|
int length = end - start;
|
|
if (length > rem)
|
|
throw new BufferOverflowException();
|
|
|
|
char[] buf = new char[Math.min(APPEND_BUF_SIZE, length)];
|
|
int index = pos;
|
|
while (start < end) {
|
|
int count = end - start;
|
|
if (count > buf.length)
|
|
count = buf.length;
|
|
|
|
if (csq instanceof String str) {
|
|
str.getChars(start, start + count, buf, 0);
|
|
} else if (csq instanceof StringBuilder sb) {
|
|
sb.getChars(start, start + count, buf, 0);
|
|
} else if (csq instanceof StringBuffer sb) {
|
|
sb.getChars(start, start + count, buf, 0);
|
|
}
|
|
|
|
putArray(index, buf, 0, count);
|
|
|
|
start += count;
|
|
index += count;
|
|
}
|
|
|
|
position(pos + length);
|
|
|
|
return this;
|
|
}
|
|
#end[rw]
|
|
|
|
public $Type$Buffer append(CharSequence csq) {
|
|
#if[rw]
|
|
if (csq instanceof StringBuilder)
|
|
return appendChars(csq, 0, csq.length());
|
|
|
|
return super.append(csq);
|
|
#else[rw]
|
|
throw new ReadOnlyBufferException();
|
|
#end[rw]
|
|
}
|
|
|
|
public $Type$Buffer append(CharSequence csq, int start, int end) {
|
|
#if[rw]
|
|
if (csq instanceof String || csq instanceof StringBuffer ||
|
|
csq instanceof StringBuilder)
|
|
return appendChars(csq, start, end);
|
|
|
|
return super.append(csq, start, end);
|
|
#else[rw]
|
|
throw new ReadOnlyBufferException();
|
|
#end[rw]
|
|
}
|
|
|
|
public CharBuffer subSequence(int start, int end) {
|
|
int pos = position();
|
|
int lim = limit();
|
|
assert (pos <= lim);
|
|
pos = (pos <= lim ? pos : lim);
|
|
int len = lim - pos;
|
|
|
|
Objects.checkFromToIndex(start, end, len);
|
|
return new DirectCharBuffer$RW$$BO$(this,
|
|
-1,
|
|
pos + start,
|
|
pos + end,
|
|
capacity(),
|
|
offset, segment);
|
|
}
|
|
|
|
#end[char]
|
|
|
|
|
|
|
|
#if[!byte]
|
|
|
|
public ByteOrder order() {
|
|
#if[boS]
|
|
return ((ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN)
|
|
? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
|
|
#end[boS]
|
|
#if[boU]
|
|
return ((ByteOrder.nativeOrder() != ByteOrder.BIG_ENDIAN)
|
|
? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
|
|
#end[boU]
|
|
}
|
|
|
|
#end[!byte]
|
|
|
|
#if[char]
|
|
ByteOrder charRegionOrder() {
|
|
return order();
|
|
}
|
|
#end[char]
|
|
|
|
#if[byte]
|
|
@ForceInline
|
|
@Override
|
|
int scaleShifts() {
|
|
return 0;
|
|
}
|
|
|
|
@ForceInline
|
|
@Override
|
|
AbstractMemorySegmentImpl heapSegment(Object base,
|
|
long offset,
|
|
long length,
|
|
boolean readOnly,
|
|
MemorySessionImpl bufferScope) {
|
|
// Direct buffers are not backed by an array.
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
#end[byte]
|
|
|
|
#if[byte]
|
|
// #BIN
|
|
//
|
|
// Binary-data access methods for short, char, int, long, float,
|
|
// and double will be inserted here
|
|
|
|
#end[byte]
|
|
|
|
}
|