/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.buffer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import io.netty.util.internal.PlatformDependent;
/**
* A NIO {@link ByteBuffer}, byte[] and address direct access wrapper.
* Only content manipulation operations are supported.
* Is best suited only for encoding/decoding purposes.
*/
public final class UnpooledUnsafeDirectByteBufWrapper extends AbstractReferenceCountedByteBuf {
private ByteBuffer buffer;
private int arrayOffset;
private byte[] array;
private long memoryAddress;
public UnpooledUnsafeDirectByteBufWrapper() {
super(0);
this.buffer = null;
this.arrayOffset = -1;
this.array = null;
this.memoryAddress = 0L;
}
public void wrap(long address, int length) {
this.memoryAddress = address;
this.arrayOffset = -1;
this.array = null;
this.buffer = null;
clear();
maxCapacity(length);
}
public void wrap(byte[] array, int srcIndex, int length) {
if (array != null) {
this.memoryAddress = 0L;
this.arrayOffset = srcIndex;
this.array = array;
this.buffer = null;
clear();
maxCapacity(length);
} else {
reset();
}
}
public void wrap(ByteBuffer buffer, int srcIndex, int length) {
if (buffer != null) {
this.buffer = buffer;
if (buffer.isDirect()) {
this.memoryAddress = PlatformDependent.directBufferAddress(buffer) + srcIndex;
this.arrayOffset = -1;
this.array = null;
} else {
this.arrayOffset = srcIndex;
this.array = buffer.array();
this.memoryAddress = 0L;
}
clear();
maxCapacity(length);
} else {
reset();
}
}
public void reset() {
this.buffer = null;
this.memoryAddress = 0L;
this.arrayOffset = -1;
this.array = null;
clear();
maxCapacity(0);
}
@Override
public boolean isDirect() {
return buffer != null ? buffer.isDirect() : false;
}
@Override
public int capacity() {
return maxCapacity();
}
@Override
public ByteBuf capacity(int newCapacity) {
if (newCapacity != maxCapacity()) {
throw new IllegalArgumentException("can't set a capacity different from the max allowed one");
}
return this;
}
@Override
public ByteBufAllocator alloc() {
return null;
}
@Override
public ByteOrder order() {
return ByteOrder.BIG_ENDIAN;
}
@Override
public boolean hasArray() {
return array != null;
}
@Override
public byte[] array() {
return array;
}
@Override
public int arrayOffset() {
return arrayOffset;
}
@Override
public boolean hasMemoryAddress() {
return memoryAddress != 0;
}
@Override
public long memoryAddress() {
return memoryAddress;
}
@Override
protected byte _getByte(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getByte(addr(index));
} else {
return UnsafeByteBufUtil.getByte(array, idx(index));
}
}
@Override
protected short _getShort(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getShort(addr(index));
} else {
return UnsafeByteBufUtil.getShort(array, idx(index));
}
}
@Override
protected short _getShortLE(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getShortLE(addr(index));
} else {
return UnsafeByteBufUtil.getShortLE(array, idx(index));
}
}
@Override
protected int _getUnsignedMedium(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getUnsignedMedium(addr(index));
} else {
return UnsafeByteBufUtil.getUnsignedMedium(array, idx(index));
}
}
@Override
protected int _getUnsignedMediumLE(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getUnsignedMediumLE(addr(index));
} else {
return UnsafeByteBufUtil.getUnsignedMediumLE(array, idx(index));
}
}
@Override
protected int _getInt(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getInt(addr(index));
} else {
return UnsafeByteBufUtil.getInt(array, idx(index));
}
}
@Override
protected int _getIntLE(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getIntLE(addr(index));
} else {
return UnsafeByteBufUtil.getIntLE(array, idx(index));
}
}
@Override
protected long _getLong(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getLong(addr(index));
} else {
return UnsafeByteBufUtil.getLong(array, idx(index));
}
}
@Override
protected long _getLongLE(int index) {
if (hasMemoryAddress()) {
return UnsafeByteBufUtil.getLongLE(addr(index));
} else {
return UnsafeByteBufUtil.getLongLE(array, idx(index));
}
}
@Override
public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.getBytes(this, addr(index), index, dst, dstIndex, length);
} else {
final int idx = idx(index);
checkDstIndex(idx, length, dstIndex, dst.capacity());
getBytes(array, idx, dst, dstIndex, length);
}
return this;
}
private static void getBytes(byte[] array, int idx, ByteBuf dst, int dstIndex, int length) {
if (dst.hasMemoryAddress()) {
PlatformDependent.copyMemory(array, idx, dst.memoryAddress() + dstIndex, length);
} else if (dst.hasArray()) {
System.arraycopy(array, idx, dst.array(), dst.arrayOffset() + dstIndex, length);
} else {
dst.setBytes(dstIndex, array, idx, length);
}
}
@Override
public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.getBytes(this, addr(index), index, dst, dstIndex, length);
} else {
final int idx = idx(index);
checkDstIndex(idx, length, dstIndex, dst.length);
System.arraycopy(array, idx, dst, dstIndex, length);
}
return this;
}
@Override
public ByteBuf getBytes(int index, ByteBuffer dst) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.getBytes(this, addr(index), index, dst);
} else {
final int idx = idx(index);
checkIndex(idx, dst.remaining());
getBytes(array, idx, dst);
}
return this;
}
private static void getBytes(byte[] array, int idx, ByteBuffer dst) {
if (dst.remaining() == 0) {
return;
}
if (dst.isDirect()) {
if (dst.isReadOnly()) {
// We need to check if dst is ready-only so we not write something in it by using Unsafe.
throw new ReadOnlyBufferException();
}
// Copy to direct memory
final long dstAddress = PlatformDependent.directBufferAddress(dst);
PlatformDependent.copyMemory(array, idx, dstAddress + dst.position(), dst.remaining());
dst.position(dst.position() + dst.remaining());
} else if (dst.hasArray()) {
// Copy to array
System.arraycopy(array, idx, dst.array(), dst.arrayOffset() + dst.position(), dst.remaining());
dst.position(dst.position() + dst.remaining());
} else {
dst.put(array, idx, dst.remaining());
}
}
@Override
public ByteBuf readBytes(ByteBuffer dst) {
final int length = dst.remaining();
checkReadableBytes(length);
getBytes(readerIndex, dst);
readerIndex += length;
return this;
}
@Override
protected void _setByte(int index, int value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setByte(addr(index), value);
} else {
UnsafeByteBufUtil.setByte(array, idx(index), value);
}
}
@Override
protected void _setShort(int index, int value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setShort(addr(index), value);
} else {
UnsafeByteBufUtil.setShort(array, idx(index), value);
}
}
@Override
protected void _setShortLE(int index, int value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setShortLE(addr(index), value);
} else {
UnsafeByteBufUtil.setShortLE(array, idx(index), value);
}
}
@Override
protected void _setMedium(int index, int value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setMedium(addr(index), value);
} else {
UnsafeByteBufUtil.setMedium(array, idx(index), value);
}
}
@Override
protected void _setMediumLE(int index, int value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setMediumLE(addr(index), value);
} else {
UnsafeByteBufUtil.setMediumLE(array, idx(index), value);
}
}
@Override
protected void _setInt(int index, int value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setInt(addr(index), value);
} else {
UnsafeByteBufUtil.setInt(array, idx(index), value);
}
}
@Override
protected void _setIntLE(int index, int value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setIntLE(addr(index), value);
} else {
UnsafeByteBufUtil.setIntLE(array, idx(index), value);
}
}
@Override
protected void _setLong(int index, long value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setLong(addr(index), value);
} else {
UnsafeByteBufUtil.setLong(array, idx(index), value);
}
}
@Override
protected void _setLongLE(int index, long value) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setLongLE(addr(index), value);
} else {
UnsafeByteBufUtil.setLongLE(array, idx(index), value);
}
}
@Override
public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setBytes(this, addr(index), index, src, srcIndex, length);
} else {
final int idx = idx(index);
checkSrcIndex(idx, length, srcIndex, src.capacity());
setBytes(array, idx, src, srcIndex, length);
}
return this;
}
private static void setBytes(byte[] array, int idx, ByteBuf src, int srcIndex, int length) {
if (src.hasMemoryAddress()) {
PlatformDependent.copyMemory(src.memoryAddress() + srcIndex, array, idx, length);
} else if (src.hasArray()) {
System.arraycopy(src.array(), src.arrayOffset() + srcIndex, array, idx, length);
} else {
src.getBytes(srcIndex, array, idx, length);
}
}
@Override
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setBytes(this, addr(index), index, src, srcIndex, length);
} else {
final int idx = idx(index);
checkSrcIndex(idx, length, srcIndex, src.length);
System.arraycopy(src, srcIndex, array, idx, length);
}
return this;
}
@Override
public ByteBuf setBytes(int index, ByteBuffer src) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setBytes(this, addr(index), index, src);
} else {
final int idx = idx(index);
checkSrcIndex(idx, src.remaining(), src.position(), src.capacity());
setBytes(array, idx(index), src);
}
return this;
}
private static void setBytes(byte[] array, int idx, ByteBuffer src) {
final int length = src.remaining();
if (length == 0) {
return;
}
if (src.isDirect()) {
// Copy from direct memory
final long srcAddress = PlatformDependent.directBufferAddress(src);
PlatformDependent.copyMemory(srcAddress + src.position(), array, idx, length);
src.position(src.position() + length);
} else if (src.hasArray()) {
// Copy from array
System.arraycopy(src.array(), src.arrayOffset() + src.position(), array, idx, length);
src.position(src.position() + length);
} else {
src.get(array, idx, src.remaining());
}
}
@Override
@Deprecated
public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public int getBytes(int index, FileChannel out, long position, int length) throws IOException {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public int readBytes(GatheringByteChannel out, int length) throws IOException {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public int readBytes(FileChannel out, long position, int length) throws IOException {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public int setBytes(int index, InputStream in, int length) throws IOException {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public int setBytes(int index, FileChannel in, long position, int length) throws IOException {
throw new UnsupportedOperationException("unsupported!");
}
@Override
public int nioBufferCount() {
return buffer == null ? 0 : 1;
}
@Override
@Deprecated
public ByteBuffer[] nioBuffers(int index, int length) {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public ByteBuf copy(int index, int length) {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
public ByteBuffer internalNioBuffer(int index, int length) {
throw new UnsupportedOperationException("cannot access directly the wrapped buffer!");
}
@Override
@Deprecated
public ByteBuffer nioBuffer(int index, int length) {
throw new UnsupportedOperationException("unsupported!");
}
@Override
@Deprecated
protected void deallocate() {
//NO_OP
}
@Override
public ByteBuf unwrap() {
return null;
}
private long addr(int index) {
return memoryAddress + index;
}
private int idx(int index) {
return arrayOffset + index;
}
@Override
@Deprecated
protected SwappedByteBuf newSwappedByteBuf() {
throw new UnsupportedOperationException("unsupported!");
}
@Override
public ByteBuf setZero(int index, int length) {
if (hasMemoryAddress()) {
UnsafeByteBufUtil.setZero(this, addr(index), index, length);
} else {
//prefer Arrays::fill here?
UnsafeByteBufUtil.setZero(array, idx(index), length);
}
return this;
}
@Override
public ByteBuf writeZero(int length) {
ensureWritable(length);
int wIndex = writerIndex;
setZero(wIndex, length);
writerIndex = wIndex + length;
return this;
}
}