View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.util;
19  
20  import java.lang.reflect.Field;
21  import java.nio.ByteBuffer;
22  import java.nio.ByteOrder;
23  import java.security.AccessController;
24  import java.security.PrivilegedAction;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.classification.InterfaceStability;
30  
31  import sun.misc.Unsafe;
32  import sun.nio.ch.DirectBuffer;
33  
34  @InterfaceAudience.Private
35  @InterfaceStability.Evolving
36  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="REC_CATCH_EXCEPTION",
37    justification="If exception, presume unaligned")
38  public final class UnsafeAccess {
39  
40    private static final Log LOG = LogFactory.getLog(UnsafeAccess.class);
41  
42    public static final Unsafe theUnsafe;
43  
44    /** The offset to the first element in a byte array. */
45    public static final long BYTE_ARRAY_BASE_OFFSET;
46  
47    public static final boolean LITTLE_ENDIAN =
48      ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN);
49  
50    // This number limits the number of bytes to copy per call to Unsafe's
51    // copyMemory method. A limit is imposed to allow for safepoint polling
52    // during a large copy
53    static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
54    static {
55      theUnsafe = (Unsafe) AccessController.doPrivileged(new PrivilegedAction<Object>() {
56        @Override
57        public Object run() {
58          try {
59            Field f = Unsafe.class.getDeclaredField("theUnsafe");
60            f.setAccessible(true);
61            return f.get(null);
62          } catch (Throwable e) {
63            LOG.warn("sun.misc.Unsafe is not accessible", e);
64          }
65          return null;
66        }
67      });
68  
69      if(theUnsafe != null){
70        BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);
71      } else{
72        BYTE_ARRAY_BASE_OFFSET = -1;
73      }
74    }
75  
76    private UnsafeAccess(){}
77  
78    // APIs to copy data. This will be direct memory location copy and will be much faster
79    /**
80     * Copies the bytes from given array's offset to length part into the given buffer.
81     * @param src
82     * @param srcOffset
83     * @param dest
84     * @param destOffset
85     * @param length
86     */
87    public static void copy(byte[] src, int srcOffset, ByteBuffer dest, int destOffset, int length) {
88      long destAddress = destOffset;
89      Object destBase = null;
90      if (dest.isDirect()) {
91        destAddress = destAddress + ((DirectBuffer) dest).address();
92      } else {
93        destAddress = destAddress + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset();
94        destBase = dest.array();
95      }
96      long srcAddress = (long) srcOffset + BYTE_ARRAY_BASE_OFFSET;
97      unsafeCopy(src, srcAddress, destBase, destAddress, length);
98    }
99  
100   private static void unsafeCopy(Object src, long srcAddr, Object dst, long destAddr, long len) {
101     while (len > 0) {
102       long size = (len > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : len;
103       theUnsafe.copyMemory(src, srcAddr, dst, destAddr, size);
104       len -= size;
105       srcAddr += size;
106       destAddr += size;
107     }
108   }
109 
110   /**
111    * Copies specified number of bytes from given offset of {@code src} ByteBuffer to the
112    * {@code dest} array.
113    *
114    * @param src
115    * @param srcOffset
116    * @param dest
117    * @param destOffset
118    * @param length
119    */
120   public static void copy(ByteBuffer src, int srcOffset, byte[] dest, int destOffset,
121       int length) {
122     long srcAddress = srcOffset;
123     Object srcBase = null;
124     if (src.isDirect()) {
125       srcAddress = srcAddress + ((DirectBuffer) src).address();
126     } else {
127       srcAddress = srcAddress + BYTE_ARRAY_BASE_OFFSET + src.arrayOffset();
128       srcBase = src.array();
129     }
130     long destAddress = (long) destOffset + BYTE_ARRAY_BASE_OFFSET;
131     unsafeCopy(srcBase, srcAddress, dest, destAddress, length);
132   }
133 
134   /**
135    * Copies specified number of bytes from given offset of {@code src} buffer into the {@code dest}
136    * buffer.
137    *
138    * @param src
139    * @param srcOffset
140    * @param dest
141    * @param destOffset
142    * @param length
143    */
144   public static void copy(ByteBuffer src, int srcOffset, ByteBuffer dest, int destOffset,
145       int length) {
146     long srcAddress, destAddress;
147     Object srcBase = null, destBase = null;
148     if (src.isDirect()) {
149       srcAddress = srcOffset + ((DirectBuffer) src).address();
150     } else {
151       srcAddress = (long) srcOffset +  src.arrayOffset() + BYTE_ARRAY_BASE_OFFSET;
152       srcBase = src.array();
153     }
154     if (dest.isDirect()) {
155       destAddress = destOffset + ((DirectBuffer) dest).address();
156     } else {
157       destAddress = (long) destOffset + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset();
158       destBase = dest.array();
159     }
160     unsafeCopy(srcBase, srcAddress, destBase, destAddress, length);
161   }
162 
163   // APIs to read primitive data from a byte[] using Unsafe way
164   /**
165    * Converts a byte array to a short value considering it was written in big-endian format.
166    * @param bytes byte array
167    * @param offset offset into array
168    * @return the short value
169    */
170   public static short toShort(byte[] bytes, int offset) {
171     if (LITTLE_ENDIAN) {
172       return Short.reverseBytes(theUnsafe.getShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET));
173     } else {
174       return theUnsafe.getShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET);
175     }
176   }
177 
178   /**
179    * Converts a byte array to an int value considering it was written in big-endian format.
180    * @param bytes byte array
181    * @param offset offset into array
182    * @return the int value
183    */
184   public static int toInt(byte[] bytes, int offset) {
185     if (LITTLE_ENDIAN) {
186       return Integer.reverseBytes(theUnsafe.getInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET));
187     } else {
188       return theUnsafe.getInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET);
189     }
190   }
191 
192   /**
193    * Converts a byte array to a long value considering it was written in big-endian format.
194    * @param bytes byte array
195    * @param offset offset into array
196    * @return the long value
197    */
198   public static long toLong(byte[] bytes, int offset) {
199     if (LITTLE_ENDIAN) {
200       return Long.reverseBytes(theUnsafe.getLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET));
201     } else {
202       return theUnsafe.getLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET);
203     }
204   }
205 
206   // APIs to write primitive data to a byte[] using Unsafe way
207   /**
208    * Put a short value out to the specified byte array position in big-endian format.
209    * @param bytes the byte array
210    * @param offset position in the array
211    * @param val short to write out
212    * @return incremented offset
213    */
214   public static int putShort(byte[] bytes, int offset, short val) {
215     if (LITTLE_ENDIAN) {
216       val = Short.reverseBytes(val);
217     }
218     theUnsafe.putShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
219     return offset + Bytes.SIZEOF_SHORT;
220   }
221 
222   /**
223    * Put an int value out to the specified byte array position in big-endian format.
224    * @param bytes the byte array
225    * @param offset position in the array
226    * @param val int to write out
227    * @return incremented offset
228    */
229   public static int putInt(byte[] bytes, int offset, int val) {
230     if (LITTLE_ENDIAN) {
231       val = Integer.reverseBytes(val);
232     }
233     theUnsafe.putInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
234     return offset + Bytes.SIZEOF_INT;
235   }
236 
237   /**
238    * Put a long value out to the specified byte array position in big-endian format.
239    * @param bytes the byte array
240    * @param offset position in the array
241    * @param val long to write out
242    * @return incremented offset
243    */
244   public static int putLong(byte[] bytes, int offset, long val) {
245     if (LITTLE_ENDIAN) {
246       val = Long.reverseBytes(val);
247     }
248     theUnsafe.putLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
249     return offset + Bytes.SIZEOF_LONG;
250   }
251 
252   // APIs to read primitive data from a ByteBuffer using Unsafe way
253   /**
254    * Reads a short value at the given buffer's offset considering it was written in big-endian
255    * format.
256    *
257    * @param buf
258    * @param offset
259    * @return short value at offset
260    */
261   public static short toShort(ByteBuffer buf, int offset) {
262     if (LITTLE_ENDIAN) {
263       return Short.reverseBytes(getAsShort(buf, offset));
264     }
265     return getAsShort(buf, offset);
266   }
267 
268   /**
269    * Reads bytes at the given offset as a short value.
270    * @param buf
271    * @param offset
272    * @return short value at offset
273    */
274   static short getAsShort(ByteBuffer buf, int offset) {
275     if (buf.isDirect()) {
276       return theUnsafe.getShort(((DirectBuffer) buf).address() + offset);
277     }
278     return theUnsafe.getShort(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
279   }
280 
281   /**
282    * Reads an int value at the given buffer's offset considering it was written in big-endian
283    * format.
284    *
285    * @param buf
286    * @param offset
287    * @return int value at offset
288    */
289   public static int toInt(ByteBuffer buf, int offset) {
290     if (LITTLE_ENDIAN) {
291       return Integer.reverseBytes(getAsInt(buf, offset));
292     }
293     return getAsInt(buf, offset);
294   }
295 
296   /**
297    * Reads bytes at the given offset as an int value.
298    * @param buf
299    * @param offset
300    * @return int value at offset
301    */
302   static int getAsInt(ByteBuffer buf, int offset) {
303     if (buf.isDirect()) {
304       return theUnsafe.getInt(((DirectBuffer) buf).address() + offset);
305     }
306     return theUnsafe.getInt(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
307   }
308 
309   /**
310    * Reads a long value at the given buffer's offset considering it was written in big-endian
311    * format.
312    *
313    * @param buf
314    * @param offset
315    * @return long value at offset
316    */
317   public static long toLong(ByteBuffer buf, int offset) {
318     if (LITTLE_ENDIAN) {
319       return Long.reverseBytes(getAsLong(buf, offset));
320     }
321     return getAsLong(buf, offset);
322   }
323 
324   /**
325    * Reads bytes at the given offset as a long value.
326    * @param buf
327    * @param offset
328    * @return long value at offset
329    */
330   static long getAsLong(ByteBuffer buf, int offset) {
331     if (buf.isDirect()) {
332       return theUnsafe.getLong(((DirectBuffer) buf).address() + offset);
333     }
334     return theUnsafe.getLong(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
335   }
336 
337   /**
338    * Put an int value out to the specified ByteBuffer offset in big-endian format.
339    * @param buf the ByteBuffer to write to
340    * @param offset offset in the ByteBuffer
341    * @param val int to write out
342    * @return incremented offset
343    */
344   public static int putInt(ByteBuffer buf, int offset, int val) {
345     if (LITTLE_ENDIAN) {
346       val = Integer.reverseBytes(val);
347     }
348     if (buf.isDirect()) {
349       theUnsafe.putInt(((DirectBuffer) buf).address() + offset, val);
350     } else {
351       theUnsafe.putInt(buf.array(), offset + buf.arrayOffset() + BYTE_ARRAY_BASE_OFFSET, val);
352     }
353     return offset + Bytes.SIZEOF_INT;
354   }
355 
356   // APIs to add primitives to BBs
357   /**
358    * Put a short value out to the specified BB position in big-endian format.
359    * @param buf the byte buffer
360    * @param offset position in the buffer
361    * @param val short to write out
362    * @return incremented offset
363    */
364   public static int putShort(ByteBuffer buf, int offset, short val) {
365     if (LITTLE_ENDIAN) {
366       val = Short.reverseBytes(val);
367     }
368     if (buf.isDirect()) {
369       theUnsafe.putShort(((DirectBuffer) buf).address() + offset, val);
370     } else {
371       theUnsafe.putShort(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, val);
372     }
373     return offset + Bytes.SIZEOF_SHORT;
374   }
375 
376   /**
377    * Put a long value out to the specified BB position in big-endian format.
378    * @param buf the byte buffer
379    * @param offset position in the buffer
380    * @param val long to write out
381    * @return incremented offset
382    */
383   public static int putLong(ByteBuffer buf, int offset, long val) {
384     if (LITTLE_ENDIAN) {
385       val = Long.reverseBytes(val);
386     }
387     if (buf.isDirect()) {
388       theUnsafe.putLong(((DirectBuffer) buf).address() + offset, val);
389     } else {
390       theUnsafe.putLong(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, val);
391     }
392     return offset + Bytes.SIZEOF_LONG;
393   }
394 
395   /**
396    * Put a byte value out to the specified BB position in big-endian format.
397    * @param buf the byte buffer
398    * @param offset position in the buffer
399    * @param b byte to write out
400    * @return incremented offset
401    */
402   public static int putByte(ByteBuffer buf, int offset, byte b) {
403     if (buf.isDirect()) {
404       theUnsafe.putByte(((DirectBuffer) buf).address() + offset, b);
405     } else {
406       theUnsafe.putByte(buf.array(),
407         BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, b);
408     }
409     return offset + 1;
410   }
411 
412   /**
413    * Returns the byte at the given offset
414    * @param buf the buffer to read
415    * @param offset the offset at which the byte has to be read
416    * @return the byte at the given offset
417    */
418   public static byte toByte(ByteBuffer buf, int offset) {
419     if (buf.isDirect()) {
420       return theUnsafe.getByte(((DirectBuffer) buf).address() + offset);
421     } else {
422       return theUnsafe.getByte(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
423     }
424   }
425 }