1 //
   2 // Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
   3 // Copyright (c) 2015, Red Hat Inc. All rights reserved.
   4 // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5 //
   6 // This code is free software; you can redistribute it and/or modify it
   7 // under the terms of the GNU General Public License version 2 only, as
   8 // published by the Free Software Foundation.
   9 //
  10 // This code is distributed in the hope that it will be useful, but WITHOUT
  11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13 // version 2 for more details (a copy is included in the LICENSE file that
  14 // accompanied this code).
  15 //
  16 // You should have received a copy of the GNU General Public License version
  17 // 2 along with this work; if not, write to the Free Software Foundation,
  18 // Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19 //
  20 // Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21 // or visit www.oracle.com if you need additional information or have any
  22 // questions.
  23 //
  24 //
  25 
  26 import java.nio.BufferOverflowException;
  27 import java.nio.BufferUnderflowException;
  28 import java.nio.ByteBuffer;
  29 import static java.nio.ByteOrder.BIG_ENDIAN;
  30 import static java.nio.ByteOrder.LITTLE_ENDIAN;
  31 import java.nio.ByteOrder;
  32 import java.util.Arrays;
  33 import java.util.Random;
  34 import jdk.test.lib.Utils;
  35 
  36 /**
  37  * @test
  38  * @bug 8026049
  39  * @modules java.base/jdk.internal.misc
  40  * @library /testlibrary
  41  * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-UseUnalignedAccesses -Djdk.test.lib.random.seed=0 HeapByteBufferTest
  42  * @run main/othervm -Djdk.test.lib.random.seed=0 HeapByteBufferTest
  43  * @summary Verify that byte buffers are correctly accessed.
  44  */
  45 
  46 // A wrapper for a ByteBuffer which maintains a backing array and a
  47 // position.  Whenever this wrapper is written the backing array and
  48 // the wrapped byte buffer are updated together, and whenever it is
  49 // read we check that the ByteBuffer and the backing array are identical.
  50 
  51 class MyByteBuffer {
  52     final ByteBuffer buf;
  53     final byte[] bytes;
  54     int pos;
  55     ByteOrder byteOrder = BIG_ENDIAN;
  56 
  57     MyByteBuffer(ByteBuffer buf, byte[] bytes) {
  58         this.buf = buf;
  59         this.bytes = Arrays.copyOf(bytes, bytes.length);
  60         pos = 0;
  61     }
  62 
  63     public final MyByteBuffer order(ByteOrder bo) {
  64         byteOrder = bo;
  65         buf.order(bo);
  66         return this;
  67     }
  68 
  69     static MyByteBuffer wrap(byte[] bytes) {
  70         return new MyByteBuffer(ByteBuffer.wrap(bytes), bytes);
  71     }
  72 
  73     int capacity() { return bytes.length; }
  74     int position() {
  75         if (buf.position() != pos)
  76             throw new RuntimeException();
  77         return buf.position();
  78     }
  79 
  80     byte[] array() { return buf.array(); }
  81     byte[] backingArray() { return bytes; }
  82 
  83     private static byte long7(long x) { return (byte)(x >> 56); }
  84     private static byte long6(long x) { return (byte)(x >> 48); }
  85     private static byte long5(long x) { return (byte)(x >> 40); }
  86     private static byte long4(long x) { return (byte)(x >> 32); }
  87     private static byte long3(long x) { return (byte)(x >> 24); }
  88     private static byte long2(long x) { return (byte)(x >> 16); }
  89     private static byte long1(long x) { return (byte)(x >>  8); }
  90     private static byte long0(long x) { return (byte)(x      ); }
  91 
  92     private static byte int3(int x) { return (byte)(x >> 24); }
  93     private static byte int2(int x) { return (byte)(x >> 16); }
  94     private static byte int1(int x) { return (byte)(x >>  8); }
  95     private static byte int0(int x) { return (byte)(x      ); }
  96 
  97     private static byte short1(short x) { return (byte)(x >> 8); }
  98     private static byte short0(short x) { return (byte)(x     ); }
  99 
 100     byte _get(long i) { return bytes[(int)i]; }
 101     void _put(long i, byte x) { bytes[(int)i] = x; }
 102 
 103     private void putLongX(long a, long x) {
 104         if (byteOrder == BIG_ENDIAN) {
 105             x = Long.reverseBytes(x);
 106         }
 107         _put(a + 7, long7(x));
 108         _put(a + 6, long6(x));
 109         _put(a + 5, long5(x));
 110         _put(a + 4, long4(x));
 111         _put(a + 3, long3(x));
 112         _put(a + 2, long2(x));
 113         _put(a + 1, long1(x));
 114         _put(a    , long0(x));
 115     }
 116 
 117     private void putIntX(long a, int x) {
 118         if (byteOrder == BIG_ENDIAN) {
 119             x = Integer.reverseBytes(x);
 120         }
 121         _put(a + 3, int3(x));
 122         _put(a + 2, int2(x));
 123         _put(a + 1, int1(x));
 124         _put(a    , int0(x));
 125     }
 126 
 127     private void putShortX(int bi, short x) {
 128         if (byteOrder == BIG_ENDIAN) {
 129             x = Short.reverseBytes(x);
 130         }
 131         _put(bi    , short0(x));
 132         _put(bi + 1, short1(x));
 133     }
 134 
 135     static private int makeInt(byte b3, byte b2, byte b1, byte b0) {
 136         return (((b3       ) << 24) |
 137                 ((b2 & 0xff) << 16) |
 138                 ((b1 & 0xff) <<  8) |
 139                 ((b0 & 0xff)      ));
 140     }
 141     int getIntX(long a) {
 142         int x = makeInt(_get(a + 3),
 143                 _get(a + 2),
 144                 _get(a + 1),
 145                 _get(a));
 146         if (byteOrder == BIG_ENDIAN) {
 147             x = Integer.reverseBytes(x);
 148         }
 149         return x;
 150     }
 151 
 152     static private long makeLong(byte b7, byte b6, byte b5, byte b4,
 153                                  byte b3, byte b2, byte b1, byte b0)
 154     {
 155         return ((((long)b7       ) << 56) |
 156                 (((long)b6 & 0xff) << 48) |
 157                 (((long)b5 & 0xff) << 40) |
 158                 (((long)b4 & 0xff) << 32) |
 159                 (((long)b3 & 0xff) << 24) |
 160                 (((long)b2 & 0xff) << 16) |
 161                 (((long)b1 & 0xff) <<  8) |
 162                 (((long)b0 & 0xff)      ));
 163     }
 164 
 165     long getLongX(long a) {
 166         long x = makeLong(_get(a + 7),
 167                 _get(a + 6),
 168                 _get(a + 5),
 169                 _get(a + 4),
 170                 _get(a + 3),
 171                 _get(a + 2),
 172                 _get(a + 1),
 173                 _get(a));
 174         if (byteOrder == BIG_ENDIAN) {
 175             x = Long.reverseBytes(x);
 176         }
 177         return x;
 178     }
 179 
 180     static private short makeShort(byte b1, byte b0) {
 181         return (short)((b1 << 8) | (b0 & 0xff));
 182     }
 183 
 184     short getShortX(long a) {
 185         short x = makeShort(_get(a + 1),
 186                             _get(a    ));
 187         if (byteOrder == BIG_ENDIAN) {
 188             x = Short.reverseBytes(x);
 189         }
 190         return x;
 191     }
 192 
 193     double getDoubleX(long a) {
 194         long x = getLongX(a);
 195         return Double.longBitsToDouble(x);
 196     }
 197 
 198     double getFloatX(long a) {
 199         int x = getIntX(a);
 200         return Float.intBitsToFloat(x);
 201     }
 202 
 203     void ck(long x, long y) {
 204         if (x != y) {
 205             throw new RuntimeException(" x = " + Long.toHexString(x) + ", y = " + Long.toHexString(y));
 206         }
 207     }
 208 
 209     void ck(double x, double y) {
 210         if (x == x && y == y && x != y) {
 211             ck(x, y);
 212         }
 213     }
 214 
 215     long getLong(int i) { ck(buf.getLong(i), getLongX(i)); return buf.getLong(i); }
 216     int getInt(int i) { ck(buf.getInt(i), getIntX(i)); return buf.getInt(i); }
 217     short getShort(int i) { ck(buf.getShort(i), getShortX(i)); return buf.getShort(i); }
 218     char getChar(int i) { ck(buf.getChar(i), (char)getShortX(i)); return buf.getChar(i); }
 219     double getDouble(int i) { ck(buf.getDouble(i), getDoubleX(i)); return buf.getDouble(i); }
 220     float getFloat(int i) { ck(buf.getFloat(i), getFloatX(i)); return buf.getFloat(i); }
 221 
 222     void putLong(int i, long x) { buf.putLong(i, x); putLongX(i, x); }
 223     void putInt(int i, int x) { buf.putInt(i, x); putIntX(i, x); }
 224     void putShort(int i, short x) { buf.putShort(i, x); putShortX(i, x); }
 225     void putChar(int i, char x) { buf.putChar(i, x); putShortX(i, (short)x); }
 226     void putDouble(int i, double x) { buf.putDouble(i, x); putLongX(i, Double.doubleToRawLongBits(x)); }
 227     void putFloat(int i, float x) { buf.putFloat(i, x); putIntX(i, Float.floatToRawIntBits(x)); }
 228 
 229     long getLong() { ck(buf.getLong(buf.position()), getLongX(pos)); long x = buf.getLong(); pos += 8; return x; }
 230     int getInt() { ck(buf.getInt(buf.position()), getIntX(pos)); int x = buf.getInt(); pos += 4; return x; }
 231     short getShort() { ck(buf.getShort(buf.position()), getShortX(pos)); short x = buf.getShort(); pos += 2; return x; }
 232     char getChar() {  ck(buf.getChar(buf.position()), (char)getShortX(pos)); char x = buf.getChar(); pos += 2; return x; }
 233     double getDouble() { ck(buf.getDouble(buf.position()), getDoubleX(pos)); double x = buf.getDouble(); pos += 8; return x; }
 234     float getFloat() { ck(buf.getFloat(buf.position()), getFloatX(pos)); float x = buf.getFloat(); pos += 4; return x; }
 235 
 236     void putLong(long x) { putLongX(pos, x); pos += 8; buf.putLong(x); }
 237     void putInt(int x) { putIntX(pos, x); pos += 4; buf.putInt(x); }
 238     void putShort(short x) { putShortX(pos, x); pos += 2; buf.putShort(x); }
 239     void putChar(char x) { putShortX(pos, (short)x); pos += 2; buf.putChar(x); }
 240     void putDouble(double x) { putLongX(pos, Double.doubleToRawLongBits(x)); pos += 8; buf.putDouble(x); }
 241     void putFloat(float x) { putIntX(pos, Float.floatToRawIntBits(x)); pos += 4; buf.putFloat(x); }
 242 
 243     void rewind() { pos = 0; buf.rewind(); }
 244 }
 245 
 246 public class HeapByteBufferTest implements Runnable {
 247 
 248     Random random = Utils.getRandomInstance();
 249     MyByteBuffer data = MyByteBuffer.wrap(new byte[1024]);
 250 
 251     int randomOffset(Random r, MyByteBuffer buf, int size) {
 252         return r.nextInt(buf.capacity() - size);
 253     }
 254 
 255     long iterations;
 256 
 257     HeapByteBufferTest(long iterations) {
 258         this.iterations = iterations;
 259     }
 260 
 261     // The core of the test.  Walk over the buffer reading and writing
 262     // random data, XORing it as we go.  We can detect writes in the
 263     // wrong place, writes which are too long or too short, and reads
 264     // or writes of the wrong data,
 265     void step(Random r) {
 266         data.order((r.nextInt() & 1) != 0 ? BIG_ENDIAN : LITTLE_ENDIAN);
 267 
 268         data.rewind();
 269         while (data.position() < data.capacity())
 270             data.putLong(data.getLong() ^ random.nextLong());
 271 
 272         data.rewind();
 273         while (data.position() < data.capacity())
 274             data.putInt(data.getInt() ^ random.nextInt());
 275 
 276         data.rewind();
 277         while (data.position() < data.capacity())
 278             data.putShort((short)(data.getShort() ^ random.nextInt()));
 279 
 280         data.rewind();
 281         while (data.position() < data.capacity())
 282             data.putChar((char)(data.getChar() ^ random.nextInt()));
 283 
 284         data.rewind();
 285         while (data.position() < data.capacity()) {
 286             data.putDouble(combine(data.getDouble(), random.nextLong()));
 287         }
 288 
 289         data.rewind();
 290         while (data.position() < data.capacity())
 291             data.putFloat(combine(data.getFloat(), random.nextInt()));
 292 
 293         for (int i = 0; i < 100; i++) {
 294             int offset = randomOffset(r, data, 8);
 295             data.putLong(offset, data.getLong(offset) ^ random.nextLong());
 296         }
 297         for (int i = 0; i < 100; i++) {
 298             int offset = randomOffset(r, data, 4);
 299             data.putInt(offset, data.getInt(offset) ^ random.nextInt());
 300         }
 301         for (int i = 0; i < 100; i++) {
 302             int offset = randomOffset(r, data, 2);
 303             data.putShort(offset, (short)(data.getShort(offset) ^ random.nextInt()));
 304         }
 305         for (int i = 0; i < 100; i++) {
 306             int offset = randomOffset(r, data, 2);
 307             data.putChar(offset, (char)(data.getChar(offset) ^ random.nextInt()));
 308         }
 309         for (int i = 0; i < 100; i++) {
 310             int offset = randomOffset(r, data, 8);
 311             data.putDouble(offset, combine(data.getDouble(offset), random.nextLong()));
 312         }
 313         for (int i = 0; i < 100; i++) {
 314             int offset = randomOffset(r, data, 4);
 315             data.putFloat(offset, combine(data.getFloat(offset), random.nextInt()));
 316         }
 317     }
 318 
 319     // XOR the bit pattern of a double and a long, returning the
 320     // result as a double.
 321     //
 322     // We convert signalling NaNs to quiet NaNs.  We need to do this
 323     // because some platforms (in particular legacy 80x87) do not
 324     // provide transparent conversions between integer and
 325     // floating-point types even when using raw conversions but
 326     // quietly convert sNaN to qNaN.  This causes spurious test
 327     // failures when the template interpreter uses 80x87 and the JITs
 328     // use XMM registers.
 329     //
 330     public double combine(double prev, long bits) {
 331         bits ^= Double.doubleToRawLongBits(prev);
 332         double result = Double.longBitsToDouble(bits);
 333         if (Double.isNaN(result)) {
 334             result = Double.longBitsToDouble(bits | 0x8000000000000l);
 335         }
 336         return result;
 337     }
 338 
 339     // XOR the bit pattern of a float and an int, returning the result
 340     // as a float.  Convert sNaNs to qNaNs.
 341     public Float combine(float prev, int bits) {
 342         bits ^= Float.floatToRawIntBits(prev);
 343         Float result = Float.intBitsToFloat(bits);
 344         if (Float.isNaN(result)) {
 345             result = Float.intBitsToFloat(bits | 0x400000);
 346         }
 347         return result;
 348     }
 349 
 350     enum PrimitiveType {
 351         BYTE(1), CHAR(2), SHORT(2), INT(4), LONG(8), FLOAT(4), DOUBLE(8);
 352 
 353         public final int size;
 354         PrimitiveType(int size) {
 355             this.size = size;
 356         }
 357     }
 358 
 359     void getOne(ByteBuffer b, PrimitiveType t) {
 360         switch (t) {
 361         case BYTE: b.get(); break;
 362         case CHAR: b.getChar(); break;
 363         case SHORT: b.getShort(); break;
 364         case INT: b.getInt(); break;
 365         case LONG: b.getLong(); break;
 366         case FLOAT: b.getFloat(); break;
 367         case DOUBLE: b.getDouble(); break;
 368         }
 369     }
 370 
 371     void putOne(ByteBuffer b, PrimitiveType t) {
 372         switch (t) {
 373         case BYTE: b.put((byte)0); break;
 374         case CHAR: b.putChar('0'); break;
 375         case SHORT: b.putShort((short)0); break;
 376         case INT: b.putInt(0); break;
 377         case LONG: b.putLong(0); break;
 378         case FLOAT: b.putFloat(0); break;
 379         case DOUBLE: b.putDouble(0); break;
 380         }
 381     }
 382 
 383     void getOne(ByteBuffer b, PrimitiveType t, int index) {
 384         switch (t) {
 385         case BYTE: b.get(index); break;
 386         case CHAR: b.getChar(index); break;
 387         case SHORT: b.getShort(index); break;
 388         case INT: b.getInt(index); break;
 389         case LONG: b.getLong(index); break;
 390         case FLOAT: b.getFloat(index); break;
 391         case DOUBLE: b.getDouble(index); break;
 392         }
 393     }
 394 
 395     void putOne(ByteBuffer b, PrimitiveType t, int index) {
 396         switch (t) {
 397         case BYTE: b.put(index, (byte)0); break;
 398         case CHAR: b.putChar(index, '0'); break;
 399         case SHORT: b.putShort(index, (short)0); break;
 400         case INT: b.putInt(index, 0); break;
 401         case LONG: b.putLong(index, 0); break;
 402         case FLOAT: b.putFloat(index, 0); break;
 403         case DOUBLE: b.putDouble(index, 0); break;
 404         }
 405     }
 406 
 407     void checkBoundaryConditions() {
 408         for (int i = 0; i < 100; i++) {
 409             int bufSize = random.nextInt(16);
 410             byte[] bytes = new byte[bufSize];
 411             ByteBuffer buf = ByteBuffer.wrap(bytes);
 412             for (int j = 0; j < 100; j++) {
 413                 int offset = random.nextInt(32) - 8;
 414                 for (PrimitiveType t : PrimitiveType.values()) {
 415                     int threw = 0;
 416                     try {
 417                         try {
 418                             buf.position(offset);
 419                             getOne(buf, t);
 420                         } catch (BufferUnderflowException e) {
 421                             if (offset + t.size < bufSize)
 422                                 throw new RuntimeException
 423                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 424                             threw++;
 425                         } catch (IllegalArgumentException e) {
 426                             if (offset >= 0 && offset + t.size < bufSize)
 427                                 throw new RuntimeException
 428                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 429                             threw++;
 430                         }
 431 
 432                         try {
 433                             buf.position(offset);
 434                             putOne(buf, t);
 435                         } catch (BufferOverflowException e) {
 436                             if (offset + t.size < bufSize)
 437                                 throw new RuntimeException
 438                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 439                             threw++;
 440                         } catch (IllegalArgumentException e) {
 441                             if (offset >= 0 && offset + t.size < bufSize)
 442                                 throw new RuntimeException
 443                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 444                             threw++;
 445                         }
 446 
 447                         try {
 448                             putOne(buf, t, offset);
 449                         } catch (IndexOutOfBoundsException e) {
 450                             if (offset >= 0 && offset + t.size < bufSize)
 451                                 throw new RuntimeException
 452                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 453                             threw++;
 454                         }
 455 
 456                         try {
 457                             getOne(buf, t, offset);
 458                         } catch (IndexOutOfBoundsException e) {
 459                             if (offset >= 0 && offset + t.size < bufSize)
 460                                 throw new RuntimeException
 461                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 462                             threw++;
 463                         }
 464 
 465                         if (threw == 0) {
 466                             // Make sure that we should not have thrown.
 467                             if (offset < 0 || offset + t.size > bufSize) {
 468                                 throw new RuntimeException
 469                                     ("should have thrown but did not, type = " + t
 470                                      + ", offset = " + offset + ", bufSize = " + bufSize);
 471                             }
 472                         } else if (threw != 4) {
 473                             // If one of the {get,put} operations threw
 474                             // due to an invalid offset then all four of
 475                             // them should have thrown.
 476                             throw new RuntimeException
 477                                 ("should have thrown but at least one did not, type = " + t
 478                                  + ", offset = " + offset + ", bufSize = " + bufSize);
 479                         }
 480                     } catch (Throwable th) {
 481                         throw new RuntimeException
 482                             ("unexpected throw: type  = " + t + ", offset = " + offset + ", bufSize = " + bufSize, th);
 483 
 484                     }
 485                 }
 486             }
 487         }
 488     }
 489 
 490     public void run() {
 491         checkBoundaryConditions();
 492 
 493         for (int i = 0; i < data.capacity(); i += 8) {
 494             data.putLong(i, random.nextLong());
 495         }
 496 
 497         for (int i = 0; i < iterations; i++) {
 498             step(random);
 499         }
 500 
 501         if (!Arrays.equals(data.array(), data.backingArray())) {
 502             throw new RuntimeException();
 503         }
 504     }
 505 
 506     public static void main(String[] args) {
 507         // The number of iterations is high to ensure that tiered
 508         // compilation kicks in all the way up to C2.
 509         long iterations = 100000;
 510         if (args.length > 0)
 511             iterations = Long.parseLong(args[0]);
 512 
 513         new HeapByteBufferTest(iterations).run();
 514     }
 515 }