[#5645] Allow to create ByteBuf from existing memory address.

Motivation:

Sometimes it is useful to be able to wrap an existing memory address (a.k.a pointer) and create a ByteBuf from it. This way its easier to interopt with other libraries.

Modifications:

Add a new Unpooled.wrappedBuffer(....) method that takes a memory address.

Result:

Be able to wrap an existing memory address into a ByteBuf.
This commit is contained in:
Norman Maurer 2016-08-05 20:17:24 +02:00
parent 00f74b92fa
commit e7449b1ef3
7 changed files with 195 additions and 3 deletions

View File

@ -207,6 +207,14 @@ public final class Unpooled {
}
}
/**
* Creates a new buffer which wraps the specified memory address. If {@code doFree} is true the
* memoryAddress will automatically be freed once the reference count of the {@link ByteBuf} reaches {@code 0}.
*/
public static ByteBuf wrappedBuffer(long memoryAddress, int size, boolean doFree) {
return new WrappedUnpooledUnsafeDirectByteBuf(ALLOC, memoryAddress, size, doFree);
}
/**
* Creates a new buffer which wraps the specified buffer's readable bytes.
* A modification on the specified buffer's content will be visible to the

View File

@ -36,11 +36,11 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf
private final ByteBufAllocator alloc;
private long memoryAddress;
private ByteBuffer tmpNioBuf;
private int capacity;
private boolean doNotFree;
ByteBuffer buffer;
long memoryAddress;
/**
* Creates a new direct buffer.
@ -74,6 +74,10 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf
* @param maxCapacity the maximum capacity of the underlying direct buffer
*/
protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initialBuffer, int maxCapacity) {
this(alloc, initialBuffer, maxCapacity, true);
}
UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initialBuffer, int maxCapacity, boolean doFree) {
super(maxCapacity);
if (alloc == null) {
throw new NullPointerException("alloc");
@ -95,7 +99,7 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf
}
this.alloc = alloc;
doNotFree = true;
doNotFree = !doFree;
setByteBuffer(initialBuffer.slice().order(ByteOrder.BIG_ENDIAN), false);
writerIndex(initialCapacity);
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project 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.nio.ByteBuffer;
final class WrappedUnpooledUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf {
WrappedUnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, long memoryAddress, int size, boolean doFree) {
super(alloc, PlatformDependent.directBuffer(memoryAddress, size), size, doFree);
}
@Override
protected void freeDirect(ByteBuffer buffer) {
PlatformDependent.freeMemory(memoryAddress);
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project 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 org.junit.Test;
import java.io.IOException;
public class WrappedUnpooledUnsafeByteBufTest extends BigEndianUnsafeDirectByteBufTest {
@Override
protected ByteBuf newBuffer(int length) {
return new WrappedUnpooledUnsafeDirectByteBuf(UnpooledByteBufAllocator.DEFAULT,
PlatformDependent.allocateMemory(length), length, true);
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testInternalNioBuffer() {
super.testInternalNioBuffer();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testDuplicateReadGatheringByteChannelMultipleThreads() throws Exception {
super.testDuplicateReadGatheringByteChannelMultipleThreads();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testSliceReadGatheringByteChannelMultipleThreads() throws Exception {
super.testSliceReadGatheringByteChannelMultipleThreads();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testDuplicateReadOutputStreamMultipleThreads() throws Exception {
super.testDuplicateReadOutputStreamMultipleThreads();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testSliceReadOutputStreamMultipleThreads() throws Exception {
super.testSliceReadOutputStreamMultipleThreads();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testDuplicateBytesInArrayMultipleThreads() throws Exception {
super.testDuplicateBytesInArrayMultipleThreads();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testSliceBytesInArrayMultipleThreads() throws Exception {
super.testSliceBytesInArrayMultipleThreads();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testNioBufferExposeOnlyRegion() {
super.testNioBufferExposeOnlyRegion();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testEnsureWritableAfterRelease() {
super.testEnsureWritableAfterRelease();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testWriteZeroAfterRelease() throws IOException {
super.testWriteZeroAfterRelease();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testGetReadOnlyDirectDst() {
super.testGetReadOnlyDirectDst();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testGetReadOnlyHeapDst() {
super.testGetReadOnlyHeapDst();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testReadBytes() {
super.testReadBytes();
}
@Test(expected = IllegalArgumentException.class)
@Override
public void testDuplicateCapacityChange() {
super.testDuplicateCapacityChange();
}
@Test(expected = IllegalArgumentException.class)
@Override
public void testRetainedDuplicateCapacityChange() {
super.testRetainedDuplicateCapacityChange();
}
@Test(expected = IndexOutOfBoundsException.class)
@Override
public void testLittleEndianWithExpand() {
super.testLittleEndianWithExpand();
}
}

View File

@ -66,6 +66,17 @@ public final class ObjectUtil {
return i;
}
/**
* Checks that the given argument is positive or zero. If it is, throws {@link IllegalArgumentException}.
* Otherwise, returns the argument.
*/
public static long checkPositiveOrZero(long i, String name) {
if (i < 0) {
throw new IllegalArgumentException(name + ": " + i + " (expected: >= 0)");
}
return i;
}
/**
* Checks that the given argument is neither null nor empty.
* If it is, throws {@link NullPointerException} or {@link IllegalArgumentException}.

View File

@ -365,6 +365,14 @@ public final class PlatformDependent {
return PlatformDependent0.directBufferAddress(buffer);
}
public static ByteBuffer directBuffer(long memoryAddress, int size) {
if (PlatformDependent0.hasDirectBufferNoCleanerConstructor()) {
return PlatformDependent0.newDirectBuffer(memoryAddress, size);
}
throw new UnsupportedOperationException(
"sun.misc.Unsafe or java.nio.DirectByteBuffer.<init>(long, int) not available");
}
public static Object getObject(Object object, long fieldOffset) {
return PlatformDependent0.getObject(object, fieldOffset);
}

View File

@ -287,7 +287,10 @@ final class PlatformDependent0 {
return newDirectBuffer(UNSAFE.allocateMemory(capacity), capacity);
}
private static ByteBuffer newDirectBuffer(long address, int capacity) {
static ByteBuffer newDirectBuffer(long address, int capacity) {
ObjectUtil.checkPositiveOrZero(address, "address");
ObjectUtil.checkPositiveOrZero(capacity, "capacity");
try {
return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
} catch (Throwable cause) {