blob: c6bc7695e58bcfc3e906b53f66933349698d71ce [file] [log] [blame]
/*
* 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 io.netty.util.internal.PlatformDependent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteOrder;
import java.util.concurrent.atomic.AtomicLong;
/**
* The underlying class we use for little-endian access to memory. Is used
* underneath DrillBufs to abstract away the Netty classes and underlying Netty
* memory management.
*/
public final class UnsafeDirectLittleEndian extends WrappedByteBuf {
private static final boolean NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
private static final AtomicLong ID_GENERATOR = new AtomicLong(0);
public final long id = ID_GENERATOR.incrementAndGet();
private final AbstractByteBuf wrapped;
private final long memoryAddress;
private final AtomicLong bufferCount;
private final AtomicLong bufferSize;
private final long initCap;
UnsafeDirectLittleEndian(DuplicatedByteBuf buf) {
this(buf, true, null, null);
}
UnsafeDirectLittleEndian(LargeBuffer buf) {
this(buf, true, null, null);
}
UnsafeDirectLittleEndian(PooledUnsafeDirectByteBuf buf, AtomicLong bufferCount, AtomicLong bufferSize) {
this(buf, true, bufferCount, bufferSize);
}
private UnsafeDirectLittleEndian(AbstractByteBuf buf, boolean fake, AtomicLong bufferCount, AtomicLong bufferSize) {
super(buf);
if (!NATIVE_ORDER) {
throw new IllegalStateException("Drill only runs on LittleEndian systems.");
}
this.bufferCount = bufferCount;
this.bufferSize = bufferSize;
// initCap is used if we're tracking memory release. If we're in non-debug mode, we'll skip this.
this.initCap = ASSERT_ENABLED ? buf.capacity() : -1;
this.wrapped = buf;
this.memoryAddress = buf.memoryAddress();
}
private long addr(int index) {
return memoryAddress + index;
}
@Override
public long getLong(int index) {
//wrapped.checkIndex(index, 8);
return PlatformDependent.getLong(addr(index));
}
@Override
public float getFloat(int index) {
return Float.intBitsToFloat(getInt(index));
}
@Override
public ByteBuf slice() {
return slice(this.readerIndex(), readableBytes());
}
@Override
public ByteBuf slice(int index, int length) {
return new SlicedByteBuf(this, index, length);
}
@Override
public ByteBuf order(ByteOrder endianness) {
return this;
}
@Override
public double getDouble(int index) {
return Double.longBitsToDouble(getLong(index));
}
@Override
public char getChar(int index) {
return (char) getShort(index);
}
@Override
public long getUnsignedInt(int index) {
return getInt(index) & 0xFFFFFFFFL;
}
@Override
public int getInt(int index) {
int v = PlatformDependent.getInt(addr(index));
return v;
}
@Override
public int getUnsignedShort(int index) {
return getShort(index) & 0xFFFF;
}
@Override
public short getShort(int index) {
short v = PlatformDependent.getShort(addr(index));
return v;
}
@Override
public ByteBuf setShort(int index, int value) {
wrapped.checkIndex(index, 2);
_setShort(index, value);
return this;
}
@Override
public ByteBuf setInt(int index, int value) {
wrapped.checkIndex(index, 4);
_setInt(index, value);
return this;
}
@Override
public ByteBuf setLong(int index, long value) {
wrapped.checkIndex(index, 8);
_setLong(index, value);
return this;
}
@Override
public ByteBuf setChar(int index, int value) {
setShort(index, value);
return this;
}
@Override
public ByteBuf setFloat(int index, float value) {
setInt(index, Float.floatToRawIntBits(value));
return this;
}
@Override
public ByteBuf setDouble(int index, double value) {
setLong(index, Double.doubleToRawLongBits(value));
return this;
}
// Clone of the super class checkIndex, but this version returns a boolean rather
// than throwing an exception.
protected boolean hasCapacity(int index, int fieldLength) {
assert fieldLength >= 0;
return (! (index < 0 || index > capacity() - fieldLength));
}
/**
* Write bytes into the buffer at the given index, if space is available.
* @param index location to write
* @param src bytes to write
* @param srcIndex start of data in the source array
* @param length length of the data to write
* @return true if the value was written, false if the value was not
* written because the value would overflow the buffer
*/
public boolean setBytesBounded(int index, byte[] src, int srcIndex, int length) {
if (! hasCapacity(index, length)) {
return false;
}
PlatformDependent.copyMemory(src, srcIndex, addr(index), length);
return true;
}
// Version of the super class setBytes(), but with bounds checking done as a boolean,
// not assertion. This version requires a direct source buffer.
public boolean setBytesBounded(int index, UnsafeDirectLittleEndian src, int srcIndex, int length) {
if (! hasCapacity(index, length)) {
return false;
}
PlatformDependent.copyMemory(src.memoryAddress() + srcIndex, addr(index), length);
return true;
}
@Override
public ByteBuf writeShort(int value) {
wrapped.ensureWritable(2);
_setShort(wrapped.writerIndex, value);
wrapped.writerIndex += 2;
return this;
}
@Override
public ByteBuf writeInt(int value) {
wrapped.ensureWritable(4);
_setInt(wrapped.writerIndex, value);
wrapped.writerIndex += 4;
return this;
}
@Override
public ByteBuf writeLong(long value) {
wrapped.ensureWritable(8);
_setLong(wrapped.writerIndex, value);
wrapped.writerIndex += 8;
return this;
}
@Override
public ByteBuf writeChar(int value) {
writeShort(value);
return this;
}
@Override
public ByteBuf writeFloat(float value) {
writeInt(Float.floatToRawIntBits(value));
return this;
}
@Override
public ByteBuf writeDouble(double value) {
writeLong(Double.doubleToRawLongBits(value));
return this;
}
private void _setShort(int index, int value) {
PlatformDependent.putShort(addr(index), (short) value);
}
private void _setInt(int index, int value) {
PlatformDependent.putInt(addr(index), value);
}
private void _setLong(int index, long value) {
PlatformDependent.putLong(addr(index), value);
}
@Override
public byte getByte(int index) {
return PlatformDependent.getByte(addr(index));
}
@Override
public ByteBuf setByte(int index, int value) {
PlatformDependent.putByte(addr(index), (byte) value);
return this;
}
@Override
public boolean release() {
return release(1);
}
@Override
public boolean release(int decrement) {
final boolean released = super.release(decrement);
if (ASSERT_ENABLED && released && bufferCount != null && bufferSize != null) {
bufferCount.decrementAndGet();
bufferSize.addAndGet(-initCap);
}
return released;
}
@Override
public int setBytes(int index, InputStream in, int length) throws IOException {
wrapped.checkIndex(index, length);
byte[] tmp = new byte[length];
int readBytes = in.read(tmp);
if (readBytes > 0) {
PlatformDependent.copyMemory(tmp, 0, addr(index), readBytes);
}
return readBytes;
}
@Override
public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
wrapped.checkIndex(index, length);
if (length != 0) {
byte[] tmp = new byte[length];
PlatformDependent.copyMemory(addr(index), tmp, 0, length);
out.write(tmp);
}
return this;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
public static final boolean ASSERT_ENABLED;
static {
boolean isAssertEnabled = false;
assert isAssertEnabled = true;
ASSERT_ENABLED = isAssertEnabled;
}
}