Skip to content

Commit d58dec8

Browse files
committed
[netty#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.
1 parent cca236a commit d58dec8

File tree

7 files changed

+259
-3
lines changed

7 files changed

+259
-3
lines changed

buffer/src/main/java/io/netty/buffer/Unpooled.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,14 @@ public static ByteBuf wrappedBuffer(ByteBuffer buffer) {
207207
}
208208
}
209209

210+
/**
211+
* Creates a new buffer which wraps the specified memory address. If {@code doFree} is true the
212+
* memoryAddress will automatically be freed once the reference count of the {@link ByteBuf} reaches {@code 0}.
213+
*/
214+
public static ByteBuf wrappedBuffer(long memoryAddress, int size, boolean doFree) {
215+
return new WrappedUnpooledUnsafeDirectByteBuf(ALLOC, memoryAddress, size, doFree);
216+
}
217+
210218
/**
211219
* Creates a new buffer which wraps the specified buffer's readable bytes.
212220
* A modification on the specified buffer's content will be visible to the

buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf
3535

3636
private final ByteBufAllocator alloc;
3737

38-
private long memoryAddress;
3938
private ByteBuffer tmpNioBuf;
4039
private int capacity;
4140
private boolean doNotFree;
4241
ByteBuffer buffer;
42+
long memoryAddress;
4343

4444
/**
4545
* Creates a new direct buffer.
@@ -73,6 +73,10 @@ protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacit
7373
* @param maxCapacity the maximum capacity of the underlying direct buffer
7474
*/
7575
protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initialBuffer, int maxCapacity) {
76+
this(alloc, initialBuffer, maxCapacity, true);
77+
}
78+
79+
UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initialBuffer, int maxCapacity, boolean doFree) {
7680
super(maxCapacity);
7781
if (alloc == null) {
7882
throw new NullPointerException("alloc");
@@ -94,7 +98,7 @@ protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initial
9498
}
9599

96100
this.alloc = alloc;
97-
doNotFree = true;
101+
doNotFree = !doFree;
98102
setByteBuffer(initialBuffer.slice().order(ByteOrder.BIG_ENDIAN), false);
99103
writerIndex(initialCapacity);
100104
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2016 The Netty Project
3+
*
4+
* The Netty Project licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
package io.netty.buffer;
17+
18+
import io.netty.util.internal.PlatformDependent;
19+
20+
import java.nio.ByteBuffer;
21+
22+
final class WrappedUnpooledUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf {
23+
24+
WrappedUnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, long memoryAddress, int size, boolean doFree) {
25+
super(alloc, PlatformDependent.directBuffer(memoryAddress, size), size, doFree);
26+
}
27+
28+
@Override
29+
protected void freeDirect(ByteBuffer buffer) {
30+
PlatformDependent.freeMemory(memoryAddress);
31+
}
32+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2016 The Netty Project
3+
*
4+
* The Netty Project licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
package io.netty.buffer;
17+
18+
import io.netty.util.internal.PlatformDependent;
19+
import org.junit.Test;
20+
21+
import java.io.IOException;
22+
23+
public class WrappedUnpooledUnsafeByteBufTest extends BigEndianUnsafeDirectByteBufTest {
24+
25+
@Override
26+
protected ByteBuf newBuffer(int length) {
27+
return new WrappedUnpooledUnsafeDirectByteBuf(UnpooledByteBufAllocator.DEFAULT,
28+
PlatformDependent.allocateMemory(length), length, true);
29+
}
30+
31+
@Test(expected = IndexOutOfBoundsException.class)
32+
@Override
33+
public void testInternalNioBuffer() {
34+
super.testInternalNioBuffer();
35+
}
36+
37+
@Test(expected = IndexOutOfBoundsException.class)
38+
@Override
39+
public void testDuplicateReadGatheringByteChannelMultipleThreads() throws Exception {
40+
super.testDuplicateReadGatheringByteChannelMultipleThreads();
41+
}
42+
43+
@Test(expected = IndexOutOfBoundsException.class)
44+
@Override
45+
public void testSliceReadGatheringByteChannelMultipleThreads() throws Exception {
46+
super.testSliceReadGatheringByteChannelMultipleThreads();
47+
}
48+
49+
@Test(expected = IndexOutOfBoundsException.class)
50+
@Override
51+
public void testDuplicateReadOutputStreamMultipleThreads() throws Exception {
52+
super.testDuplicateReadOutputStreamMultipleThreads();
53+
}
54+
55+
@Test(expected = IndexOutOfBoundsException.class)
56+
@Override
57+
public void testSliceReadOutputStreamMultipleThreads() throws Exception {
58+
super.testSliceReadOutputStreamMultipleThreads();
59+
}
60+
61+
@Test(expected = IndexOutOfBoundsException.class)
62+
@Override
63+
public void testDuplicateBytesInArrayMultipleThreads() throws Exception {
64+
super.testDuplicateBytesInArrayMultipleThreads();
65+
}
66+
67+
@Test(expected = IndexOutOfBoundsException.class)
68+
@Override
69+
public void testSliceBytesInArrayMultipleThreads() throws Exception {
70+
super.testSliceBytesInArrayMultipleThreads();
71+
}
72+
73+
@Test(expected = IndexOutOfBoundsException.class)
74+
@Override
75+
public void testNioBufferExposeOnlyRegion() {
76+
super.testNioBufferExposeOnlyRegion();
77+
}
78+
79+
@Test(expected = IndexOutOfBoundsException.class)
80+
@Override
81+
public void testEnsureWritableAfterRelease() {
82+
super.testEnsureWritableAfterRelease();
83+
}
84+
85+
@Test(expected = IndexOutOfBoundsException.class)
86+
@Override
87+
public void testWriteZeroAfterRelease() throws IOException {
88+
super.testWriteZeroAfterRelease();
89+
}
90+
91+
@Test(expected = IndexOutOfBoundsException.class)
92+
@Override
93+
public void testGetReadOnlyDirectDst() {
94+
super.testGetReadOnlyDirectDst();
95+
}
96+
97+
@Test(expected = IndexOutOfBoundsException.class)
98+
@Override
99+
public void testGetReadOnlyHeapDst() {
100+
super.testGetReadOnlyHeapDst();
101+
}
102+
103+
@Test(expected = IndexOutOfBoundsException.class)
104+
@Override
105+
public void testReadBytes() {
106+
super.testReadBytes();
107+
}
108+
109+
@Test(expected = IllegalArgumentException.class)
110+
@Override
111+
public void testDuplicateCapacityChange() {
112+
super.testDuplicateCapacityChange();
113+
}
114+
115+
@Test(expected = IllegalArgumentException.class)
116+
@Override
117+
public void testRetainedDuplicateCapacityChange() {
118+
super.testRetainedDuplicateCapacityChange();
119+
}
120+
121+
@Test(expected = IndexOutOfBoundsException.class)
122+
@Override
123+
public void testLittleEndianWithExpand() {
124+
super.testLittleEndianWithExpand();
125+
}
126+
}

common/src/main/java/io/netty/util/internal/ObjectUtil.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,79 @@ public static <T> T checkNotNull(T arg, String text) {
3232
}
3333
return arg;
3434
}
35+
36+
/**
37+
* Checks that the given argument is strictly positive. If it is, throws {@link IllegalArgumentException}.
38+
* Otherwise, returns the argument.
39+
*/
40+
public static int checkPositive(int i, String name) {
41+
if (i <= 0) {
42+
throw new IllegalArgumentException(name + ": " + i + " (expected: > 0)");
43+
}
44+
return i;
45+
}
46+
47+
/**
48+
* Checks that the given argument is strictly positive. If it is, throws {@link IllegalArgumentException}.
49+
* Otherwise, returns the argument.
50+
*/
51+
public static long checkPositive(long i, String name) {
52+
if (i <= 0) {
53+
throw new IllegalArgumentException(name + ": " + i + " (expected: > 0)");
54+
}
55+
return i;
56+
}
57+
58+
/**
59+
* Checks that the given argument is positive or zero. If it is, throws {@link IllegalArgumentException}.
60+
* Otherwise, returns the argument.
61+
*/
62+
public static int checkPositiveOrZero(int i, String name) {
63+
if (i < 0) {
64+
throw new IllegalArgumentException(name + ": " + i + " (expected: >= 0)");
65+
}
66+
return i;
67+
}
68+
69+
/**
70+
* Checks that the given argument is positive or zero. If it is, throws {@link IllegalArgumentException}.
71+
* Otherwise, returns the argument.
72+
*/
73+
public static long checkPositiveOrZero(long i, String name) {
74+
if (i < 0) {
75+
throw new IllegalArgumentException(name + ": " + i + " (expected: >= 0)");
76+
}
77+
return i;
78+
}
79+
80+
/**
81+
* Checks that the given argument is neither null nor empty.
82+
* If it is, throws {@link NullPointerException} or {@link IllegalArgumentException}.
83+
* Otherwise, returns the argument.
84+
*/
85+
public static <T> T[] checkNonEmpty(T[] array, String name) {
86+
checkNotNull(array, name);
87+
checkPositive(array.length, name + ".length");
88+
return array;
89+
}
90+
91+
/**
92+
* Resolves a possibly null Integer to a primitive int, using a default value.
93+
* @param wrapper the wrapper
94+
* @param defaultValue the default value
95+
* @return the primitive value
96+
*/
97+
public static int intValue(Integer wrapper, int defaultValue) {
98+
return wrapper != null ? wrapper.intValue() : defaultValue;
99+
}
100+
101+
/**
102+
* Resolves a possibly null Long to a primitive long, using a default value.
103+
* @param wrapper the wrapper
104+
* @param defaultValue the default value
105+
* @return the primitive value
106+
*/
107+
public static long longValue(Long wrapper, long defaultValue) {
108+
return wrapper != null ? wrapper.longValue() : defaultValue;
109+
}
35110
}

common/src/main/java/io/netty/util/internal/PlatformDependent.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,14 @@ public static long directBufferAddress(ByteBuffer buffer) {
360360
return PlatformDependent0.directBufferAddress(buffer);
361361
}
362362

363+
public static ByteBuffer directBuffer(long memoryAddress, int size) {
364+
if (PlatformDependent0.hasDirectBufferNoCleanerConstructor()) {
365+
return PlatformDependent0.newDirectBuffer(memoryAddress, size);
366+
}
367+
throw new UnsupportedOperationException(
368+
"sun.misc.Unsafe or java.nio.DirectByteBuffer.<init>(long, int) not available");
369+
}
370+
363371
public static Object getObject(Object object, long fieldOffset) {
364372
return PlatformDependent0.getObject(object, fieldOffset);
365373
}

common/src/main/java/io/netty/util/internal/PlatformDependent0.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,10 @@ static ByteBuffer allocateDirectNoCleaner(int capacity) {
278278
return newDirectBuffer(UNSAFE.allocateMemory(capacity), capacity);
279279
}
280280

281-
private static ByteBuffer newDirectBuffer(long address, int capacity) {
281+
static ByteBuffer newDirectBuffer(long address, int capacity) {
282+
ObjectUtil.checkPositiveOrZero(address, "address");
283+
ObjectUtil.checkPositiveOrZero(capacity, "capacity");
284+
282285
try {
283286
return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
284287
} catch (Throwable cause) {

0 commit comments

Comments
 (0)