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 a long value considering it was written in big-endian format.
086   * @param bytes  byte array
087   * @param offset offset into array
088   * @return the long value
089   */
090  public static long toLong(byte[] bytes, int offset) {
091    if (LITTLE_ENDIAN) {
092      return Long
093        .reverseBytes(HBasePlatformDependent.getLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET));
094    } else {
095      return HBasePlatformDependent.getLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET);
096    }
097  }
098
099  // APIs to write primitive data to a byte[] using Unsafe way
100  /**
101   * Put a short value out to the specified byte array position in big-endian format.
102   * @param bytes  the byte array
103   * @param offset position in the array
104   * @param val    short to write out
105   * @return incremented offset
106   */
107  public static int putShort(byte[] bytes, int offset, short val) {
108    if (LITTLE_ENDIAN) {
109      val = Short.reverseBytes(val);
110    }
111    HBasePlatformDependent.putShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
112    return offset + Bytes.SIZEOF_SHORT;
113  }
114
115  /**
116   * Put an int 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    int to write out
120   * @return incremented offset
121   */
122  public static int putInt(byte[] bytes, int offset, int val) {
123    if (LITTLE_ENDIAN) {
124      val = Integer.reverseBytes(val);
125    }
126    HBasePlatformDependent.putInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
127    return offset + Bytes.SIZEOF_INT;
128  }
129
130  /**
131   * Put a long 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    long to write out
135   * @return incremented offset
136   */
137  public static int putLong(byte[] bytes, int offset, long val) {
138    if (LITTLE_ENDIAN) {
139      val = Long.reverseBytes(val);
140    }
141    HBasePlatformDependent.putLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val);
142    return offset + Bytes.SIZEOF_LONG;
143  }
144
145  // APIs to read primitive data from a ByteBuffer using Unsafe way
146  /**
147   * Reads a short value at the given buffer's offset considering it was written in big-endian
148   * format.
149   * @return short value at offset
150   */
151  public static short toShort(ByteBuffer buf, int offset) {
152    if (LITTLE_ENDIAN) {
153      return Short.reverseBytes(getAsShort(buf, offset));
154    }
155    return getAsShort(buf, offset);
156  }
157
158  /**
159   * Reads a short value at the given Object's offset considering it was written in big-endian
160   * format.
161   * @return short value at offset
162   */
163  public static short toShort(Object ref, long offset) {
164    if (LITTLE_ENDIAN) {
165      return Short.reverseBytes(HBasePlatformDependent.getShort(ref, offset));
166    }
167    return HBasePlatformDependent.getShort(ref, offset);
168  }
169
170  /**
171   * Reads bytes at the given offset as a short value.
172   * @return short value at offset
173   */
174  private static short getAsShort(ByteBuffer buf, int offset) {
175    if (buf.isDirect()) {
176      return HBasePlatformDependent.getShort(directBufferAddress(buf) + offset);
177    }
178    return HBasePlatformDependent.getShort(buf.array(),
179      BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
180  }
181
182  /**
183   * Reads an int value at the given buffer's offset considering it was written in big-endian
184   * format.
185   * @return int value at offset
186   */
187  public static int toInt(ByteBuffer buf, int offset) {
188    if (LITTLE_ENDIAN) {
189      return Integer.reverseBytes(getAsInt(buf, offset));
190    }
191    return getAsInt(buf, offset);
192  }
193
194  /**
195   * Reads a int value at the given Object's offset considering it was written in big-endian format.
196   * @return int value at offset
197   */
198  public static int toInt(Object ref, long offset) {
199    if (LITTLE_ENDIAN) {
200      return Integer.reverseBytes(HBasePlatformDependent.getInt(ref, offset));
201    }
202    return HBasePlatformDependent.getInt(ref, offset);
203  }
204
205  /**
206   * Reads bytes at the given offset as an int value.
207   * @return int value at offset
208   */
209  private static int getAsInt(ByteBuffer buf, int offset) {
210    if (buf.isDirect()) {
211      return HBasePlatformDependent.getInt(directBufferAddress(buf) + offset);
212    }
213    return HBasePlatformDependent.getInt(buf.array(),
214      BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
215  }
216
217  /**
218   * Reads a long value at the given buffer's offset considering it was written in big-endian
219   * format.
220   * @return long value at offset
221   */
222  public static long toLong(ByteBuffer buf, int offset) {
223    if (LITTLE_ENDIAN) {
224      return Long.reverseBytes(getAsLong(buf, offset));
225    }
226    return getAsLong(buf, offset);
227  }
228
229  /**
230   * Reads a long value at the given Object's offset considering it was written in big-endian
231   * format.
232   * @return long value at offset
233   */
234  public static long toLong(Object ref, long offset) {
235    if (LITTLE_ENDIAN) {
236      return Long.reverseBytes(HBasePlatformDependent.getLong(ref, offset));
237    }
238    return HBasePlatformDependent.getLong(ref, offset);
239  }
240
241  /**
242   * Reads bytes at the given offset as a long value.
243   * @return long value at offset
244   */
245  private static long getAsLong(ByteBuffer buf, int offset) {
246    if (buf.isDirect()) {
247      return HBasePlatformDependent.getLong(directBufferAddress(buf) + offset);
248    }
249    return HBasePlatformDependent.getLong(buf.array(),
250      BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
251  }
252
253  /**
254   * Returns the byte at the given offset
255   * @param buf    the buffer to read
256   * @param offset the offset at which the byte has to be read
257   * @return the byte at the given offset
258   */
259  public static byte toByte(ByteBuffer buf, int offset) {
260    if (buf.isDirect()) {
261      return HBasePlatformDependent.getByte(directBufferAddress(buf) + offset);
262    } else {
263      return HBasePlatformDependent.getByte(buf.array(),
264        BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
265    }
266  }
267
268  /**
269   * Returns the byte at the given offset of the object
270   * @return the byte at the given offset
271   */
272  public static byte toByte(Object ref, long offset) {
273    return HBasePlatformDependent.getByte(ref, offset);
274  }
275
276  /**
277   * Put an int value out to the specified ByteBuffer offset in big-endian format.
278   * @param buf    the ByteBuffer to write to
279   * @param offset offset in the ByteBuffer
280   * @param val    int to write out
281   * @return incremented offset
282   */
283  public static int putInt(ByteBuffer buf, int offset, int val) {
284    if (LITTLE_ENDIAN) {
285      val = Integer.reverseBytes(val);
286    }
287    if (buf.isDirect()) {
288      HBasePlatformDependent.putInt(directBufferAddress(buf) + offset, val);
289    } else {
290      HBasePlatformDependent.putInt(buf.array(),
291        offset + buf.arrayOffset() + BYTE_ARRAY_BASE_OFFSET, val);
292    }
293    return offset + Bytes.SIZEOF_INT;
294  }
295
296  // APIs to copy data. This will be direct memory location copy and will be much faster
297  /**
298   * Copies the bytes from given array's offset to length part into the given buffer.
299   * @param src        source array
300   * @param srcOffset  offset into source buffer
301   * @param dest       destination buffer
302   * @param destOffset offset into destination buffer
303   * @param length     length of data to copy
304   */
305  public static void copy(byte[] src, int srcOffset, ByteBuffer dest, int destOffset, int length) {
306    long destAddress = destOffset;
307    Object destBase = null;
308    if (dest.isDirect()) {
309      destAddress = destAddress + directBufferAddress(dest);
310    } else {
311      destAddress = destAddress + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset();
312      destBase = dest.array();
313    }
314    long srcAddress = srcOffset + BYTE_ARRAY_BASE_OFFSET;
315    unsafeCopy(src, srcAddress, destBase, destAddress, length);
316  }
317
318  private static void unsafeCopy(Object src, long srcAddr, Object dst, long destAddr, long len) {
319    while (len > 0) {
320      long size = (len > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : len;
321      HBasePlatformDependent.copyMemory(src, srcAddr, dst, destAddr, size);
322      len -= size;
323      srcAddr += size;
324      destAddr += size;
325    }
326  }
327
328  /**
329   * Copies specified number of bytes from given offset of {@code src} ByteBuffer to the
330   * {@code dest} array.
331   * @param src        source buffer
332   * @param srcOffset  offset into source buffer
333   * @param dest       destination array
334   * @param destOffset offset into destination buffer
335   * @param length     length of data to copy
336   */
337  public static void copy(ByteBuffer src, int srcOffset, byte[] dest, int destOffset, int length) {
338    long srcAddress = srcOffset;
339    Object srcBase = null;
340    if (src.isDirect()) {
341      srcAddress = srcAddress + directBufferAddress(src);
342    } else {
343      srcAddress = srcAddress + BYTE_ARRAY_BASE_OFFSET + src.arrayOffset();
344      srcBase = src.array();
345    }
346    long destAddress = destOffset + BYTE_ARRAY_BASE_OFFSET;
347    unsafeCopy(srcBase, srcAddress, dest, destAddress, length);
348  }
349
350  /**
351   * Copies specified number of bytes from given offset of {@code src} buffer into the {@code dest}
352   * buffer.
353   * @param src        source buffer
354   * @param srcOffset  offset into source buffer
355   * @param dest       destination buffer
356   * @param destOffset offset into destination buffer
357   * @param length     length of data to copy
358   */
359  public static void copy(ByteBuffer src, int srcOffset, ByteBuffer dest, int destOffset,
360    int length) {
361    long srcAddress, destAddress;
362    Object srcBase = null, destBase = null;
363    if (src.isDirect()) {
364      srcAddress = srcOffset + directBufferAddress(src);
365    } else {
366      srcAddress = (long) srcOffset + src.arrayOffset() + BYTE_ARRAY_BASE_OFFSET;
367      srcBase = src.array();
368    }
369    if (dest.isDirect()) {
370      destAddress = destOffset + directBufferAddress(dest);
371    } else {
372      destAddress = destOffset + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset();
373      destBase = dest.array();
374    }
375    unsafeCopy(srcBase, srcAddress, destBase, destAddress, length);
376  }
377
378  // APIs to add primitives to BBs
379  /**
380   * Put a short value out to the specified BB position in big-endian format.
381   * @param buf    the byte buffer
382   * @param offset position in the buffer
383   * @param val    short to write out
384   * @return incremented offset
385   */
386  public static int putShort(ByteBuffer buf, int offset, short val) {
387    if (LITTLE_ENDIAN) {
388      val = Short.reverseBytes(val);
389    }
390    if (buf.isDirect()) {
391      HBasePlatformDependent.putShort(directBufferAddress(buf) + offset, val);
392    } else {
393      HBasePlatformDependent.putShort(buf.array(),
394        BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, val);
395    }
396    return offset + Bytes.SIZEOF_SHORT;
397  }
398
399  /**
400   * Put a long value out to the specified BB position in big-endian format.
401   * @param buf    the byte buffer
402   * @param offset position in the buffer
403   * @param val    long to write out
404   * @return incremented offset
405   */
406  public static int putLong(ByteBuffer buf, int offset, long val) {
407    if (LITTLE_ENDIAN) {
408      val = Long.reverseBytes(val);
409    }
410    if (buf.isDirect()) {
411      HBasePlatformDependent.putLong(directBufferAddress(buf) + offset, val);
412    } else {
413      HBasePlatformDependent.putLong(buf.array(),
414        BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, val);
415    }
416    return offset + Bytes.SIZEOF_LONG;
417  }
418
419  /**
420   * Put a byte value out to the specified BB position in big-endian format.
421   * @param buf    the byte buffer
422   * @param offset position in the buffer
423   * @param b      byte to write out
424   * @return incremented offset
425   */
426  public static int putByte(ByteBuffer buf, int offset, byte b) {
427    if (buf.isDirect()) {
428      HBasePlatformDependent.putByte(directBufferAddress(buf) + offset, b);
429    } else {
430      HBasePlatformDependent.putByte(buf.array(),
431        BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, b);
432    }
433    return offset + 1;
434  }
435
436  public static long directBufferAddress(ByteBuffer buf) {
437    return PlatformDependent.directBufferAddress(buf);
438  }
439
440  public static void freeDirectBuffer(ByteBuffer buffer) {
441    // here we just use the method in netty
442    PlatformDependent.freeDirectBuffer(buffer);
443  }
444}