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}