/* * Copyright (C) 2014 The Android Open Source Project * Copyright (c) 2000, 2011, 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 java.io.FileDescriptor; import dalvik.system.VMRuntime; import libcore.io.Memory; import libcore.io.SizeOf; import sun.misc.Cleaner; import sun.nio.ch.DirectBuffer; /** @hide */ // Not final because it is extended in tests. public class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer { /** * Stores the details of the memory backing a DirectByteBuffer. This could be a pointer * (passed through from JNI or resulting from a mapping) or a non-movable byte array allocated * from Java. Each MemoryRef also has an isAccessible associated with it, which determines * whether the underlying memory is "accessible". The notion of "accessibility" is usually * defined by the allocator of the reference, and is separate from the accessibility of the * memory as defined by the underlying system. * * A single MemoryRef instance is shared across all slices and duplicates of a given buffer. */ final static class MemoryRef { byte[] buffer; long allocatedAddress; final int offset; boolean isAccessible; boolean isFreed; // Reference to original DirectByteBuffer that held this MemoryRef. The field is set // only for the MemoryRef created through JNI NewDirectByteBuffer(void*, long) function. // This allows users of JNI NewDirectByteBuffer to create a PhantomReference on the // DirectByteBuffer instance that will only be put in the associated ReferenceQueue when // the underlying memory is not referenced by any DirectByteBuffer instance. The // MemoryRef can outlive the original DirectByteBuffer instance if, for example, slice() // or asReadOnlyBuffer() are called and all strong references to the original DirectByteBuffer // are discarded. final Object originalBufferObject; MemoryRef(int capacity) { VMRuntime runtime = VMRuntime.getRuntime(); buffer = (byte[]) runtime.newNonMovableArray(byte.class, capacity + 7); allocatedAddress = runtime.addressOf(buffer); // Offset is set to handle the alignment: http://b/16449607 offset = (int) (((allocatedAddress + 7) & ~(long) 7) - allocatedAddress); isAccessible = true; isFreed = false; originalBufferObject = null; } MemoryRef(long allocatedAddress, Object originalBufferObject) { buffer = null; this.allocatedAddress = allocatedAddress; this.offset = 0; this.originalBufferObject = originalBufferObject; isAccessible = true; } void free() { buffer = null; allocatedAddress = 0; isAccessible = false; isFreed = true; } } final Cleaner cleaner; final MemoryRef memoryRef; DirectByteBuffer(int capacity, MemoryRef memoryRef) { super(-1, 0, capacity, capacity, memoryRef.buffer, memoryRef.offset); // Only have references to java objects, no need for a cleaner since the GC will do all // the work. this.memoryRef = memoryRef; this.address = memoryRef.allocatedAddress + memoryRef.offset; cleaner = null; this.isReadOnly = false; } // Invoked only by JNI: NewDirectByteBuffer(void*, long) @SuppressWarnings("unused") private DirectByteBuffer(long addr, int cap) { super(-1, 0, cap, cap); memoryRef = new MemoryRef(addr, this); address = addr; cleaner = null; } /** @hide */ public DirectByteBuffer(int cap, long addr, FileDescriptor fd, Runnable unmapper, boolean isReadOnly) { super(-1, 0, cap, cap, fd); this.isReadOnly = isReadOnly; memoryRef = new MemoryRef(addr, null); address = addr; cleaner = Cleaner.create(memoryRef, unmapper); } // For duplicates and slices DirectByteBuffer(MemoryRef memoryRef, // package-private int mark, int pos, int lim, int cap, int off) { this(memoryRef, mark, pos, lim, cap, off, false); } DirectByteBuffer(MemoryRef memoryRef, // package-private int mark, int pos, int lim, int cap, int off, boolean isReadOnly) { super(mark, pos, lim, cap, memoryRef.buffer, off); this.isReadOnly = isReadOnly; this.memoryRef = memoryRef; address = memoryRef.allocatedAddress + off; cleaner = null; } @Override public final Object attachment() { return memoryRef; } @Override public final Cleaner cleaner() { return cleaner; } @Override public final ByteBuffer slice() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } int pos = position(); int lim = limit(); assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); int off = pos + offset; assert (off >= 0); return new DirectByteBuffer(memoryRef, -1, 0, rem, rem, off, isReadOnly); } @Override public final ByteBuffer duplicate() { if (memoryRef.isFreed) { throw new IllegalStateException("buffer has been freed"); } return new DirectByteBuffer(memoryRef, this.markValue(), this.position(), this.limit(), this.capacity(), offset, isReadOnly); } @Override public final ByteBuffer asReadOnlyBuffer() { if (memoryRef.isFreed) { throw new IllegalStateException("buffer has been freed"); } return new DirectByteBuffer(memoryRef, this.markValue(), this.position(), this.limit(), this.capacity(), offset, true); } @Override public final long address() { return address; } private long ix(int i) { return address + i; } private byte get(long a) { return Memory.peekByte(a); } @Override public final byte get() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return get(ix(nextGetIndex())); } @Override public final byte get(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return get(ix(checkIndex(i))); } // This method is not declared final because it is overridden in tests. @Override public ByteBuffer get(byte[] dst, int dstOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } checkBounds(dstOffset, length, dst.length); int pos = position(); int lim = limit(); assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); if (length > rem) throw new BufferUnderflowException(); Memory.peekByteArray(ix(pos), dst, dstOffset, length); position = pos + length; return this; } private ByteBuffer put(long a, byte x) { Memory.pokeByte(a, x); return this; } @Override public final ByteBuffer put(byte x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } put(ix(nextPutIndex()), x); return this; } @Override public final ByteBuffer put(int i, byte x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } put(ix(checkIndex(i)), x); return this; } // This method is not declared final because it is overridden in tests. @Override public ByteBuffer put(byte[] src, int srcOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } checkBounds(srcOffset, length, src.length); int pos = position(); int lim = limit(); assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); if (length > rem) throw new BufferOverflowException(); Memory.pokeByteArray(ix(pos), src, srcOffset, length); position = pos + length; return this; } @Override public final ByteBuffer compact() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } int pos = position(); int lim = limit(); assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); System.arraycopy(hb, position + offset, hb, offset, remaining()); position(rem); limit(capacity()); discardMark(); return this; } @Override public final boolean isDirect() { return true; } @Override public final boolean isReadOnly() { return isReadOnly; } // Used by java.nio.Bits @Override final byte _get(int i) { // package-private return get(i); } // Used by java.nio.Bits @Override final void _put(int i, byte b) { // package-private put(i, b); } @Override public final char getChar() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } int newPosition = position + SizeOf.CHAR; if (newPosition > limit()) { throw new BufferUnderflowException(); } char x = (char) Memory.peekShort(ix(position), !nativeByteOrder); position = newPosition; return x; } @Override public final char getChar(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } checkIndex(i, SizeOf.CHAR); return (char) Memory.peekShort(ix(i), !nativeByteOrder); } @Override char getCharUnchecked(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return (char) Memory.peekShort(ix(i), !nativeByteOrder); } @Override void getUnchecked(int pos, char[] dst, int dstOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.peekCharArray(ix(pos), dst, dstOffset, length, !nativeByteOrder); } private ByteBuffer putChar(long a, char x) { Memory.pokeShort(a, (short) x, !nativeByteOrder); return this; } @Override public final ByteBuffer putChar(char x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putChar(ix(nextPutIndex(SizeOf.CHAR)), x); return this; } @Override public final ByteBuffer putChar(int i, char x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putChar(ix(checkIndex(i, SizeOf.CHAR)), x); return this; } @Override void putCharUnchecked(int i, char x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } putChar(ix(i), x); } @Override void putUnchecked(int pos, char[] src, int srcOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.pokeCharArray(ix(pos), src, srcOffset, length, !nativeByteOrder); } @Override public final CharBuffer asCharBuffer() { if (memoryRef.isFreed) { throw new IllegalStateException("buffer has been freed"); } int off = this.position(); int lim = this.limit(); assert (off <= lim); int rem = (off <= lim ? lim - off : 0); int size = rem >> 1; return new ByteBufferAsCharBuffer(this, -1, 0, size, size, off, order()); } private short getShort(long a) { return Memory.peekShort(a, !nativeByteOrder); } @Override public final short getShort() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getShort(ix(nextGetIndex(SizeOf.SHORT))); } @Override public final short getShort(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getShort(ix(checkIndex(i, SizeOf.SHORT))); } @Override short getShortUnchecked(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getShort(ix(i)); } @Override void getUnchecked(int pos, short[] dst, int dstOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.peekShortArray(ix(pos), dst, dstOffset, length, !nativeByteOrder); } private ByteBuffer putShort(long a, short x) { Memory.pokeShort(a, x, !nativeByteOrder); return this; } @Override public final ByteBuffer putShort(short x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putShort(ix(nextPutIndex(SizeOf.SHORT)), x); return this; } @Override public final ByteBuffer putShort(int i, short x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putShort(ix(checkIndex(i, SizeOf.SHORT)), x); return this; } @Override void putShortUnchecked(int i, short x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } putShort(ix(i), x); } @Override void putUnchecked(int pos, short[] src, int srcOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.pokeShortArray(ix(pos), src, srcOffset, length, !nativeByteOrder); } @Override public final ShortBuffer asShortBuffer() { if (memoryRef.isFreed) { throw new IllegalStateException("buffer has been freed"); } int off = this.position(); int lim = this.limit(); assert (off <= lim); int rem = (off <= lim ? lim - off : 0); int size = rem >> 1; return new ByteBufferAsShortBuffer(this, -1, 0, size, size, off, order()); } private int getInt(long a) { return Memory.peekInt(a, !nativeByteOrder); } @Override public int getInt() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getInt(ix(nextGetIndex(SizeOf.INT))); } @Override public int getInt(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getInt(ix(checkIndex(i, (SizeOf.INT)))); } @Override final int getIntUnchecked(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getInt(ix(i)); } @Override final void getUnchecked(int pos, int[] dst, int dstOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.peekIntArray(ix(pos), dst, dstOffset, length, !nativeByteOrder); } private ByteBuffer putInt(long a, int x) { Memory.pokeInt(a, x, !nativeByteOrder); return this; } @Override public final ByteBuffer putInt(int x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putInt(ix(nextPutIndex(SizeOf.INT)), x); return this; } @Override public final ByteBuffer putInt(int i, int x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putInt(ix(checkIndex(i, SizeOf.INT)), x); return this; } @Override final void putIntUnchecked(int i, int x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } putInt(ix(i), x); } @Override final void putUnchecked(int pos, int[] src, int srcOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.pokeIntArray(ix(pos), src, srcOffset, length, !nativeByteOrder); } @Override public final IntBuffer asIntBuffer() { if (memoryRef.isFreed) { throw new IllegalStateException("buffer has been freed"); } int off = this.position(); int lim = this.limit(); assert (off <= lim); int rem = (off <= lim ? lim - off : 0); int size = rem >> 2; return new ByteBufferAsIntBuffer(this, -1, 0, size, size, off, order()); } private long getLong(long a) { return Memory.peekLong(a, !nativeByteOrder); } @Override public final long getLong() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getLong(ix(nextGetIndex(SizeOf.LONG))); } @Override public final long getLong(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getLong(ix(checkIndex(i, SizeOf.LONG))); } @Override final long getLongUnchecked(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getLong(ix(i)); } @Override final void getUnchecked(int pos, long[] dst, int dstOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.peekLongArray(ix(pos), dst, dstOffset, length, !nativeByteOrder); } private ByteBuffer putLong(long a, long x) { Memory.pokeLong(a, x, !nativeByteOrder); return this; } @Override public final ByteBuffer putLong(long x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putLong(ix(nextPutIndex(SizeOf.LONG)), x); return this; } @Override public final ByteBuffer putLong(int i, long x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putLong(ix(checkIndex(i, SizeOf.LONG)), x); return this; } @Override final void putLongUnchecked(int i, long x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } putLong(ix(i), x); } @Override final void putUnchecked(int pos, long[] src, int srcOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.pokeLongArray(ix(pos), src, srcOffset, length, !nativeByteOrder); } @Override public final LongBuffer asLongBuffer() { if (memoryRef.isFreed) { throw new IllegalStateException("buffer has been freed"); } int off = this.position(); int lim = this.limit(); assert (off <= lim); int rem = (off <= lim ? lim - off : 0); int size = rem >> 3; return new ByteBufferAsLongBuffer(this, -1, 0, size, size, off, order()); } private float getFloat(long a) { int x = Memory.peekInt(a, !nativeByteOrder); return Float.intBitsToFloat(x); } @Override public final float getFloat() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getFloat(ix(nextGetIndex(SizeOf.FLOAT))); } @Override public final float getFloat(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getFloat(ix(checkIndex(i, SizeOf.FLOAT))); } @Override final float getFloatUnchecked(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getFloat(ix(i)); } @Override final void getUnchecked(int pos, float[] dst, int dstOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.peekFloatArray(ix(pos), dst, dstOffset, length, !nativeByteOrder); } private ByteBuffer putFloat(long a, float x) { int y = Float.floatToRawIntBits(x); Memory.pokeInt(a, y, !nativeByteOrder); return this; } @Override public final ByteBuffer putFloat(float x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putFloat(ix(nextPutIndex(SizeOf.FLOAT)), x); return this; } @Override public final ByteBuffer putFloat(int i, float x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putFloat(ix(checkIndex(i, SizeOf.FLOAT)), x); return this; } @Override final void putFloatUnchecked(int i, float x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } putFloat(ix(i), x); } @Override final void putUnchecked(int pos, float[] src, int srcOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.pokeFloatArray(ix(pos), src, srcOffset, length, !nativeByteOrder); } @Override public final FloatBuffer asFloatBuffer() { if (memoryRef.isFreed) { throw new IllegalStateException("buffer has been freed"); } int off = this.position(); int lim = this.limit(); assert (off <= lim); int rem = (off <= lim ? lim - off : 0); int size = rem >> 2; return new ByteBufferAsFloatBuffer(this, -1, 0, size, size, off, order()); } private double getDouble(long a) { long x = Memory.peekLong(a, !nativeByteOrder); return Double.longBitsToDouble(x); } @Override public final double getDouble() { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getDouble(ix(nextGetIndex(SizeOf.DOUBLE))); } @Override public final double getDouble(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getDouble(ix(checkIndex(i, SizeOf.DOUBLE))); } @Override final double getDoubleUnchecked(int i) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } return getDouble(ix(i)); } @Override final void getUnchecked(int pos, double[] dst, int dstOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.peekDoubleArray(ix(pos), dst, dstOffset, length, !nativeByteOrder); } private ByteBuffer putDouble(long a, double x) { long y = Double.doubleToRawLongBits(x); Memory.pokeLong(a, y, !nativeByteOrder); return this; } @Override public final ByteBuffer putDouble(double x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putDouble(ix(nextPutIndex(SizeOf.DOUBLE)), x); return this; } @Override public final ByteBuffer putDouble(int i, double x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } if (isReadOnly) { throw new ReadOnlyBufferException(); } putDouble(ix(checkIndex(i, SizeOf.DOUBLE)), x); return this; } @Override final void putDoubleUnchecked(int i, double x) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } putDouble(ix(i), x); } @Override final void putUnchecked(int pos, double[] src, int srcOffset, int length) { if (!memoryRef.isAccessible) { throw new IllegalStateException("buffer is inaccessible"); } Memory.pokeDoubleArray(ix(pos), src, srcOffset, length, !nativeByteOrder); } @Override public final DoubleBuffer asDoubleBuffer() { if (memoryRef.isFreed) { throw new IllegalStateException("buffer has been freed"); } int off = this.position(); int lim = this.limit(); assert (off <= lim); int rem = (off <= lim ? lim - off : 0); int size = rem >> 3; return new ByteBufferAsDoubleBuffer(this, -1, 0, size, size, off, order()); } @Override public final boolean isAccessible() { return memoryRef.isAccessible; } @Override public final void setAccessible(boolean value) { memoryRef.isAccessible = value; } }