|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.nio; |
|
|
|
import java.io.FileDescriptor; |
|
import java.lang.ref.Reference; |
|
import jdk.internal.misc.Unsafe; |
|
|
|
|
|
/** |
|
* A direct byte buffer whose content is a memory-mapped region of a file. |
|
* |
|
* <p> Mapped byte buffers are created via the {@link |
|
* java.nio.channels.FileChannel#map FileChannel.map} method. This class |
|
* extends the {@link ByteBuffer} class with operations that are specific to |
|
* memory-mapped file regions. |
|
* |
|
* <p> A mapped byte buffer and the file mapping that it represents remain |
|
* valid until the buffer itself is garbage-collected. |
|
* |
|
* <p> The content of a mapped byte buffer can change at any time, for example |
|
* if the content of the corresponding region of the mapped file is changed by |
|
* this program or another. Whether or not such changes occur, and when they |
|
* occur, is operating-system dependent and therefore unspecified. |
|
* |
|
* <a id="inaccess"></a><p> All or part of a mapped byte buffer may become |
|
* inaccessible at any time, for example if the mapped file is truncated. An |
|
* attempt to access an inaccessible region of a mapped byte buffer will not |
|
* change the buffer's content and will cause an unspecified exception to be |
|
* thrown either at the time of the access or at some later time. It is |
|
* therefore strongly recommended that appropriate precautions be taken to |
|
* avoid the manipulation of a mapped file by this program, or by a |
|
* concurrently running program, except to read or write the file's content. |
|
* |
|
* <p> Mapped byte buffers otherwise behave no differently than ordinary direct |
|
* byte buffers. </p> |
|
* |
|
* |
|
* @author Mark Reinhold |
|
* @author JSR-51 Expert Group |
|
* @since 1.4 |
|
*/ |
|
|
|
public abstract class MappedByteBuffer |
|
extends ByteBuffer |
|
{ |
|
|
|
// This is a little bit backwards: By rights MappedByteBuffer should be a |
|
// subclass of DirectByteBuffer, but to keep the spec clear and simple, and |
|
// for optimization purposes, it's easier to do it the other way around. |
|
// This works because DirectByteBuffer is a package-private class. |
|
|
|
// For mapped buffers, a FileDescriptor that may be used for mapping |
|
|
|
private final FileDescriptor fd; |
|
|
|
// This should only be invoked by the DirectByteBuffer constructors |
|
|
|
MappedByteBuffer(int mark, int pos, int lim, int cap, |
|
FileDescriptor fd) |
|
{ |
|
super(mark, pos, lim, cap); |
|
this.fd = fd; |
|
} |
|
|
|
MappedByteBuffer(int mark, int pos, int lim, int cap) { |
|
super(mark, pos, lim, cap); |
|
this.fd = null; |
|
} |
|
|
|
// Returns the distance (in bytes) of the buffer from the page aligned address |
|
|
|
private long mappingOffset() { |
|
int ps = Bits.pageSize(); |
|
long offset = address % ps; |
|
return (offset >= 0) ? offset : (ps + offset); |
|
} |
|
|
|
private long mappingAddress(long mappingOffset) { |
|
return address - mappingOffset; |
|
} |
|
|
|
private long mappingLength(long mappingOffset) { |
|
return (long)capacity() + mappingOffset; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean isLoaded() { |
|
if (fd == null) { |
|
return true; |
|
} |
|
if ((address == 0) || (capacity() == 0)) |
|
return true; |
|
long offset = mappingOffset(); |
|
long length = mappingLength(offset); |
|
return isLoaded0(mappingAddress(offset), length, Bits.pageCount(length)); |
|
} |
|
|
|
|
|
private static byte unused; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final MappedByteBuffer load() { |
|
if (fd == null) { |
|
return this; |
|
} |
|
if ((address == 0) || (capacity() == 0)) |
|
return this; |
|
long offset = mappingOffset(); |
|
long length = mappingLength(offset); |
|
load0(mappingAddress(offset), length); |
|
|
|
// Read a byte from each page to bring it into memory. A checksum |
|
// is computed as we go along to prevent the compiler from otherwise |
|
|
|
Unsafe unsafe = Unsafe.getUnsafe(); |
|
int ps = Bits.pageSize(); |
|
int count = Bits.pageCount(length); |
|
long a = mappingAddress(offset); |
|
byte x = 0; |
|
try { |
|
for (int i=0; i<count; i++) { |
|
// TODO consider changing to getByteOpaque thus avoiding |
|
|
|
x ^= unsafe.getByte(a); |
|
a += ps; |
|
} |
|
} finally { |
|
Reference.reachabilityFence(this); |
|
} |
|
if (unused != 0) |
|
unused = x; |
|
|
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final MappedByteBuffer force() { |
|
if (fd == null) { |
|
return this; |
|
} |
|
if ((address != 0) && (capacity() != 0)) { |
|
long offset = mappingOffset(); |
|
force0(fd, mappingAddress(offset), mappingLength(offset)); |
|
} |
|
return this; |
|
} |
|
|
|
private native boolean isLoaded0(long address, long length, int pageCount); |
|
private native void load0(long address, long length); |
|
private native void force0(FileDescriptor fd, long address, long length); |
|
|
|
// -- Covariant return type overrides |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final MappedByteBuffer position(int newPosition) { |
|
super.position(newPosition); |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final MappedByteBuffer limit(int newLimit) { |
|
super.limit(newLimit); |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final MappedByteBuffer mark() { |
|
super.mark(); |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final MappedByteBuffer reset() { |
|
super.reset(); |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final MappedByteBuffer clear() { |
|
super.clear(); |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final MappedByteBuffer flip() { |
|
super.flip(); |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final MappedByteBuffer rewind() { |
|
super.rewind(); |
|
return this; |
|
} |
|
} |