1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import jdk.internal.misc.Unsafe; 25 import java.lang.reflect.Field; 26 27 /* 28 * @test 29 * @summary Test Unsafe.copySwapMemory 30 * @modules java.base/jdk.internal.misc 31 */ 32 public class CopySwap { 33 private static final boolean DEBUG = Boolean.getBoolean("CopySwap.DEBUG"); 34 35 public static final long KB = 1024; 36 public static final long MB = KB * 1024; 37 public static final long GB = MB * 1024; 38 39 private static final Unsafe UNSAFE; 40 private static final int SMALL_COPY_SIZE = 32; 41 private static final int BASE_ALIGNMENT = 16; 42 43 static { 44 try { 45 Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe"); 46 f.setAccessible(true); 47 UNSAFE = (jdk.internal.misc.Unsafe) f.get(null); 48 } catch (Exception e) { 49 throw new RuntimeException("Unable to get Unsafe instance.", e); 50 } 51 } 52 53 private static long alignDown(long value, long alignment) { 54 return value & ~(alignment - 1); 55 } 56 57 private static long alignUp(long value, long alignment) { 58 return (value + alignment - 1) & ~(alignment - 1); 59 } 60 61 private static boolean isAligned(long value, long alignment) { 62 return value == alignDown(value, alignment); 63 } 64 65 private CopySwap() { 66 } 67 68 /** 69 * Generate verification data for a given offset 70 * 71 * The verification data is used to verify that the correct bytes 72 * have indeed been copied and byte swapped. 73 * 74 * The data is generated based on the offset (in bytes) into the 75 * source buffer. For a native buffer the offset is relative to 76 * the base pointer. For a heap array it is relative to the 77 * address of the first array element. 78 * 79 * This method will return the result of doing an elementSize byte 80 * read starting at offset (in bytes). 81 * 82 * @param offset offset into buffer 83 * @param elemSize size (in bytes) of the element 84 * 85 * @return the verification data, only the least significant 86 * elemSize*8 bits are set, zero extended 87 */ 88 private long getVerificationDataForOffset(long offset, long elemSize) { 89 byte[] bytes = new byte[(int)elemSize]; 90 91 for (long i = 0; i < elemSize; i++) { 92 bytes[(int)i] = (byte)(offset + i); 93 } 94 95 long o = UNSAFE.arrayBaseOffset(byte[].class); 96 97 switch ((int)elemSize) { 98 case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o)); 99 case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o)); 100 case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o)); 101 case 8: return UNSAFE.getLongUnaligned(bytes, o); 102 default: throw new IllegalArgumentException("Invalid element size: " + elemSize); 103 } 104 } 105 106 /** 107 * Verify byte swapped data 108 * 109 * @param ptr the data to verify 110 * @param srcOffset the srcOffset (in bytes) from which the copy started, 111 * used as key to regenerate the verification data 112 * @param dstOffset the offset (in bytes) in the array at which to start 113 * the verification, relative to the first element in the array 114 * @param size size (in bytes) of data to to verify 115 * @param elemSize size (in bytes) of the individual array elements 116 * 117 * @throws RuntimeException if an error is found 118 */ 119 private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) { 120 for (long offset = 0; offset < size; offset += elemSize) { 121 long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize); 122 long expected = byteSwap(expectedUnswapped, elemSize); 123 124 long actual = getArrayElem(ptr, dstOffset + offset, elemSize); 125 126 if (expected != actual) { 127 throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) + 128 " dstOffset: 0x" + Long.toHexString(dstOffset) + 129 " size: 0x" + Long.toHexString(size) + 130 " offset: 0x" + Long.toHexString(offset) + 131 " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) + 132 " expected: 0x" + Long.toHexString(expected) + 133 " != actual: 0x" + Long.toHexString(actual)); 134 } 135 } 136 } 137 138 /** 139 * Initialize an array with verification friendly data 140 * 141 * @param ptr pointer to the data to initialize 142 * @param size size (in bytes) of the data 143 * @param elemSize size (in bytes) of the individual elements 144 */ 145 private void initVerificationData(GenericPointer ptr, long size, long elemSize) { 146 for (long offset = 0; offset < size; offset++) { 147 byte data = (byte)getVerificationDataForOffset(offset, 1); 148 149 if (ptr.isOnHeap()) { 150 UNSAFE.putByte(ptr.getObject(), ptr.getOffset() + offset, data); 151 } else { 152 UNSAFE.putByte(ptr.getOffset() + offset, data); 153 } 154 } 155 } 156 157 /** 158 * Allocate a primitive array 159 * 160 * @param size size (in bytes) of all the array elements (elemSize * length) 161 * @param elemSize the size of the array elements 162 * 163 * @return a newly allocated primitive array 164 */ 165 Object allocArray(long size, long elemSize) { 166 int length = (int)(size / elemSize); 167 168 switch ((int)elemSize) { 169 case 2: return new short[length]; 170 case 4: return new int[length]; 171 case 8: return new long[length]; 172 default: 173 throw new IllegalArgumentException("Invalid element size: " + elemSize); 174 } 175 } 176 177 /** 178 * Get the value of a primitive array entry 179 * 180 * @param ptr pointer to the data 181 * @param offset offset (in bytes) of the array element, relative to the first element in the array 182 * 183 * @return the array element, as an unsigned long 184 */ 185 private long getArrayElem(GenericPointer ptr, long offset, long elemSize) { 186 if (ptr.isOnHeap()) { 187 Object o = ptr.getObject(); 188 int index = (int)(offset / elemSize); 189 190 if (o instanceof short[]) { 191 short[] arr = (short[])o; 192 return Short.toUnsignedLong(arr[index]); 193 } else if (o instanceof int[]) { 194 int[] arr = (int[])o; 195 return Integer.toUnsignedLong(arr[index]); 196 } else if (o instanceof long[]) { 197 long[] arr = (long[])o; 198 return arr[index]; 199 } else { 200 throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName()); 201 } 202 } else { 203 long addr = ptr.getOffset() + offset; 204 205 switch ((int)elemSize) { 206 case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr)); 207 case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr)); 208 case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr)); 209 case 8: return UNSAFE.getLongUnaligned(null, addr); 210 default: throw new IllegalArgumentException("Invalid element size: " + elemSize); 211 } 212 } 213 } 214 215 private void putValue(long addr, long elemSize, long value) { 216 switch ((int)elemSize) { 217 case 1: UNSAFE.putByte(addr, (byte)value); break; 218 case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break; 219 case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break; 220 case 8: UNSAFE.putLongUnaligned(null, addr, value); break; 221 default: throw new IllegalArgumentException("Invalid element size: " + elemSize); 222 } 223 } 224 225 /** 226 * Get the size of the elements for an array 227 * 228 * @param o a primitive heap array 229 * 230 * @return the size (in bytes) of the individual array elements 231 */ 232 private long getArrayElemSize(Object o) { 233 if (o instanceof short[]) { 234 return 2; 235 } else if (o instanceof int[]) { 236 return 4; 237 } else if (o instanceof long[]) { 238 return 8; 239 } else { 240 throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName()); 241 } 242 } 243 244 /** 245 * Byte swap a value 246 * 247 * @param value the value to swap, only the bytes*8 least significant bits are used 248 * @param size size (in bytes) of the value 249 * 250 * @return the byte swapped value in the bytes*8 least significant bits 251 */ 252 private long byteSwap(long value, long size) { 253 switch ((int)size) { 254 case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value)); 255 case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value)); 256 case 8: return Long.reverseBytes(value); 257 default: throw new IllegalArgumentException("Invalid element size: " + size); 258 } 259 } 260 261 /** 262 * Verify data in a heap array which has *not* been byte swapped 263 * 264 * @param ptr the data to verify 265 * @param startOffset the offset (in bytes) at which to start the verification 266 * @param size size (in bytes) of the data to verify 267 * 268 * @throws RuntimeException if an error is found 269 */ 270 private void verifyUnswappedData(GenericPointer ptr, long startOffset, long size) { 271 for (long elemOffset = startOffset; elemOffset < startOffset + size; elemOffset++) { 272 byte expected = (byte)getVerificationDataForOffset(elemOffset, 1); 273 274 byte actual; 275 if (ptr.isOnHeap()) { 276 actual = UNSAFE.getByte(ptr.getObject(), ptr.getOffset() + elemOffset); 277 } else { 278 actual = UNSAFE.getByte(ptr.getOffset() + elemOffset); 279 } 280 281 if (expected != actual) { 282 throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) + 283 " size: 0x" + Long.toHexString(size) + 284 " elemOffset: 0x" + Long.toHexString(elemOffset) + 285 " expected: 0x" + Long.toHexString(expected) + 286 " != actual: 0x" + Long.toHexString(actual)); 287 } 288 } 289 } 290 291 292 /** 293 * Copy and byte swap data from the source to the destination 294 * 295 * This method will pre-populate the whole source and destination 296 * buffers with verification friendly data. It will then use 297 * copySwapMemory to fill part of the destination buffer with 298 * swapped data from the source. Some space (padding) will be 299 * left before and after the data in the destination buffer, which 300 * should not be touched/overwritten by the copy call. 301 * 302 * Note: Both source and destination buffers will be overwritten! 303 * 304 * @param src source buffer to copy from 305 * @param srcOffset the offset (in bytes) in the source buffer, relative to 306 * the first array element, at which to start reading data 307 * @param dst destination buffer to copy to 308 * @param dstOffset the offset (in bytes) in the destination 309 * buffer, relative to the first array element, at which to 310 * start writing data 311 * @param bufSize the size (in bytes) of the src and dst arrays 312 * @param copyBytes the size (in bytes) of the copy to perform, 313 * must be a multiple of elemSize 314 * @param elemSize the size (in bytes) of the elements to byte swap 315 * 316 * @throws RuntimeException if an error is found 317 */ 318 private void testCopySwap(GenericPointer src, long srcOffset, 319 GenericPointer dst, long dstOffset, 320 long bufSize, long copyBytes, long elemSize) { 321 if (!isAligned(copyBytes, elemSize)) { 322 throw new IllegalArgumentException( 323 "copyBytes (" + copyBytes + ") must be a multiple of elemSize (" + elemSize + ")"); 324 } 325 if (src.isOnHeap() && !isAligned(srcOffset, elemSize)) { 326 throw new IllegalArgumentException( 327 "srcOffset (" + srcOffset + ") must be a multiple of elemSize (" + elemSize + ")"); 328 } 329 if (dst.isOnHeap() && !isAligned(dstOffset, elemSize)) { 330 throw new IllegalArgumentException( 331 "dstOffset (" + dstOffset + ") must be a multiple of elemSize (" + elemSize + ")"); 332 } 333 if (srcOffset + copyBytes > bufSize) { 334 throw new IllegalArgumentException( 335 "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")"); 336 } 337 if (dstOffset + copyBytes > bufSize) { 338 throw new IllegalArgumentException( 339 "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")"); 340 } 341 342 // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes) 343 initVerificationData(src, bufSize, elemSize); 344 if (!src.equals(dst)) { 345 initVerificationData(dst, bufSize, elemSize); 346 } 347 348 if (DEBUG) { 349 System.out.println("===before==="); 350 for (int offset = 0; offset < bufSize; offset += elemSize) { 351 long srcValue = getArrayElem(src, offset, elemSize); 352 long dstValue = getArrayElem(dst, offset, elemSize); 353 354 System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) + 355 " src=0x" + Long.toHexString(srcValue) + 356 " dst=0x" + Long.toHexString(dstValue)); 357 } 358 } 359 360 // Copy & swap data into the middle of the destination buffer 361 UNSAFE.copySwapMemory(src.getObject(), 362 src.getOffset() + srcOffset, 363 dst.getObject(), 364 dst.getOffset() + dstOffset, 365 copyBytes, 366 elemSize); 367 368 if (DEBUG) { 369 System.out.println("===after==="); 370 for (int offset = 0; offset < bufSize; offset += elemSize) { 371 long srcValue = getArrayElem(src, offset, elemSize); 372 long dstValue = getArrayElem(dst, offset, elemSize); 373 374 System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) + 375 " src=0x" + Long.toHexString(srcValue) + 376 " dst=0x" + Long.toHexString(dstValue)); 377 } 378 } 379 380 // Verify the the front padding is unchanged 381 verifyUnswappedData(dst, 0, dstOffset); 382 383 // Verify swapped data 384 verifySwappedData(dst, srcOffset, dstOffset, copyBytes, elemSize); 385 386 // Verify that the back back padding is unchanged 387 long frontAndDataBytes = dstOffset + copyBytes; 388 long trailingBytes = bufSize - frontAndDataBytes; 389 verifyUnswappedData(dst, frontAndDataBytes, trailingBytes); 390 } 391 392 /** 393 * Test various configurations copy-swapping from one buffer to the other 394 * 395 * @param src the source buffer to copy from 396 * @param dst the destination buffer to copy to 397 * @param size size (in bytes) of the buffers 398 * @param elemSize size (in bytes) of the individual elements 399 * 400 * @throws RuntimeException if an error is found 401 */ 402 public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize) { 403 // offset in source from which to start reading data 404 for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) { 405 406 // offset in destination at which to start writing data 407 for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) { 408 409 // number of bytes to copy 410 long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset); 411 for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) { 412 try { 413 testCopySwap(src, srcOffset, dst, dstOffset, size, copyBytes, elemSize); 414 } catch (RuntimeException e) { 415 // Wrap the exception in another exception to catch the relevant configuration data 416 throw new RuntimeException("testBufferPair: " + 417 "src=" + src + 418 " dst=" + dst + 419 " elemSize=0x" + Long.toHexString(elemSize) + 420 " copyBytes=0x" + Long.toHexString(copyBytes) + 421 " srcOffset=0x" + Long.toHexString(srcOffset) + 422 " dstOffset=0x" + Long.toHexString(dstOffset), 423 e); 424 } 425 } 426 } 427 } 428 } 429 430 /** 431 * Test copying between various permutations of buffers 432 * 433 * @param buffers buffers to permute (src x dst) 434 * @param size size (in bytes) of buffers 435 * @param elemSize size (in bytes) of individual elements 436 * 437 * @throws RuntimeException if an error is found 438 */ 439 public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize) { 440 for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) { 441 for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) { 442 testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize); 443 } 444 } 445 } 446 447 /** 448 * Test copying of a specific element size 449 * 450 * @param size size (in bytes) of buffers to allocate 451 * @param elemSize size (in bytes) of individual elements 452 * 453 * @throws RuntimeException if an error is found 454 */ 455 private void testElemSize(long size, long elemSize) { 456 long buf1Raw = 0; 457 long buf2Raw = 0; 458 459 try { 460 buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); 461 long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT); 462 463 buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); 464 long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT); 465 466 GenericPointer[] buffers = { 467 new GenericPointer(buf1), 468 new GenericPointer(buf2), 469 new GenericPointer(allocArray(size, elemSize)), 470 new GenericPointer(allocArray(size, elemSize)) 471 }; 472 473 testPermuteBuffers(buffers, size, elemSize); 474 } finally { 475 if (buf1Raw != 0) { 476 UNSAFE.freeMemory(buf1Raw); 477 } 478 if (buf2Raw != 0) { 479 UNSAFE.freeMemory(buf2Raw); 480 } 481 } 482 } 483 484 /** 485 * Verify that small copy swaps work 486 */ 487 private void testSmallCopy() { 488 int smallBufSize = SMALL_COPY_SIZE; 489 490 // Test various element types and heap/native combinations 491 for (long elemSize = 2; elemSize <= 8; elemSize <<= 1) { 492 testElemSize(smallBufSize, elemSize); 493 } 494 } 495 496 497 /** 498 * Verify that large copy swaps work 499 */ 500 private void testLargeCopy() { 501 long size = 2 * GB + 8; 502 long bufRaw = 0; 503 504 // Check that a large native copy succeeds 505 try { 506 try { 507 bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); 508 } catch (OutOfMemoryError e) { 509 // Accept failure, skip test 510 return; 511 } 512 513 long buf = alignUp(bufRaw, BASE_ALIGNMENT); 514 515 UNSAFE.copySwapMemory(null, buf, null, buf, size, 8); 516 } catch (Exception e) { 517 throw new RuntimeException("copySwapMemory of large buffer failed"); 518 } finally { 519 if (bufRaw != 0) { 520 UNSAFE.freeMemory(bufRaw); 521 } 522 } 523 } 524 525 /** 526 * Run positive tests 527 * 528 * @throws RuntimeException if an error is found 529 */ 530 private void testPositive() { 531 testSmallCopy(); 532 testLargeCopy(); 533 } 534 535 /** 536 * Run negative tests, testing corner cases and the various exceptions 537 * 538 * @throws RuntimeException if an error is found 539 */ 540 private void testNegative() { 541 long bufRaw = 0; 542 543 try { 544 bufRaw = UNSAFE.allocateMemory(1024); 545 long buf = alignUp(bufRaw, BASE_ALIGNMENT); 546 short[] arr = new short[16]; 547 548 // Check various illegal element sizes 549 for (int elemSize = 2; elemSize <= 8; elemSize <<= 1) { 550 long[] illegalSizes = { -1, 1, elemSize - 1, elemSize + 1, elemSize * 2 - 1 }; 551 for (long size : illegalSizes) { 552 try { 553 // Check that illegal elemSize throws an IAE 554 UNSAFE.copySwapMemory(null, buf, null, buf, size, elemSize); 555 throw new RuntimeException("copySwapMemory failed to throw IAE for size=" + size + " elemSize=" + elemSize); 556 } catch (IllegalArgumentException e) { 557 // good 558 } 559 } 560 } 561 562 try { 563 // Check that negative srcOffset throws an IAE 564 UNSAFE.copySwapMemory(arr, -1, arr, UNSAFE.arrayBaseOffset(arr.getClass()), 16, 2); 565 throw new RuntimeException("copySwapMemory failed to throw IAE for srcOffset=-1"); 566 } catch (IllegalArgumentException e) { 567 // good 568 } 569 570 try { 571 // Check that negative dstOffset throws an IAE 572 UNSAFE.copySwapMemory(arr, UNSAFE.arrayBaseOffset(arr.getClass()), arr, -1, 16, 2); 573 throw new RuntimeException("copySwapMemory failed to throw IAE for destOffset=-1"); 574 } catch (IllegalArgumentException e) { 575 // good 576 } 577 578 long illegalElemSizes[] = { 0, 1, 3, 5, 6, 7, 9, 10, -1 }; 579 for (long elemSize : illegalElemSizes) { 580 try { 581 // Check that elemSize 1 throws an IAE 582 UNSAFE.copySwapMemory(null, buf, null, buf, 16, elemSize); 583 throw new RuntimeException("copySwapMemory failed to throw NPE"); 584 } catch (IllegalArgumentException e) { 585 // good 586 } 587 } 588 589 try { 590 // Check that a reference array destination throws IAE 591 UNSAFE.copySwapMemory(null, buf, new Object[16], UNSAFE.arrayBaseOffset(Object[].class), 16, 8); 592 throw new RuntimeException("copySwapMemory failed to throw NPE"); 593 } catch (IllegalArgumentException e) { 594 // good 595 } 596 597 // Check that invalid source & dest pointers throw IAEs (only relevant on 32-bit platforms) 598 if (UNSAFE.addressSize() == 4) { 599 long invalidPtr = (long)1 << 35; // Pick a random bit in upper 32 bits 600 601 try { 602 // Check that an invalid (not 32-bit clean) source pointer throws IAE 603 UNSAFE.copySwapMemory(null, invalidPtr, null, buf, 16, 2); 604 throw new RuntimeException("copySwapMemory failed to throw IAE for srcOffset 0x" + 605 Long.toHexString(invalidPtr)); 606 } catch (IllegalArgumentException e) { 607 // good 608 } 609 610 try { 611 // Check that an invalid (not 32-bit clean) source pointer throws IAE 612 UNSAFE.copySwapMemory(null, buf, null, invalidPtr, 16, 2); 613 throw new RuntimeException("copySwapMemory failed to throw IAE for destOffset 0x" + 614 Long.toHexString(invalidPtr)); 615 } catch (IllegalArgumentException e) { 616 // good 617 } 618 } 619 } finally { 620 if (bufRaw != 0) { 621 UNSAFE.freeMemory(bufRaw); 622 } 623 } 624 } 625 626 /** 627 * Run all tests 628 * 629 * @throws RuntimeException if an error is found 630 */ 631 private void test() { 632 testPositive(); 633 testNegative(); 634 } 635 636 public static void main(String[] args) { 637 CopySwap cs = new CopySwap(); 638 cs.test(); 639 } 640 641 /** 642 * Helper class to represent a "pointer" - either a heap array or 643 * a pointer to a native buffer. 644 * 645 * In the case of a native pointer, the Object is null and the offset is 646 * the absolute address of the native buffer. 647 * 648 * In the case of a heap object, the Object is a primitive array, and 649 * the offset will be set to the base offset to the first element, meaning 650 * the object and the offset together form a double-register pointer. 651 */ 652 static class GenericPointer { 653 private final Object o; 654 private final long offset; 655 656 private GenericPointer(Object o, long offset) { 657 this.o = o; 658 this.offset = offset; 659 } 660 661 public String toString() { 662 return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")"; 663 } 664 665 public boolean equals(Object other) { 666 if (!(other instanceof GenericPointer)) { 667 return false; 668 } 669 670 GenericPointer otherp = (GenericPointer)other; 671 672 return o == otherp.o && offset == otherp.offset; 673 } 674 675 GenericPointer(Object o) { 676 this(o, UNSAFE.arrayBaseOffset(o.getClass())); 677 } 678 679 GenericPointer(long offset) { 680 this(null, offset); 681 } 682 683 public boolean isOnHeap() { 684 return o != null; 685 } 686 687 public Object getObject() { 688 return o; 689 } 690 691 public long getOffset() { 692 return offset; 693 } 694 } 695 }