Skip to content

Commit c2fe48c

Browse files
committed
Extract DirectBufferAccess to a new file
1 parent 3ba731f commit c2fe48c

File tree

2 files changed

+145
-144
lines changed

2 files changed

+145
-144
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package org.msgpack.core.buffer;
2+
3+
import java.lang.reflect.Constructor;
4+
import java.lang.reflect.InvocationTargetException;
5+
import java.lang.reflect.Method;
6+
import java.nio.ByteBuffer;
7+
8+
/**
9+
* Wraps the difference of access methods to DirectBuffers between Android and others.
10+
*/
11+
class DirectBufferAccess {
12+
13+
enum DirectBufferConstructorType {
14+
ARGS_LONG_INT_REF,
15+
ARGS_LONG_INT,
16+
ARGS_INT_INT,
17+
ARGS_MB_INT_INT
18+
}
19+
20+
21+
static Method mGetAddress;
22+
static Method mCleaner;
23+
static Method mClean;
24+
25+
// TODO We should use MethodHandle for efficiency, but it is not available in JDK6
26+
static Constructor byteBufferConstructor;
27+
static Class<?> directByteBufferClass;
28+
static DirectBufferConstructorType directBufferConstructorType;
29+
static Method memoryBlockWrapFromJni;
30+
31+
static {
32+
try {
33+
// Find the hidden constructor for DirectByteBuffer
34+
directByteBufferClass = ClassLoader.getSystemClassLoader().loadClass("java.nio.DirectByteBuffer");
35+
Constructor directByteBufferConstructor = null;
36+
DirectBufferConstructorType constructorType = null;
37+
Method mbWrap = null;
38+
try {
39+
// TODO We should use MethodHandle for Java7, which can avoid the cost of boxing with JIT optimization
40+
directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(long.class, int.class, Object.class);
41+
constructorType = DirectBufferConstructorType.ARGS_LONG_INT_REF;
42+
}
43+
catch(NoSuchMethodException e0) {
44+
try {
45+
// https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/java/nio/DirectByteBuffer.java
46+
// DirectByteBuffer(long address, int capacity)
47+
directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(long.class, int.class);
48+
constructorType = DirectBufferConstructorType.ARGS_LONG_INT;
49+
}
50+
catch(NoSuchMethodException e1) {
51+
try {
52+
directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(int.class, int.class);
53+
constructorType = DirectBufferConstructorType.ARGS_INT_INT;
54+
}
55+
catch(NoSuchMethodException e2) {
56+
Class<?> aClass = Class.forName("java.nio.MemoryBlock");
57+
mbWrap = aClass.getDeclaredMethod("wrapFromJni", int.class, long.class);
58+
mbWrap.setAccessible(true);
59+
directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(aClass, int.class, int.class);
60+
constructorType = DirectBufferConstructorType.ARGS_MB_INT_INT;
61+
}
62+
}
63+
}
64+
65+
byteBufferConstructor = directByteBufferConstructor;
66+
directBufferConstructorType = constructorType;
67+
memoryBlockWrapFromJni = mbWrap;
68+
69+
if(byteBufferConstructor == null)
70+
throw new RuntimeException("Constructor of DirectByteBuffer is not found");
71+
byteBufferConstructor.setAccessible(true);
72+
}
73+
catch(Exception e) {
74+
75+
}
76+
77+
try {
78+
mGetAddress = directByteBufferClass.getDeclaredMethod("address");
79+
mGetAddress.setAccessible(true);
80+
81+
mCleaner = directByteBufferClass.getDeclaredMethod("cleaner");
82+
mCleaner.setAccessible(true);
83+
84+
mClean = mCleaner.getReturnType().getDeclaredMethod("clean");
85+
mClean.setAccessible(true);
86+
}
87+
catch(NoSuchMethodException e) {
88+
throw new RuntimeException(e);
89+
}
90+
}
91+
92+
static long getAddress(Object base) {
93+
try {
94+
return (Long) mGetAddress.invoke(base);
95+
}
96+
catch(IllegalAccessException e) {
97+
throw new RuntimeException(e);
98+
}
99+
catch(InvocationTargetException e) {
100+
throw new RuntimeException(e);
101+
}
102+
}
103+
104+
static void clean(Object base) {
105+
try {
106+
Object cleaner = mCleaner.invoke(base);
107+
mClean.invoke(cleaner);
108+
}
109+
catch(Throwable e) {
110+
throw new RuntimeException(e);
111+
}
112+
}
113+
114+
static boolean isDirectByteBufferInstance(Object s) {
115+
return directByteBufferClass.isInstance(s);
116+
}
117+
118+
static ByteBuffer newByteBuffer(long address, int index, int length, ByteBuffer reference) {
119+
try {
120+
switch(directBufferConstructorType) {
121+
case ARGS_LONG_INT_REF:
122+
return (ByteBuffer) byteBufferConstructor.newInstance(address + index, length, reference);
123+
case ARGS_LONG_INT:
124+
return (ByteBuffer) byteBufferConstructor.newInstance(address + index, length);
125+
case ARGS_INT_INT:
126+
return (ByteBuffer) byteBufferConstructor.newInstance((int) address + index, length);
127+
case ARGS_MB_INT_INT:
128+
return (ByteBuffer) byteBufferConstructor.newInstance(
129+
memoryBlockWrapFromJni.invoke(null, address + index, length),
130+
length, 0);
131+
default:
132+
throw new IllegalStateException("Unexpected value");
133+
}
134+
}
135+
catch(Throwable e) {
136+
// Convert checked exception to unchecked exception
137+
throw new RuntimeException(e);
138+
}
139+
}
140+
}

msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBuffer.java

Lines changed: 5 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55

66
import java.lang.reflect.Constructor;
77
import java.lang.reflect.Field;
8-
import java.lang.reflect.InvocationTargetException;
9-
import java.lang.reflect.Method;
108
import java.nio.BufferOverflowException;
119
import java.nio.ByteBuffer;
1210
import java.nio.ByteOrder;
13-
import java.security.AccessControlException;
1411

1512
import static org.msgpack.core.Preconditions.*;
1613

@@ -26,18 +23,10 @@ public class MessageBuffer {
2623

2724
static final boolean isUniversalBuffer;
2825
static final Unsafe unsafe;
29-
// TODO We should use MethodHandle for efficiency, but it is not available in JDK6
26+
3027
static final int ARRAY_BYTE_BASE_OFFSET;
3128
static final int ARRAY_BYTE_INDEX_SCALE;
3229

33-
enum DirectBufferConstructorType {
34-
ARGS_LONG_INT_REF,
35-
ARGS_LONG_INT,
36-
ARGS_INT_INT,
37-
ARGS_MB_INT_INT,
38-
NONE
39-
}
40-
4130
static {
4231
try {
4332
// Check java version
@@ -134,133 +123,6 @@ enum DirectBufferConstructorType {
134123
}
135124
}
136125

137-
/**
138-
* Wraps the difference of access methods to DirectBuffers between Android and others.
139-
*/
140-
private static class DirectBufferAccess {
141-
static Method mGetAddress;
142-
static Method mCleaner;
143-
static Method mClean;
144-
145-
static Constructor byteBufferConstructor;
146-
147-
static Class<?> directByteBufferClass;
148-
static DirectBufferConstructorType directBufferConstructorType;
149-
static Method memoryBlockWrapFromJni;
150-
151-
static {
152-
try {
153-
if(!isUniversalBuffer) {
154-
try {
155-
// Find the hidden constructor for DirectByteBuffer
156-
directByteBufferClass = ClassLoader.getSystemClassLoader().loadClass("java.nio.DirectByteBuffer");
157-
Constructor directByteBufferConstructor = null;
158-
DirectBufferConstructorType constructorType = null;
159-
Method mbWrap = null;
160-
try {
161-
// TODO We should use MethodHandle for Java7, which can avoid the cost of boxing with JIT optimization
162-
directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(long.class, int.class, Object.class);
163-
constructorType = DirectBufferConstructorType.ARGS_LONG_INT_REF;
164-
}
165-
catch(NoSuchMethodException e0) {
166-
try {
167-
// https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/java/nio/DirectByteBuffer.java
168-
// DirectByteBuffer(long address, int capacity)
169-
directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(long.class, int.class);
170-
constructorType = DirectBufferConstructorType.ARGS_LONG_INT;
171-
}
172-
catch(NoSuchMethodException e1) {
173-
try {
174-
directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(int.class, int.class);
175-
constructorType = DirectBufferConstructorType.ARGS_INT_INT;
176-
}
177-
catch(NoSuchMethodException e2) {
178-
Class<?> aClass = Class.forName("java.nio.MemoryBlock");
179-
mbWrap = aClass.getDeclaredMethod("wrapFromJni", int.class, long.class);
180-
mbWrap.setAccessible(true);
181-
directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(aClass, int.class, int.class);
182-
constructorType = DirectBufferConstructorType.ARGS_MB_INT_INT;
183-
}
184-
}
185-
}
186-
187-
byteBufferConstructor = directByteBufferConstructor;
188-
directBufferConstructorType = constructorType;
189-
memoryBlockWrapFromJni = mbWrap;
190-
191-
if(byteBufferConstructor == null)
192-
throw new RuntimeException("Constructor of DirectByteBuffer is not found");
193-
byteBufferConstructor.setAccessible(true);
194-
}
195-
catch(Exception e) {
196-
directBufferConstructorType = DirectBufferConstructorType.NONE;
197-
}
198-
}
199-
200-
mGetAddress = directByteBufferClass.getDeclaredMethod("address");
201-
mGetAddress.setAccessible(true);
202-
203-
mCleaner = directByteBufferClass.getDeclaredMethod("cleaner");
204-
mCleaner.setAccessible(true);
205-
206-
mClean = mCleaner.getReturnType().getDeclaredMethod("clean");
207-
mClean.setAccessible(true);
208-
}
209-
catch(NoSuchMethodException e) {
210-
throw new RuntimeException(e);
211-
}
212-
}
213-
214-
static long getAddress(Object base) {
215-
try {
216-
return (Long) mGetAddress.invoke(base);
217-
}
218-
catch(IllegalAccessException e) {
219-
throw new RuntimeException(e);
220-
}
221-
catch(InvocationTargetException e) {
222-
throw new RuntimeException(e);
223-
}
224-
}
225-
226-
static void clean(Object base) {
227-
try {
228-
Object cleaner = mCleaner.invoke(base);
229-
mClean.invoke(cleaner);
230-
}
231-
catch(Throwable e) {
232-
throw new RuntimeException(e);
233-
}
234-
}
235-
236-
static boolean isDirectByteBufferInstance(Object s) {
237-
return directByteBufferClass.isInstance(s);
238-
}
239-
240-
static ByteBuffer newByteBuffer(long address, int index, int length, ByteBuffer reference) {
241-
try {
242-
switch(directBufferConstructorType) {
243-
case ARGS_LONG_INT_REF:
244-
return (ByteBuffer) byteBufferConstructor.newInstance(address + index, length, reference);
245-
case ARGS_LONG_INT:
246-
return (ByteBuffer) byteBufferConstructor.newInstance(address + index, length);
247-
case ARGS_INT_INT:
248-
return (ByteBuffer) byteBufferConstructor.newInstance((int) address + index, length);
249-
case ARGS_MB_INT_INT:
250-
return (ByteBuffer) byteBufferConstructor.newInstance(
251-
memoryBlockWrapFromJni.invoke(null, address + index, length),
252-
length, 0);
253-
default:
254-
throw new IllegalStateException("Unexpected value");
255-
}
256-
}
257-
catch(Throwable e) {
258-
// Convert checked exception to unchecked exception
259-
throw new RuntimeException(e);
260-
}
261-
}
262-
}
263-
264126
/**
265127
* MessageBuffer class to use. If this machine is big-endian, it uses MessageBufferBE, which overrides some methods in this class that translate endians. If not, uses MessageBuffer.
266128
*/
@@ -295,10 +157,6 @@ static ByteBuffer newByteBuffer(long address, int index, int length, ByteBuffer
295157
*/
296158
protected final ByteBuffer reference;
297159

298-
// TODO life-time management of this buffer
299-
//private AtomicInteger referenceCounter;
300-
301-
302160
static MessageBuffer newOffHeapBuffer(int length) {
303161
// This method is not available in Android OS
304162
if(!isUniversalBuffer) {
@@ -362,7 +220,7 @@ private static MessageBuffer newMessageBuffer(byte[] arr) {
362220
}
363221

364222
public static void releaseBuffer(MessageBuffer buffer) {
365-
if(buffer.base instanceof byte[]) {
223+
if(isUniversalBuffer || buffer.base instanceof byte[]) {
366224
// We have nothing to do. Wait until the garbage-collector collects this array object
367225
}
368226
else if(DirectBufferAccess.isDirectByteBufferInstance(buffer.base)) {
@@ -394,6 +252,9 @@ else if(DirectBufferAccess.isDirectByteBufferInstance(buffer.base)) {
394252
*/
395253
MessageBuffer(ByteBuffer bb) {
396254
if(bb.isDirect()) {
255+
if(isUniversalBuffer) {
256+
throw new IllegalStateException("Cannot create MessageBuffer from DirectBuffer");
257+
}
397258
// Direct buffer or off-heap memory
398259
this.base = null;
399260
this.address = DirectBufferAccess.getAddress(bb);

0 commit comments

Comments
 (0)