001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import java.nio.ByteBuffer;
021import java.nio.ByteOrder;
022import org.apache.hadoop.hbase.unsafe.HBasePlatformDependent;
023import org.apache.yetus.audience.InterfaceAudience;
024import org.apache.yetus.audience.InterfaceStability;
025
026import org.apache.hbase.thirdparty.io.netty.util.internal.PlatformDependent;
027
028@InterfaceAudience.Private
029@InterfaceStability.Evolving
030public final class UnsafeAccess {
031
032  /** The offset to the first element in a byte array. */
033  public static final long BYTE_ARRAY_BASE_OFFSET;
034
035  public static final boolean LITTLE_ENDIAN =
036    ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN);
037
038  // This number limits the number of bytes to copy per call to Unsafe's
039  // copyMemory method. A limit is imposed to allow for safepoint polling
040  // during a large copy
041  static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
042  static {
043    if (HBasePlatformDependent.isUnsafeAvailable()) {
044      BYTE_ARRAY_BASE_OFFSET = HBasePlatformDependent.arrayBaseOffset(byte[].class);
045    } else {
046      BYTE_ARRAY_BASE_OFFSET = -1;
047    }
048  }
049
050  private UnsafeAccess() {
051  }
052
053  // APIs to read primitive data from a byte[] using Unsafe way
054  /**
055   * Converts a byte array to a short value considering it was written in big-endian format.
056   * @param bytes  byte array
057   * @param offset offset into array
058   * @return the short value
059   */
060  public static short toShort(byte[] bytes, int offset) {
061    if (LITTLE_ENDIAN) {
062      return Short
063        .reverseBytes(HBasePlatformDependent.getShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET));
064    } else {
065      return HBasePlatformDependent.getShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET);
066    }
067  }
068
069  /**
070   * Converts a byte array to an int value considering it was written in big-endian format.
071   * @param bytes  byte array
072   * @param offset offset into array
073   * @return the int value
074   */
075  public static int toInt(byte[] bytes, int offset) {
076    if (LITTLE_ENDIAN) {
077      return Integer
078        .reverseBytes(HBasePlatformDependent.getInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET));
079    } else {
080      return HBasePlatformDependent.getInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET);
081    }
082  }
083
084  /**
085   * Converts a byte array to an int value considering it was written in little-endian format.
086   * @param bytes  byte array
087   * @param offset offset into array
088   * @return the int value
089   */
090  public static int toIntLE(byte[] bytes, int offset) {
091    if (LITTLE_ENDIAN) {
092      return HBasePlatformDependent.getInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET);
093    } else {
094      return Integer
095        .reverseBytes(HBasePlatformDependent.getInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET));
096    }
097  }
098
099  /**
100   * Converts a byte array to a long value considering it was written in big-endian format.
101   * @param bytes  byte array
102   * @param offset offset into array
103   * @return the long value
104   */
105  public static long toLong(byte[] bytes, int offset) {
106    if (LITTLE_ENDIAN) {
107      return Long
108        .reverseBytes(HBasePlatformDependent.getLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET));
109    } else {
110      return HBasePlatformDependent.getLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET);
111    }
112  }
113
114  // APIs to write primitive data to a byte[] using Unsafe way
115  /**
116   * Put a short value out to the specified byte array position in big-endian format.
117   * @param bytes  the byte array
118   * @param offset position in the array
119   * @param val    short to write out
120   * @return incremented offset
121   */
122  public static int putShort(byte[] bytes, int offset, short val) {
123    if (LITTLE_ENDIAN) {
124      val = Short.reverseBytes(val);
125    }
126    HBasePlatformDependent.putShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
127    return offset + Bytes.SIZEOF_SHORT;
128  }
129
130  /**
131   * Put an int value out to the specified byte array position in big-endian format.
132   * @param bytes  the byte array
133   * @param offset position in the array
134   * @param val    int to write out
135   * @return incremented offset
136   */
137  public static int putInt(byte[] bytes, int offset, int val) {
138    if (LITTLE_ENDIAN) {
139      val = Integer.reverseBytes(val);
140    }
141    HBasePlatformDependent.putInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
142    return offset + Bytes.SIZEOF_INT;
143  }
144
145  /**
146   * Put an int value out to the specified byte array position in little-endian format.
147   * @param bytes  the byte array
148   * @param offset position in the array
149   * @param val    int to write out
150   * @return incremented offset
151   */
152  public static int putIntLE(byte[] bytes, int offset, int val) {
153    if (!LITTLE_ENDIAN) {
154      val = Integer.reverseBytes(val);
155    }
156    HBasePlatformDependent.putInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
157    return offset + Bytes.SIZEOF_INT;
158  }
159
160  /**
161   * Put a long value out to the specified byte array position in big-endian format.
162   * @param bytes  the byte array
163   * @param offset position in the array
164   * @param val    long to write out
165   * @return incremented offset
166   */
167  public static int putLong(byte[] bytes, int offset, long val) {
168    if (LITTLE_ENDIAN) {
169      val = Long.reverseBytes(val);
170    }
171    HBasePlatformDependent.putLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
172    return offset + Bytes.SIZEOF_LONG;
173  }
174
175  // APIs to read primitive data from a ByteBuffer using Unsafe way
176  /**
177   * Reads a short value at the given buffer's offset considering it was written in big-endian
178   * format.
179   * @return short value at offset
180   */
181  public static short toShort(ByteBuffer buf, int offset) {
182    if (LITTLE_ENDIAN) {
183      return Short.reverseBytes(getAsShort(buf, offset));
184    }
185    return getAsShort(buf, offset);
186  }
187
188  /**
189   * Reads a short value at the given Object's offset considering it was written in big-endian
190   * format.
191   * @return short value at offset
192   */
193  public static short toShort(Object ref, long offset) {
194    if (LITTLE_ENDIAN) {
195      return Short.reverseBytes(HBasePlatformDependent.getShort(ref, offset));
196    }
197    return HBasePlatformDependent.getShort(ref, offset);
198  }
199
200  /**
201   * Reads bytes at the given offset as a short value.
202   * @return short value at offset
203   */
204  private static short getAsShort(ByteBuffer buf, int offset) {
205    if (buf.isDirect()) {
206      return HBasePlatformDependent.getShort(directBufferAddress(buf) + offset);
207    }
208    return HBasePlatformDependent.getShort(buf.array(),
209      BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
210  }
211
212  /**
213   * Reads an int value at the given buffer's offset considering it was written in big-endian
214   * format.
215   * @return int value at offset
216   */
217  public static int toInt(ByteBuffer buf, int offset) {
218    if (LITTLE_ENDIAN) {
219      return Integer.reverseBytes(getAsInt(buf, offset));
220    }
221    return getAsInt(buf, offset);
222  }
223
224  /**
225   * Reads an int value at the given buffer's offset considering it was written in little-endian
226   * format.
227   * @return int value at offset
228   */
229  public static int toIntLE(ByteBuffer buf, int offset) {
230    if (LITTLE_ENDIAN) {
231      return getAsInt(buf, offset);
232    }
233    return Integer.reverseBytes(getAsInt(buf, offset));
234  }
235
236  /**
237   * Reads a int value at the given Object's offset considering it was written in big-endian format.
238   * @return int value at offset
239   */
240  public static int toInt(Object ref, long offset) {
241    if (LITTLE_ENDIAN) {
242      return Integer.reverseBytes(HBasePlatformDependent.getInt(ref, offset));
243    }
244    return HBasePlatformDependent.getInt(ref, offset);
245  }
246
247  /**
248   * Reads bytes at the given offset as an int value.
249   * @return int value at offset
250   */
251  private static int getAsInt(ByteBuffer buf, int offset) {
252    if (buf.isDirect()) {
253      return HBasePlatformDependent.getInt(directBufferAddress(buf) + offset);
254    }
255    return HBasePlatformDependent.getInt(buf.array(),
256      BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
257  }
258
259  /**
260   * Reads a long value at the given buffer's offset considering it was written in big-endian
261   * format.
262   * @return long value at offset
263   */
264  public static long toLong(ByteBuffer buf, int offset) {
265    if (LITTLE_ENDIAN) {
266      return Long.reverseBytes(getAsLong(buf, offset));
267    }
268    return getAsLong(buf, offset);
269  }
270
271  /**
272   * Reads a long value at the given Object's offset considering it was written in big-endian
273   * format.
274   * @return long value at offset
275   */
276  public static long toLong(Object ref, long offset) {
277    if (LITTLE_ENDIAN) {
278      return Long.reverseBytes(HBasePlatformDependent.getLong(ref, offset));
279    }
280    return HBasePlatformDependent.getLong(ref, offset);
281  }
282
283  /**
284   * Reads bytes at the given offset as a long value.
285   * @return long value at offset
286   */
287  private static long getAsLong(ByteBuffer buf, int offset) {
288    if (buf.isDirect()) {
289      return HBasePlatformDependent.getLong(directBufferAddress(buf) + offset);
290    }
291    return HBasePlatformDependent.getLong(buf.array(),
292      BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
293  }
294
295  /**
296   * Returns the byte at the given offset
297   * @param buf    the buffer to read
298   * @param offset the offset at which the byte has to be read
299   * @return the byte at the given offset
300   */
301  public static byte toByte(ByteBuffer buf, int offset) {
302    if (buf.isDirect()) {
303      return HBasePlatformDependent.getByte(directBufferAddress(buf) + offset);
304    } else {
305      return HBasePlatformDependent.getByte(buf.array(),
306        BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
307    }
308  }
309
310  /**
311   * Returns the byte at the given offset of the object
312   * @return the byte at the given offset
313   */
314  public static byte toByte(Object ref, long offset) {
315    return HBasePlatformDependent.getByte(ref, offset);
316  }
317
318  /**
319   * Put an int value out to the specified ByteBuffer offset in big-endian format.
320   * @param buf    the ByteBuffer to write to
321   * @param offset offset in the ByteBuffer
322   * @param val    int to write out
323   * @return incremented offset
324   */
325  public static int putInt(ByteBuffer buf, int offset, int val) {
326    if (LITTLE_ENDIAN) {
327      val = Integer.reverseBytes(val);
328    }
329    if (buf.isDirect()) {
330      HBasePlatformDependent.putInt(directBufferAddress(buf) + offset, val);
331    } else {
332      HBasePlatformDependent.putInt(buf.array(),
333        offset + buf.arrayOffset() + BYTE_ARRAY_BASE_OFFSET, val);
334    }
335    return offset + Bytes.SIZEOF_INT;
336  }
337
338  // APIs to copy data. This will be direct memory location copy and will be much faster
339  /**
340   * Copies the bytes from given array's offset to length part into the given buffer.
341   * @param src        source array
342   * @param srcOffset  offset into source buffer
343   * @param dest       destination buffer
344   * @param destOffset offset into destination buffer
345   * @param length     length of data to copy
346   */
347  public static void copy(byte[] src, int srcOffset, ByteBuffer dest, int destOffset, int length) {
348    long destAddress = destOffset;
349    Object destBase = null;
350    if (dest.isDirect()) {
351      destAddress = destAddress + directBufferAddress(dest);
352    } else {
353      destAddress = destAddress + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset();
354      destBase = dest.array();
355    }
356    long srcAddress = srcOffset + BYTE_ARRAY_BASE_OFFSET;
357    unsafeCopy(src, srcAddress, destBase, destAddress, length);
358  }
359
360  private static void unsafeCopy(Object src, long srcAddr, Object dst, long destAddr, long len) {
361    while (len > 0) {
362      long size = (len > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : len;
363      HBasePlatformDependent.copyMemory(src, srcAddr, dst, destAddr, size);
364      len -= size;
365      srcAddr += size;
366      destAddr += size;
367    }
368  }
369
370  /**
371   * Copies specified number of bytes from given offset of {@code src} ByteBuffer to the
372   * {@code dest} array.
373   * @param src        source buffer
374   * @param srcOffset  offset into source buffer
375   * @param dest       destination array
376   * @param destOffset offset into destination buffer
377   * @param length     length of data to copy
378   */
379  public static void copy(ByteBuffer src, int srcOffset, byte[] dest, int destOffset, int length) {
380    long srcAddress = srcOffset;
381    Object srcBase = null;
382    if (src.isDirect()) {
383      srcAddress = srcAddress + directBufferAddress(src);
384    } else {
385      srcAddress = srcAddress + BYTE_ARRAY_BASE_OFFSET + src.arrayOffset();
386      srcBase = src.array();
387    }
388    long destAddress = destOffset + BYTE_ARRAY_BASE_OFFSET;
389    unsafeCopy(srcBase, srcAddress, dest, destAddress, length);
390  }
391
392  /**
393   * Copies specified number of bytes from given offset of {@code src} buffer into the {@code dest}
394   * buffer.
395   * @param src        source buffer
396   * @param srcOffset  offset into source buffer
397   * @param dest       destination buffer
398   * @param destOffset offset into destination buffer
399   * @param length     length of data to copy
400   */
401  public static void copy(ByteBuffer src, int srcOffset, ByteBuffer dest, int destOffset,
402    int length) {
403    long srcAddress, destAddress;
404    Object srcBase = null, destBase = null;
405    if (src.isDirect()) {
406      srcAddress = srcOffset + directBufferAddress(src);
407    } else {
408      srcAddress = (long) srcOffset + src.arrayOffset() + BYTE_ARRAY_BASE_OFFSET;
409      srcBase = src.array();
410    }
411    if (dest.isDirect()) {
412      destAddress = destOffset + directBufferAddress(dest);
413    } else {
414      destAddress = destOffset + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset();
415      destBase = dest.array();
416    }
417    unsafeCopy(srcBase, srcAddress, destBase, destAddress, length);
418  }
419
420  // APIs to add primitives to BBs
421  /**
422   * Put a short value out to the specified BB position in big-endian format.
423   * @param buf    the byte buffer
424   * @param offset position in the buffer
425   * @param val    short to write out
426   * @return incremented offset
427   */
428  public static int putShort(ByteBuffer buf, int offset, short val) {
429    if (LITTLE_ENDIAN) {
430      val = Short.reverseBytes(val);
431    }
432    if (buf.isDirect()) {
433      HBasePlatformDependent.putShort(directBufferAddress(buf) + offset, val);
434    } else {
435      HBasePlatformDependent.putShort(buf.array(),
436        BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, val);
437    }
438    return offset + Bytes.SIZEOF_SHORT;
439  }
440
441  /**
442   * Put a long value out to the specified BB position in big-endian format.
443   * @param buf    the byte buffer
444   * @param offset position in the buffer
445   * @param val    long to write out
446   * @return incremented offset
447   */
448  public static int putLong(ByteBuffer buf, int offset, long val) {
449    if (LITTLE_ENDIAN) {
450      val = Long.reverseBytes(val);
451    }
452    if (buf.isDirect()) {
453      HBasePlatformDependent.putLong(directBufferAddress(buf) + offset, val);
454    } else {
455      HBasePlatformDependent.putLong(buf.array(),
456        BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, val);
457    }
458    return offset + Bytes.SIZEOF_LONG;
459  }
460
461  /**
462   * Put a byte value out to the specified BB position in big-endian format.
463   * @param buf    the byte buffer
464   * @param offset position in the buffer
465   * @param b      byte to write out
466   * @return incremented offset
467   */
468  public static int putByte(ByteBuffer buf, int offset, byte b) {
469    if (buf.isDirect()) {
470      HBasePlatformDependent.putByte(directBufferAddress(buf) + offset, b);
471    } else {
472      HBasePlatformDependent.putByte(buf.array(),
473        BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, b);
474    }
475    return offset + 1;
476  }
477
478  public static long directBufferAddress(ByteBuffer buf) {
479    return PlatformDependent.directBufferAddress(buf);
480  }
481
482  public static void freeDirectBuffer(ByteBuffer buffer) {
483    // here we just use the method in netty
484    PlatformDependent.freeDirectBuffer(buffer);
485  }
486}