Buffer should not expose nativeAddress() directly (#11665)
Motivation: Accessing the native address, if a buffer has any, violates the no-aliasing rule for Buffers. Also, it inherently assumes that a buffer has a single, native memory allocation. The native address, or addresses, are already available via the Readable- and WritableComponents. Accessing these via forEachReadable and forEachWritable will also ensure that composite buffers will be handled correctly. Modification: Remove the nativeAddress() method from the Buffer API. Update the ByteBufAdaptor and a few tests to cope with this change. Result: Less error prone code, and make unsafe APIs a bit more hidden.
This commit is contained in:
parent
3152ec76db
commit
1eb9a9764e
@ -172,12 +172,6 @@ public interface Buffer extends Resource<Buffer>, BufferAccessor {
|
|||||||
*/
|
*/
|
||||||
Buffer fill(byte value);
|
Buffer fill(byte value);
|
||||||
|
|
||||||
/**
|
|
||||||
* Gives the native memory address backing this buffer, or return 0 if this buffer has no native memory address.
|
|
||||||
* @return The native memory address, if any, otherwise 0.
|
|
||||||
*/
|
|
||||||
long nativeAddress();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes this buffer read-only. This is irreversible.
|
* Makes this buffer read-only. This is irreversible.
|
||||||
* This operation is also idempotent, so calling this method multiple times on the same buffer makes no difference.
|
* This operation is also idempotent, so calling this method multiple times on the same buffer makes no difference.
|
||||||
|
@ -25,8 +25,9 @@ import java.util.function.Supplier;
|
|||||||
public interface BufferAllocator extends AutoCloseable {
|
public interface BufferAllocator extends AutoCloseable {
|
||||||
/**
|
/**
|
||||||
* Produces a {@link BufferAllocator} that allocates unpooled, on-heap buffers.
|
* Produces a {@link BufferAllocator} that allocates unpooled, on-heap buffers.
|
||||||
* On-heap buffers have a {@code byte[]} internally, and their {@linkplain Buffer#nativeAddress() native address}
|
* On-heap buffers have a {@code byte[]} internally, and their
|
||||||
* is zero.
|
* {@linkplain ReadableComponent#readableNativeAddress() readable} and
|
||||||
|
* {@linkplain WritableComponent#writableNativeAddress() writable} native addresses are zero.
|
||||||
* <p>
|
* <p>
|
||||||
* The concrete {@link Buffer} implementation is chosen by {@link MemoryManager#instance()}.
|
* The concrete {@link Buffer} implementation is chosen by {@link MemoryManager#instance()}.
|
||||||
*
|
*
|
||||||
@ -39,7 +40,8 @@ public interface BufferAllocator extends AutoCloseable {
|
|||||||
/**
|
/**
|
||||||
* Produces a {@link BufferAllocator} that allocates unpooled, off-heap buffers.
|
* Produces a {@link BufferAllocator} that allocates unpooled, off-heap buffers.
|
||||||
* Off-heap buffers a native memory pointer internally, which can be obtained from their
|
* Off-heap buffers a native memory pointer internally, which can be obtained from their
|
||||||
* {@linkplain Buffer#nativeAddress() native address method.
|
* {@linkplain ReadableComponent#readableNativeAddress() readable} and
|
||||||
|
* {@linkplain WritableComponent#writableNativeAddress() writable} native address methods.
|
||||||
* <p>
|
* <p>
|
||||||
* The concrete {@link Buffer} implementation is chosen by {@link MemoryManager#instance()}.
|
* The concrete {@link Buffer} implementation is chosen by {@link MemoryManager#instance()}.
|
||||||
*
|
*
|
||||||
@ -51,8 +53,9 @@ public interface BufferAllocator extends AutoCloseable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a pooling {@link BufferAllocator} that allocates and recycles on-heap buffers.
|
* Produces a pooling {@link BufferAllocator} that allocates and recycles on-heap buffers.
|
||||||
* On-heap buffers have a {@code byte[]} internally, and their {@linkplain Buffer#nativeAddress() native address}
|
* On-heap buffers have a {@code byte[]} internally, and their
|
||||||
* is zero.
|
* {@linkplain ReadableComponent#readableNativeAddress() readable} and
|
||||||
|
* {@linkplain WritableComponent#writableNativeAddress() writable} native addresses are zero.
|
||||||
* <p>
|
* <p>
|
||||||
* The concrete {@link Buffer} implementation is chosen by {@link MemoryManager#instance()}.
|
* The concrete {@link Buffer} implementation is chosen by {@link MemoryManager#instance()}.
|
||||||
*
|
*
|
||||||
@ -65,7 +68,8 @@ public interface BufferAllocator extends AutoCloseable {
|
|||||||
/**
|
/**
|
||||||
* Produces a pooling {@link BufferAllocator} that allocates and recycles off-heap buffers.
|
* Produces a pooling {@link BufferAllocator} that allocates and recycles off-heap buffers.
|
||||||
* Off-heap buffers a native memory pointer internally, which can be obtained from their
|
* Off-heap buffers a native memory pointer internally, which can be obtained from their
|
||||||
* {@linkplain Buffer#nativeAddress() native address method.
|
* {@linkplain ReadableComponent#readableNativeAddress() readable} and
|
||||||
|
* {@linkplain WritableComponent#writableNativeAddress() writable} native address methods.
|
||||||
* <p>
|
* <p>
|
||||||
* The concrete {@link Buffer} implementation is chosen by {@link MemoryManager#instance()}.
|
* The concrete {@link Buffer} implementation is chosen by {@link MemoryManager#instance()}.
|
||||||
*
|
*
|
||||||
|
@ -76,11 +76,6 @@ public class BufferStub implements Buffer {
|
|||||||
return delegate.fill(value);
|
return delegate.fill(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long nativeAddress() {
|
|
||||||
return delegate.nativeAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Buffer makeReadOnly() {
|
public Buffer makeReadOnly() {
|
||||||
return delegate.makeReadOnly();
|
return delegate.makeReadOnly();
|
||||||
|
@ -384,11 +384,6 @@ public final class CompositeBuffer extends ResourceSupport<Buffer, CompositeBuff
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long nativeAddress() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompositeBuffer makeReadOnly() {
|
public CompositeBuffer makeReadOnly() {
|
||||||
for (Buffer buf : bufs) {
|
for (Buffer buf : bufs) {
|
||||||
|
@ -48,7 +48,7 @@ import static io.netty.buffer.api.internal.Statics.isOwned;
|
|||||||
public final class ByteBufAdaptor extends ByteBuf {
|
public final class ByteBufAdaptor extends ByteBuf {
|
||||||
private final ByteBufAllocatorAdaptor alloc;
|
private final ByteBufAllocatorAdaptor alloc;
|
||||||
private final Buffer buffer;
|
private final Buffer buffer;
|
||||||
private final boolean hasMemoryAddress;
|
private long nativeAddress;
|
||||||
private final int maxCapacity;
|
private final int maxCapacity;
|
||||||
|
|
||||||
public ByteBufAdaptor(ByteBufAllocatorAdaptor alloc, Buffer buffer, int maxCapacity) {
|
public ByteBufAdaptor(ByteBufAllocatorAdaptor alloc, Buffer buffer, int maxCapacity) {
|
||||||
@ -58,10 +58,36 @@ public final class ByteBufAdaptor extends ByteBuf {
|
|||||||
}
|
}
|
||||||
this.alloc = Objects.requireNonNull(alloc, "The ByteBuf allocator adaptor cannot be null.");
|
this.alloc = Objects.requireNonNull(alloc, "The ByteBuf allocator adaptor cannot be null.");
|
||||||
this.buffer = Objects.requireNonNull(buffer, "The buffer being adapted cannot be null.");
|
this.buffer = Objects.requireNonNull(buffer, "The buffer being adapted cannot be null.");
|
||||||
hasMemoryAddress = buffer.nativeAddress() != 0;
|
updateNativeAddress(buffer);
|
||||||
this.maxCapacity = maxCapacity;
|
this.maxCapacity = maxCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateNativeAddress(Buffer buffer) {
|
||||||
|
if (buffer.countReadableComponents() == 1) {
|
||||||
|
buffer.forEachReadable(0, (index, component) -> {
|
||||||
|
if (index == 0) {
|
||||||
|
nativeAddress = component.readableNativeAddress();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
nativeAddress = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (buffer.countWritableComponents() == 1) {
|
||||||
|
buffer.forEachWritable(0, (index, component) -> {
|
||||||
|
if (index == 0) {
|
||||||
|
nativeAddress = component.writableNativeAddress();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
nativeAddress = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
nativeAddress = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the underlying {@link Buffer} instance that is backing this {@link ByteBuf}, if any.
|
* Extracts the underlying {@link Buffer} instance that is backing this {@link ByteBuf}, if any.
|
||||||
* This is similar to {@link #unwrap()} except the return type is a {@link Buffer}.
|
* This is similar to {@link #unwrap()} except the return type is a {@link Buffer}.
|
||||||
@ -154,7 +180,7 @@ public final class ByteBufAdaptor extends ByteBuf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirect() {
|
public boolean isDirect() {
|
||||||
return hasMemoryAddress;
|
return nativeAddress != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1549,7 +1575,7 @@ public final class ByteBufAdaptor extends ByteBuf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasMemoryAddress() {
|
public boolean hasMemoryAddress() {
|
||||||
return hasMemoryAddress;
|
return isDirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1557,7 +1583,7 @@ public final class ByteBufAdaptor extends ByteBuf {
|
|||||||
if (!hasMemoryAddress()) {
|
if (!hasMemoryAddress()) {
|
||||||
throw new UnsupportedOperationException("No memory address associated with this buffer.");
|
throw new UnsupportedOperationException("No memory address associated with this buffer.");
|
||||||
}
|
}
|
||||||
return buffer.nativeAddress();
|
return nativeAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -165,8 +165,7 @@ class NioBuffer extends AdaptableBuffer<NioBuffer> implements ReadableComponent,
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private long nativeAddress() {
|
||||||
public long nativeAddress() {
|
|
||||||
return rmem.isDirect() && PlatformDependent.hasUnsafe()? PlatformDependent.directBufferAddress(rmem) : 0;
|
return rmem.isDirect() && PlatformDependent.hasUnsafe()? PlatformDependent.directBufferAddress(rmem) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +135,7 @@ class UnsafeBuffer extends AdaptableBuffer<UnsafeBuffer> implements ReadableComp
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private long nativeAddress() {
|
||||||
public long nativeAddress() {
|
|
||||||
return base == null? address : 0;
|
return base == null? address : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,14 +231,16 @@ class UnsafeBuffer extends AdaptableBuffer<UnsafeBuffer> implements ReadableComp
|
|||||||
if (dest.readOnly()) {
|
if (dest.readOnly()) {
|
||||||
throw bufferIsReadOnly(this);
|
throw bufferIsReadOnly(this);
|
||||||
}
|
}
|
||||||
long nativeAddress = dest.nativeAddress();
|
|
||||||
try {
|
try {
|
||||||
|
if (dest instanceof UnsafeBuffer) {
|
||||||
|
UnsafeBuffer destUnsafe = (UnsafeBuffer) dest;
|
||||||
|
long nativeAddress = destUnsafe.nativeAddress();
|
||||||
if (nativeAddress != 0) {
|
if (nativeAddress != 0) {
|
||||||
PlatformDependent.copyMemory(base, address + srcPos, null, nativeAddress + destPos, length);
|
PlatformDependent.copyMemory(base, address + srcPos, null, nativeAddress + destPos, length);
|
||||||
} else if (dest instanceof UnsafeBuffer) {
|
} else {
|
||||||
UnsafeBuffer destUnsafe = (UnsafeBuffer) dest;
|
|
||||||
PlatformDependent.copyMemory(
|
PlatformDependent.copyMemory(
|
||||||
base, address + srcPos, destUnsafe.base, destUnsafe.address + destPos, length);
|
base, address + srcPos, destUnsafe.base, destUnsafe.address + destPos, length);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Statics.copyToViaReverseLoop(this, srcPos, dest, destPos, length);
|
Statics.copyToViaReverseLoop(this, srcPos, dest, destPos, length);
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,30 @@ public class BufferBulkAccessTest extends BufferTestSupport {
|
|||||||
public void heapBufferMustHaveZeroAddress(Fixture fixture) {
|
public void heapBufferMustHaveZeroAddress(Fixture fixture) {
|
||||||
try (BufferAllocator allocator = fixture.createAllocator();
|
try (BufferAllocator allocator = fixture.createAllocator();
|
||||||
Buffer buf = allocator.allocate(8)) {
|
Buffer buf = allocator.allocate(8)) {
|
||||||
assertThat(buf.nativeAddress()).isZero();
|
assertThat(buf.countReadableComponents()).isZero();
|
||||||
|
assertThat(buf.countWritableComponents()).isOne();
|
||||||
|
buf.forEachWritable(0, (index, component) -> {
|
||||||
|
assertThat(component.writableNativeAddress()).isZero();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
buf.writeInt(42);
|
||||||
|
assertThat(buf.countReadableComponents()).isOne();
|
||||||
|
assertThat(buf.countWritableComponents()).isOne();
|
||||||
|
buf.forEachReadable(0, (index, component) -> {
|
||||||
|
assertThat(component.readableNativeAddress()).isZero();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
buf.forEachWritable(0, (index, component) -> {
|
||||||
|
assertThat(component.writableNativeAddress()).isZero();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
buf.writeInt(42);
|
||||||
|
assertThat(buf.countReadableComponents()).isOne();
|
||||||
|
assertThat(buf.countWritableComponents()).isZero();
|
||||||
|
buf.forEachReadable(0, (index, component) -> {
|
||||||
|
assertThat(component.readableNativeAddress()).isZero();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +265,30 @@ public class BufferBulkAccessTest extends BufferTestSupport {
|
|||||||
public void directBufferMustHaveNonZeroAddress(Fixture fixture) {
|
public void directBufferMustHaveNonZeroAddress(Fixture fixture) {
|
||||||
try (BufferAllocator allocator = fixture.createAllocator();
|
try (BufferAllocator allocator = fixture.createAllocator();
|
||||||
Buffer buf = allocator.allocate(8)) {
|
Buffer buf = allocator.allocate(8)) {
|
||||||
assertThat(buf.nativeAddress()).isNotZero();
|
assertThat(buf.countReadableComponents()).isZero();
|
||||||
|
assertThat(buf.countWritableComponents()).isOne();
|
||||||
|
buf.forEachWritable(0, (index, component) -> {
|
||||||
|
assertThat(component.writableNativeAddress()).isNotZero();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
buf.writeInt(42);
|
||||||
|
assertThat(buf.countReadableComponents()).isOne();
|
||||||
|
assertThat(buf.countWritableComponents()).isOne();
|
||||||
|
buf.forEachReadable(0, (index, component) -> {
|
||||||
|
assertThat(component.readableNativeAddress()).isNotZero();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
buf.forEachWritable(0, (index, component) -> {
|
||||||
|
assertThat(component.writableNativeAddress()).isNotZero();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
buf.writeInt(42);
|
||||||
|
assertThat(buf.countReadableComponents()).isOne();
|
||||||
|
assertThat(buf.countWritableComponents()).isZero();
|
||||||
|
buf.forEachReadable(0, (index, component) -> {
|
||||||
|
assertThat(component.readableNativeAddress()).isNotZero();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user