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}