1 /*
   2  * Copyright (c) 2015, 2026, 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 /*
  25  * @test
  26  * @bug 8033148 8141409
  27  * @summary tests for array equals and compare
  28  * @library /test/lib
  29  * @run junit ArraysEqCmpTest
  30 */
  31 
  32 import java.lang.invoke.MethodHandle;
  33 import java.lang.invoke.MethodHandles;
  34 import java.lang.invoke.MethodType;
  35 import java.lang.reflect.Array;
  36 import java.util.Arrays;
  37 import java.util.Comparator;
  38 import java.util.HashMap;
  39 import java.util.List;
  40 import java.util.Map;
  41 import java.util.Objects;
  42 import java.util.function.BiFunction;
  43 import java.util.function.LongFunction;
  44 import java.util.stream.IntStream;
  45 
  46 import jdk.test.lib.valueclass.AsValueClass;
  47 
  48 import org.junit.jupiter.api.Assertions;
  49 import org.junit.jupiter.api.Test;
  50 import org.junit.jupiter.api.TestInstance;
  51 import org.junit.jupiter.params.ParameterizedTest;
  52 import org.junit.jupiter.params.provider.MethodSource;
  53 
  54 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
  55 public class ArraysEqCmpTest {
  56 
  57     // Maximum width in bits
  58     static final int MAX_WIDTH = 512;
  59 
  60     static final Map<Class, Integer> typeToWidth;
  61 
  62     @AsValueClass
  63     record Point(int x, int y) implements Comparable<Point> {
  64         @Override
  65         public int compareTo(Point o) {
  66             int r = Integer.compare(this.x, o.x);
  67             return (r != 0) ? r : Integer.compare(this.y, o.y);
  68         }
  69     }
  70 
  71     static {
  72         typeToWidth = new HashMap<>();
  73         typeToWidth.put(boolean.class, Byte.SIZE);
  74         typeToWidth.put(byte.class, Byte.SIZE);
  75         typeToWidth.put(short.class, Short.SIZE);
  76         typeToWidth.put(char.class, Character.SIZE);
  77         typeToWidth.put(int.class, Integer.SIZE);
  78         typeToWidth.put(long.class, Long.SIZE);
  79         typeToWidth.put(float.class, Float.SIZE);
  80         typeToWidth.put(double.class, Double.SIZE);
  81         typeToWidth.put(Object.class, Integer.SIZE); // @@@ 32 or 64?
  82     }
  83 
  84     static int arraySizeFor(Class<?> type) {
  85         type = type.isPrimitive() ? type : Object.class;
  86         return 4 * MAX_WIDTH / typeToWidth.get(type);
  87     }
  88 
  89     static abstract class ArrayType<T> {
  90         final Class<?> arrayType;
  91         final Class<?> componentType;
  92         final boolean unsigned;
  93 
  94         final MethodHandle cpy;
  95 
  96         final MethodHandle eq;
  97         final MethodHandle eqr;
  98         final MethodHandle cmp;
  99         final MethodHandle cmpr;
 100         final MethodHandle mm;
 101         final MethodHandle mmr;
 102 
 103         final MethodHandle getter;
 104 
 105         final MethodHandle toString;
 106 
 107         public ArrayType(Class<T> arrayType) {
 108             this(arrayType, false);
 109         }
 110 
 111         public ArrayType(Class<T> arrayType, boolean unsigned) {
 112             this.arrayType = arrayType;
 113             this.componentType = arrayType.getComponentType();
 114             this.unsigned = unsigned;
 115 
 116             try {
 117                 MethodHandles.Lookup l = MethodHandles.lookup();
 118 
 119                 getter = MethodHandles.arrayElementGetter(arrayType);
 120 
 121                 if (componentType.isPrimitive()) {
 122                     cpy = l.findStatic(Arrays.class, "copyOfRange",
 123                                        MethodType.methodType(arrayType, arrayType, int.class, int.class));
 124 
 125                     MethodType eqt = MethodType.methodType(
 126                             boolean.class, arrayType, arrayType);
 127                     MethodType eqrt = MethodType.methodType(
 128                             boolean.class, arrayType, int.class, int.class, arrayType, int.class, int.class);
 129 
 130                     eq = l.findStatic(Arrays.class, "equals", eqt);
 131                     eqr = l.findStatic(Arrays.class, "equals", eqrt);
 132 
 133                     String compareName = unsigned ? "compareUnsigned" : "compare";
 134                     cmp = l.findStatic(Arrays.class, compareName,
 135                                        eqt.changeReturnType(int.class));
 136                     cmpr = l.findStatic(Arrays.class, compareName,
 137                                         eqrt.changeReturnType(int.class));
 138 
 139                     mm = l.findStatic(Arrays.class, "mismatch",
 140                                        eqt.changeReturnType(int.class));
 141                     mmr = l.findStatic(Arrays.class, "mismatch",
 142                                        eqrt.changeReturnType(int.class));
 143 
 144                     toString = l.findStatic(Arrays.class, "toString",
 145                                             MethodType.methodType(String.class, arrayType));
 146                 }
 147                 else {
 148                     cpy = l.findStatic(Arrays.class, "copyOfRange",
 149                                        MethodType.methodType(Object[].class, Object[].class, int.class, int.class));
 150 
 151                     MethodType eqt = MethodType.methodType(
 152                             boolean.class, Object[].class, Object[].class);
 153                     MethodType eqrt = MethodType.methodType(
 154                             boolean.class, Object[].class, int.class, int.class, Object[].class, int.class, int.class);
 155 
 156                     eq = l.findStatic(Arrays.class, "equals", eqt);
 157                     eqr = l.findStatic(Arrays.class, "equals", eqrt);
 158 
 159                     MethodType cmpt = MethodType.methodType(
 160                             int.class, Comparable[].class, Comparable[].class);
 161                     MethodType cmprt = MethodType.methodType(
 162                             int.class, Comparable[].class, int.class, int.class, Comparable[].class, int.class, int.class);
 163 
 164                     cmp = l.findStatic(Arrays.class, "compare", cmpt);
 165                     cmpr = l.findStatic(Arrays.class, "compare", cmprt);
 166 
 167                     mm = l.findStatic(Arrays.class, "mismatch",
 168                                       eqt.changeReturnType(int.class));
 169                     mmr = l.findStatic(Arrays.class, "mismatch",
 170                                        eqrt.changeReturnType(int.class));
 171 
 172                     toString = l.findStatic(Arrays.class, "toString",
 173                                             MethodType.methodType(String.class, Object[].class));
 174                 }
 175 
 176             }
 177             catch (Exception e) {
 178                 throw new Error(e);
 179             }
 180         }
 181 
 182         @Override
 183         public String toString() {
 184             String s = arrayType.getCanonicalName();
 185             return unsigned ? "unsigned " + s : s;
 186         }
 187 
 188         Object construct(int length) {
 189             return Array.newInstance(componentType, length);
 190         }
 191 
 192         Object copyOf(Object a) {
 193             return copyOf(a, 0, Array.getLength(a));
 194         }
 195 
 196         Object copyOf(Object a, int from, int to) {
 197             try {
 198                 return (Object) cpy.invoke(a, from, to);
 199             }
 200             catch (RuntimeException | Error e) {
 201                 throw e;
 202             }
 203             catch (Throwable t) {
 204                 throw new Error(t);
 205             }
 206         }
 207 
 208         Object get(Object a, int i) {
 209             try {
 210                 return (Object) getter.invoke(a, i);
 211             }
 212             catch (RuntimeException | Error e) {
 213                 throw e;
 214             }
 215             catch (Throwable t) {
 216                 throw new Error(t);
 217             }
 218         }
 219 
 220         abstract void set(Object a, int i, Object v);
 221 
 222         boolean equals(Object a, Object b) {
 223             try {
 224                 return (boolean) eq.invoke(a, b);
 225             }
 226             catch (RuntimeException | Error e) {
 227                 throw e;
 228             }
 229             catch (Throwable t) {
 230                 throw new Error(t);
 231             }
 232         }
 233 
 234         boolean equals(Object a, int aFromIndex, int aToIndex,
 235                        Object b, int bFromIndex, int bToIndex) {
 236             try {
 237                 return (boolean) eqr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
 238             }
 239             catch (RuntimeException | Error e) {
 240                 throw e;
 241             }
 242             catch (Throwable t) {
 243                 throw new Error(t);
 244             }
 245         }
 246 
 247         int compare(Object a, Object b) {
 248             try {
 249                 return (int) cmp.invoke(a, b);
 250             }
 251             catch (RuntimeException | Error e) {
 252                 throw e;
 253             }
 254             catch (Throwable t) {
 255                 throw new Error(t);
 256             }
 257         }
 258 
 259         int compare(Object a, int aFromIndex, int aToIndex,
 260                     Object b, int bFromIndex, int bToIndex) {
 261             try {
 262                 return (int) cmpr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
 263             }
 264             catch (RuntimeException | Error e) {
 265                 throw e;
 266             }
 267             catch (Throwable t) {
 268                 throw new Error(t);
 269             }
 270         }
 271 
 272         int mismatch(Object a, Object b) {
 273             try {
 274                 return (int) mm.invoke(a, b);
 275             }
 276             catch (RuntimeException | Error e) {
 277                 throw e;
 278             }
 279             catch (Throwable t) {
 280                 throw new Error(t);
 281             }
 282         }
 283 
 284         int mismatch(Object a, int aFromIndex, int aToIndex,
 285                      Object b, int bFromIndex, int bToIndex) {
 286             try {
 287                 return (int) mmr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
 288             }
 289             catch (RuntimeException | Error e) {
 290                 throw e;
 291             }
 292             catch (Throwable t) {
 293                 throw new Error(t);
 294             }
 295         }
 296 
 297         String toString(Object a) {
 298             try {
 299                 return (String) toString.invoke(a);
 300             }
 301             catch (RuntimeException | Error e) {
 302                 throw e;
 303             }
 304             catch (Throwable t) {
 305                 throw new Error(t);
 306             }
 307         }
 308 
 309         static class BoxedIntegers extends ArrayType<Integer[]> {
 310             public BoxedIntegers() {
 311                 super(Integer[].class);
 312             }
 313 
 314             @Override
 315             void set(Object a, int i, Object v) {
 316                 // Ensure unique reference
 317                 ((Integer[]) a)[i] = v != null ? new Integer((Integer) v) : null;
 318             }
 319         }
 320 
 321         static class BoxedIntegersWithReverseComparator extends BoxedIntegers {
 322             final Comparator<Integer> c = (a, b) -> {
 323                 // Nulls sort after non-nulls
 324                 if (a == null || b == null)
 325                     return a == null ? b == null ? 0 : 1 : -1;
 326 
 327                 return Integer.compare(b, a);
 328             };
 329 
 330             final MethodHandle eqc;
 331             final MethodHandle eqcr;
 332             final MethodHandle cmpc;
 333             final MethodHandle cmpcr;
 334             final MethodHandle mismatchc;
 335             final MethodHandle mismatchcr;
 336 
 337             public BoxedIntegersWithReverseComparator() {
 338                 try {
 339                     MethodHandles.Lookup l = MethodHandles.lookup();
 340 
 341                     MethodType cmpt = MethodType.methodType(
 342                             int.class, Object[].class, Object[].class, Comparator.class);
 343                     MethodType cmprt = MethodType.methodType(
 344                             int.class, Object[].class, int.class, int.class,
 345                             Object[].class, int.class, int.class, Comparator.class);
 346 
 347                     eqc = l.findStatic(Arrays.class, "equals", cmpt.changeReturnType(boolean.class));
 348                     eqcr = l.findStatic(Arrays.class, "equals", cmprt.changeReturnType(boolean.class));
 349                     cmpc = l.findStatic(Arrays.class, "compare", cmpt);
 350                     cmpcr = l.findStatic(Arrays.class, "compare", cmprt);
 351                     mismatchc = l.findStatic(Arrays.class, "mismatch", cmpt);
 352                     mismatchcr = l.findStatic(Arrays.class, "mismatch", cmprt);
 353                 }
 354                 catch (Exception e) {
 355                     throw new Error(e);
 356                 }
 357             }
 358 
 359             @Override
 360             boolean equals(Object a, Object b) {
 361                 try {
 362                     return (boolean) eqc.invoke(a, b, c);
 363                 }
 364                 catch (RuntimeException | Error e) {
 365                     throw e;
 366                 }
 367                 catch (Throwable t) {
 368                     throw new Error(t);
 369                 }
 370             }
 371 
 372             @Override
 373             boolean equals(Object a, int aFromIndex, int aToIndex,
 374                            Object b, int bFromIndex, int bToIndex) {
 375                 try {
 376                     return (boolean) eqcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c);
 377                 }
 378                 catch (RuntimeException | Error e) {
 379                     throw e;
 380                 }
 381                 catch (Throwable t) {
 382                     throw new Error(t);
 383                 }
 384             }
 385 
 386             @Override
 387             int compare(Object a, Object b) {
 388                 try {
 389                     return (int) cmpc.invoke(a, b, c);
 390                 }
 391                 catch (RuntimeException | Error e) {
 392                     throw e;
 393                 }
 394                 catch (Throwable t) {
 395                     throw new Error(t);
 396                 }
 397             }
 398 
 399             @Override
 400             int compare(Object a, int aFromIndex, int aToIndex,
 401                         Object b, int bFromIndex, int bToIndex) {
 402                 try {
 403                     return (int) cmpcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c);
 404                 }
 405                 catch (RuntimeException | Error e) {
 406                     throw e;
 407                 }
 408                 catch (Throwable t) {
 409                     throw new Error(t);
 410                 }
 411             }
 412 
 413             @Override
 414             int mismatch(Object a, Object b) {
 415                 try {
 416                     return (int) mismatchc.invoke(a, b, c);
 417                 }
 418                 catch (RuntimeException | Error e) {
 419                     throw e;
 420                 }
 421                 catch (Throwable t) {
 422                     throw new Error(t);
 423                 }
 424             }
 425 
 426             @Override
 427             int mismatch(Object a, int aFromIndex, int aToIndex,
 428                          Object b, int bFromIndex, int bToIndex) {
 429                 try {
 430                     return (int) mismatchcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c);
 431                 }
 432                 catch (RuntimeException | Error e) {
 433                     throw e;
 434                 }
 435                 catch (Throwable t) {
 436                     throw new Error(t);
 437                 }
 438             }
 439 
 440             @Override
 441             public String toString() {
 442                 return arrayType.getCanonicalName() + " with Comparator";
 443             }
 444         }
 445 
 446         static class Booleans extends ArrayType<boolean[]> {
 447             public Booleans() {
 448                 super(boolean[].class);
 449             }
 450 
 451             @Override
 452             void set(Object a, int i, Object v) {
 453                 boolean pv;
 454                 if (v instanceof Boolean) {
 455                     pv = (Boolean) v;
 456                 }
 457                 else if (v instanceof Integer) {
 458                     pv = ((Integer) v) >= 0;
 459                 }
 460                 else throw new IllegalStateException();
 461 
 462                 ((boolean[]) a)[i] = pv;
 463             }
 464         }
 465 
 466         static class Bytes extends ArrayType<byte[]> {
 467             public Bytes(boolean unsigned) {
 468                 super(byte[].class, unsigned);
 469             }
 470 
 471             @Override
 472             void set(Object a, int i, Object v) {
 473                 byte pv;
 474                 if (v instanceof Byte) {
 475                     pv = (Byte) v;
 476                 }
 477                 else if (v instanceof Integer) {
 478                     pv = ((Integer) v).byteValue();
 479                 }
 480                 else throw new IllegalStateException();
 481 
 482                 ((byte[]) a)[i] = pv;
 483             }
 484         }
 485 
 486         static class Characters extends ArrayType<char[]> {
 487             public Characters() {
 488                 super(char[].class);
 489             }
 490 
 491             @Override
 492             void set(Object a, int i, Object v) {
 493                 char pv;
 494                 if (v instanceof Character) {
 495                     pv = (Character) v;
 496                 }
 497                 else if (v instanceof Integer) {
 498                     pv = (char) ((Integer) v).intValue();
 499                 }
 500                 else throw new IllegalStateException();
 501 
 502                 ((char[]) a)[i] = pv;
 503             }
 504         }
 505 
 506         static class Shorts extends ArrayType<short[]> {
 507             public Shorts(boolean unsigned) {
 508                 super(short[].class, unsigned);
 509             }
 510 
 511             @Override
 512             void set(Object a, int i, Object v) {
 513                 short pv;
 514                 if (v instanceof Short) {
 515                     pv = (Short) v;
 516                 }
 517                 else if (v instanceof Integer) {
 518                     pv = ((Integer) v).shortValue();
 519                 }
 520                 else throw new IllegalStateException();
 521 
 522                 ((short[]) a)[i] = pv;
 523             }
 524         }
 525 
 526         static class Integers extends ArrayType<int[]> {
 527             public Integers(boolean unsigned) {
 528                 super(int[].class, unsigned);
 529             }
 530 
 531             @Override
 532             void set(Object a, int i, Object v) {
 533                 int pv;
 534                 if (v instanceof Integer) {
 535                     pv = ((Integer) v).shortValue();
 536                 }
 537                 else throw new IllegalStateException();
 538 
 539                 ((int[]) a)[i] = pv;
 540             }
 541         }
 542 
 543         static class Longs extends ArrayType<long[]> {
 544             public Longs(boolean unsigned) {
 545                 super(long[].class, unsigned);
 546             }
 547 
 548             @Override
 549             void set(Object a, int i, Object v) {
 550                 long pv;
 551                 if (v instanceof Long) {
 552                     pv = (Long) v;
 553                 }
 554                 else if (v instanceof Integer) {
 555                     pv = ((Integer) v).longValue();
 556                 }
 557                 else throw new IllegalStateException();
 558 
 559                 ((long[]) a)[i] = pv;
 560             }
 561         }
 562 
 563         static class Floats extends ArrayType<float[]> {
 564             public Floats() {
 565                 super(float[].class);
 566             }
 567 
 568             @Override
 569             void set(Object a, int i, Object v) {
 570                 float pv;
 571                 if (v instanceof Float) {
 572                     pv = (Float) v;
 573                 }
 574                 else if (v instanceof Integer) {
 575                     pv = ((Integer) v).floatValue();
 576                 }
 577                 else throw new IllegalStateException();
 578 
 579                 ((float[]) a)[i] = pv;
 580             }
 581         }
 582 
 583         static class Doubles extends ArrayType<double[]> {
 584             public Doubles() {
 585                 super(double[].class);
 586             }
 587 
 588             @Override
 589             void set(Object a, int i, Object v) {
 590                 double pv;
 591                 if (v instanceof Double) {
 592                     pv = (Double) v;
 593                 }
 594                 else if (v instanceof Integer) {
 595                     pv = ((Integer) v).doubleValue();
 596                 }
 597                 else throw new IllegalStateException();
 598 
 599                 ((double[]) a)[i] = pv;
 600             }
 601         }
 602 
 603         static class ValuePoints extends ArrayType<Point[]> {
 604             public ValuePoints() {
 605                 super(Point[].class);
 606             }
 607 
 608             @Override
 609             void set(Object a, int i, Object v) {
 610                 if (v == null) {
 611                     ((Point[]) a)[i] = null;
 612                 } else if (v instanceof Point) {
 613                     ((Point[]) a)[i] = (Point) v;
 614                 } else if (v instanceof Integer) {
 615                     int val = (Integer) v;
 616                     ((Point[]) a)[i] = new Point(val, val);
 617                 } else throw new IllegalStateException();
 618             }
 619         }
 620     }
 621 
 622     static Object[][] arrayTypes;
 623 
 624     public static Object[][] arrayTypesProvider() {
 625         if (arrayTypes == null) {
 626             arrayTypes = new Object[][]{
 627                     new Object[]{new ArrayType.BoxedIntegers()},
 628                     new Object[]{new ArrayType.BoxedIntegersWithReverseComparator()},
 629                     new Object[]{new ArrayType.Booleans()},
 630                     new Object[]{new ArrayType.Bytes(false)},
 631                     new Object[]{new ArrayType.Bytes(true)},
 632                     new Object[]{new ArrayType.Characters()},
 633                     new Object[]{new ArrayType.Shorts(false)},
 634                     new Object[]{new ArrayType.Shorts(true)},
 635                     new Object[]{new ArrayType.Integers(false)},
 636                     new Object[]{new ArrayType.Integers(true)},
 637                     new Object[]{new ArrayType.Longs(false)},
 638                     new Object[]{new ArrayType.Longs(true)},
 639                     new Object[]{new ArrayType.Floats()},
 640                     new Object[]{new ArrayType.Doubles()},
 641                     new Object[]{new ArrayType.ValuePoints()}
 642             };
 643         }
 644         return arrayTypes;
 645     }
 646 
 647     static Object[][] floatArrayTypes;
 648 
 649     public static Object[][] floatArrayTypesProvider() {
 650         if (floatArrayTypes == null) {
 651             LongFunction<Object> bTof = rb -> Float.intBitsToFloat((int) rb);
 652             LongFunction<Object> bToD = Double::longBitsToDouble;
 653 
 654             floatArrayTypes = new Object[][]{
 655                     new Object[]{new ArrayType.Floats(), 0x7fc00000L, 0x7f800001L, bTof},
 656                     new Object[]{new ArrayType.Doubles(), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD},
 657             };
 658         }
 659         return floatArrayTypes;
 660     }
 661 
 662     static Object[][] objectArrayTypes;
 663 
 664     public static Object[][] objectArrayTypesProvider() {
 665         if (objectArrayTypes == null) {
 666             LongFunction<Object> bTof = rb -> Float.intBitsToFloat((int) rb);
 667             LongFunction<Object> bToD = Double::longBitsToDouble;
 668 
 669             objectArrayTypes = new Object[][]{
 670                     new Object[]{new ArrayType.BoxedIntegers()},
 671                     new Object[]{new ArrayType.BoxedIntegersWithReverseComparator()},
 672                     new Object[]{new ArrayType.ValuePoints()},
 673             };
 674         }
 675         return objectArrayTypes;
 676     }
 677 
 678 
 679     static Object[][] signedUnsignedArrayTypes;
 680 
 681     public static Object[][] signedUnsignedArrayTypes() {
 682         if (signedUnsignedArrayTypes == null) {
 683             signedUnsignedArrayTypes = new Object[][]{
 684                     new Object[]{new ArrayType.Bytes(false), new ArrayType.Bytes(true)},
 685                     new Object[]{new ArrayType.Shorts(false), new ArrayType.Shorts(true)},
 686                     new Object[]{new ArrayType.Integers(false), new ArrayType.Integers(true)},
 687                     new Object[]{new ArrayType.Longs(false), new ArrayType.Longs(true)},
 688             };
 689         }
 690         return signedUnsignedArrayTypes;
 691     }
 692 
 693     // Equality and comparison tests
 694 
 695     @ParameterizedTest
 696     @MethodSource("arrayTypesProvider")
 697     public void testArray(ArrayType<?> arrayType) {
 698         BiFunction<ArrayType<?>, Integer, Object> constructor = (at, s) -> {
 699             Object a = at.construct(s);
 700             for (int x = 0; x < s; x++) {
 701                 at.set(a, x, x % 8);
 702             }
 703             return a;
 704         };
 705 
 706         BiFunction<ArrayType<?>, Object, Object> cloner = (at, a) ->
 707                 constructor.apply(at, Array.getLength(a));
 708 
 709         testArrayType(arrayType, constructor, cloner);
 710     }
 711 
 712     @ParameterizedTest
 713     @MethodSource("floatArrayTypesProvider")
 714     public void testPrimitiveFloatArray(
 715             ArrayType<?> arrayType,
 716             long canonicalNanRawBits, long nonCanonicalNanRawBits,
 717             LongFunction<Object> bitsToFloat) {
 718         Object canonicalNan = bitsToFloat.apply(canonicalNanRawBits);
 719         // If conversion is a signalling NaN it may be subject to conversion to a
 720         // quiet NaN on some processors, even if a copy is performed
 721         // The tests assume that if conversion occurs it does not convert to the
 722         // canonical NaN
 723         Object nonCanonicalNan = bitsToFloat.apply(nonCanonicalNanRawBits);
 724 
 725         BiFunction<ArrayType<?>, Integer, Object> canonicalNaNs = (at, s) -> {
 726             Object a = at.construct(s);
 727             for (int x = 0; x < s; x++) {
 728                 at.set(a, x, canonicalNan);
 729             }
 730             return a;
 731         };
 732 
 733         BiFunction<ArrayType<?>, Object, Object> nonCanonicalNaNs = (at, a) -> {
 734             int s = Array.getLength(a);
 735             Object ac = at.construct(s);
 736             for (int x = 0; x < s; x++) {
 737                 at.set(ac, x, nonCanonicalNan);
 738             }
 739             return ac;
 740         };
 741 
 742         BiFunction<ArrayType<?>, Object, Object> halfNonCanonicalNaNs = (at, a) -> {
 743             int s = Array.getLength(a);
 744             Object ac = at.construct(s);
 745             for (int x = 0; x < s / 2; x++) {
 746                 at.set(ac, x, nonCanonicalNan);
 747             }
 748             for (int x = s / 2; x < s; x++) {
 749                 at.set(ac, x, 1);
 750             }
 751             return ac;
 752         };
 753 
 754         testArrayType(arrayType, canonicalNaNs, nonCanonicalNaNs);
 755         testArrayType(arrayType, canonicalNaNs, halfNonCanonicalNaNs);
 756     }
 757 
 758     @ParameterizedTest
 759     @MethodSource("objectArrayTypesProvider")
 760     public void testNullElementsInObjectArray(ArrayType<?> arrayType) {
 761         BiFunction<ArrayType<?>, Object, Object> cloner = ArrayType::copyOf;
 762 
 763         // All nulls
 764         testArrayType(arrayType,
 765                       (at, s) -> {
 766                           Object a = at.construct(s);
 767                           for (int x = 0; x < s; x++) {
 768                               at.set(a, x, null);
 769                           }
 770                           return a;
 771                       },
 772                       cloner);
 773 
 774 
 775         // Some nulls
 776         testArrayType(arrayType,
 777                       (at, s) -> {
 778                           Object a = at.construct(s);
 779                           for (int x = 0; x < s; x++) {
 780                               int v = x % 8;
 781                               at.set(a, x, v == 0 ? null : v);
 782                           }
 783                           return a;
 784                       },
 785                       cloner);
 786 
 787         Integer[] a = new Integer[]{null, 0};
 788         Integer[] b = new Integer[]{0, 0};
 789         Assertions.assertTrue(Arrays.compare(a, b) < 0);
 790         Assertions.assertTrue(Arrays.compare(b, a) > 0);
 791     }
 792 
 793     @ParameterizedTest
 794     @MethodSource("objectArrayTypesProvider")
 795     public void testSameRefElementsInObjectArray(ArrayType<?> arrayType) {
 796         BiFunction<ArrayType<?>, Object, Object> cloner = ArrayType::copyOf;
 797 
 798         // One ref
 799         Integer one = 1;
 800         testArrayType(arrayType,
 801                     (at, s) -> {
 802                         Object a = at.construct(s);
 803                             for (int x = 0; x < s; x++) {
 804                                 at.set(a, x, one);
 805                             }
 806                         return a;
 807                     },
 808                     cloner);
 809 
 810         // All ref
 811         testArrayType(arrayType,
 812                     (at, s) -> {
 813                         Object a = at.construct(s);
 814                         for (int x = 0; x < s; x++) {
 815                             at.set(a, x, s);
 816                         }
 817                         return a;
 818                     },
 819                     cloner);
 820 
 821         // Some same ref
 822         testArrayType(arrayType,
 823                     (at, s) -> {
 824                         Object a = at.construct(s);
 825                         for (int x = 0; x < s; x++) {
 826                             int v = x % 8;
 827                             at.set(a, x, v == 1 ? one : v);
 828                         }
 829                         return a;
 830                     },
 831                     cloner);
 832     }
 833 
 834     @ParameterizedTest
 835     @MethodSource("signedUnsignedArrayTypes")
 836     public void testSignedUnsignedArray(ArrayType<?> sat, ArrayType<?> uat) {
 837         BiFunction<ArrayType<?>, Integer, Object> constructor = (at, s) -> {
 838             Object a = at.construct(s);
 839             for (int x = 0; x < s; x++) {
 840                 at.set(a, x, 1);
 841             }
 842             return a;
 843         };
 844 
 845         int n = arraySizeFor(sat.componentType);
 846 
 847         for (int s : ranges(0, n)) {
 848             Object a = constructor.apply(sat, s);
 849 
 850             for (int aFrom : ranges(0, s)) {
 851                 for (int aTo : ranges(aFrom, s)) {
 852                     int aLength = aTo - aFrom;
 853 
 854                     if (aLength > 0) {
 855                         for (int i = aFrom; i < aTo; i++) {
 856                             Object ac = sat.copyOf(a);
 857                             // Create common prefix with a length of i - aFrom
 858                             sat.set(ac, i, -1);
 859 
 860                             int sc = sat.compare(ac, aFrom, aTo, a, aFrom, aTo);
 861                             int uc = uat.compare(ac, aFrom, aTo, a, aFrom, aTo);
 862 
 863                             Assertions.assertTrue(sc < 0);
 864                             Assertions.assertTrue(uc > 0);
 865                         }
 866                     }
 867                 }
 868             }
 869         }
 870     }
 871 
 872     void testArrayType(ArrayType<?> at,
 873                        BiFunction<ArrayType<?>, Integer, Object> constructor,
 874                        BiFunction<ArrayType<?>, Object, Object> cloner) {
 875         int n = arraySizeFor(at.componentType);
 876 
 877         for (int s : ranges(0, n)) {
 878             Object a = constructor.apply(at, s);
 879             Object b = cloner.apply(at, a);
 880 
 881             for (int aFrom : ranges(0, s)) {
 882                 for (int aTo : ranges(aFrom, s)) {
 883                     int aLength = aTo - aFrom;
 884 
 885                     for (int bFrom : ranges(0, s)) {
 886                         for (int bTo : ranges(bFrom, s)) {
 887                             int bLength = bTo - bFrom;
 888 
 889                             Object anr = at.copyOf(a, aFrom, aTo);
 890                             Object bnr = at.copyOf(b, bFrom, bTo);
 891 
 892                             boolean eq = isEqual(at, a, aFrom, aTo, b, bFrom, bTo);
 893                             Assertions.assertEquals(eq, at.equals(a, aFrom, aTo, b, bFrom, bTo));
 894                             Assertions.assertEquals(eq, at.equals(b, bFrom, bTo, a, aFrom, aTo));
 895                             Assertions.assertEquals(eq, at.equals(anr, bnr));
 896                             Assertions.assertEquals(eq, at.equals(bnr, anr));
 897                             if (eq) {
 898                                 Assertions.assertEquals(0, at.compare(a, aFrom, aTo, b, bFrom, bTo));
 899                                 Assertions.assertEquals(0, at.compare(b, bFrom, bTo, a, aFrom, aTo));
 900                                 Assertions.assertEquals(0, at.compare(anr, bnr));
 901                                 Assertions.assertEquals(0, at.compare(bnr, anr));
 902 
 903                                 Assertions.assertEquals(-1, at.mismatch(a, aFrom, aTo, b, bFrom, bTo));
 904                                 Assertions.assertEquals(-1, at.mismatch(b, bFrom, bTo, a, aFrom, aTo));
 905                                 Assertions.assertEquals(-1, at.mismatch(anr, bnr));
 906                                 Assertions.assertEquals(-1, at.mismatch(bnr, anr));
 907                             }
 908                             else {
 909                                 int aCb = at.compare(a, aFrom, aTo, b, bFrom, bTo);
 910                                 int bCa = at.compare(b, bFrom, bTo, a, aFrom, aTo);
 911                                 int v = Integer.signum(aCb) * Integer.signum(bCa);
 912                                 Assertions.assertTrue(v == -1);
 913 
 914                                 int anrCbnr = at.compare(anr, bnr);
 915                                 int bnrCanr = at.compare(bnr, anr);
 916                                 Assertions.assertEquals(aCb, anrCbnr);
 917                                 Assertions.assertEquals(bCa, bnrCanr);
 918 
 919 
 920                                 int aMb = at.mismatch(a, aFrom, aTo, b, bFrom, bTo);
 921                                 int bMa = at.mismatch(b, bFrom, bTo, a, aFrom, aTo);
 922                                 int anrMbnr = at.mismatch(anr, bnr);
 923                                 int bnrManr = at.mismatch(bnr, anr);
 924 
 925                                 Assertions.assertNotEquals(-1, aMb);
 926                                 Assertions.assertEquals(bMa, aMb);
 927                                 Assertions.assertNotEquals(-1, anrMbnr);
 928                                 Assertions.assertEquals(bnrManr, anrMbnr);
 929                                 Assertions.assertEquals(anrMbnr, aMb);
 930                                 Assertions.assertEquals(bnrManr, bMa);
 931 
 932                                 // Common or proper prefix
 933                                 Assertions.assertTrue(at.equals(a, aFrom, aFrom + aMb, b, bFrom, bFrom + aMb));
 934                                 if (aMb < Math.min(aLength, bLength)) {
 935                                     // Common prefix
 936                                     Assertions.assertFalse(isEqual(at, a, aFrom + aMb, b, bFrom + aMb));
 937                                 }
 938                             }
 939                         }
 940                     }
 941 
 942                     if (aLength > 0) {
 943                         for (int i = aFrom; i < aTo; i++) {
 944                             Object ac = at.copyOf(a);
 945                             // Create common prefix with a length of i - aFrom
 946                             at.set(ac, i, -1);
 947 
 948                             Object acnr = at.copyOf(ac, aFrom, aTo);
 949                             Object anr = at.copyOf(a, aFrom, aTo);
 950 
 951                             Assertions.assertFalse(at.equals(ac, aFrom, aTo, a, aFrom, aTo));
 952                             Assertions.assertFalse(at.equals(acnr, anr));
 953 
 954                             int acCa = at.compare(ac, aFrom, aTo, a, aFrom, aTo);
 955                             int aCac = at.compare(a, aFrom, aTo, ac, aFrom, aTo);
 956                             int v = Integer.signum(acCa) * Integer.signum(aCac);
 957                             Assertions.assertTrue(v == -1);
 958 
 959                             int acnrCanr = at.compare(acnr, anr);
 960                             int anrCacnr = at.compare(anr, acnr);
 961                             Assertions.assertEquals(acCa, acnrCanr);
 962                             Assertions.assertEquals(aCac, anrCacnr);
 963 
 964 
 965                             int acMa = at.mismatch(ac, aFrom, aTo, a, aFrom, aTo);
 966                             int aMac = at.mismatch(a, aFrom, aTo, ac, aFrom, aTo);
 967                             Assertions.assertEquals(aMac, acMa);
 968                             Assertions.assertEquals(i - aFrom, acMa);
 969 
 970                             int acnrManr = at.mismatch(acnr, anr);
 971                             int anrMacnr = at.mismatch(anr, acnr);
 972                             Assertions.assertEquals(anrMacnr, acnrManr);
 973                             Assertions.assertEquals(i - aFrom, acnrManr);
 974                         }
 975                     }
 976                 }
 977             }
 978         }
 979     }
 980 
 981     static boolean isEqual(ArrayType<?> at, Object a, int aFromIndex, int aToIndex,
 982                            Object b, int bFromIndex, int bToIndex) {
 983         int aLength = aToIndex - aFromIndex;
 984         int bLength = bToIndex - bFromIndex;
 985         if (aLength != bLength)
 986             return false;
 987 
 988         for (int i = 0; i < aLength; i++) {
 989             Object av = at.get(a, aFromIndex++);
 990             Object bv = at.get(b, bFromIndex++);
 991             if (!Objects.equals(av, bv)) return false;
 992         }
 993 
 994         return true;
 995     }
 996 
 997     static boolean isEqual(ArrayType<?> at, Object a, int aFrom, Object b, int bFrom) {
 998         Object av = at.get(a, aFrom);
 999         Object bv = at.get(b, bFrom);
1000 
1001         return Objects.equals(av, bv);
1002     }
1003 
1004     static int[] ranges(int from, int to) {
1005         int width = to - from;
1006         switch (width) {
1007             case 0:
1008                 return new int[]{};
1009             case 1:
1010                 return new int[]{from, to};
1011             case 2:
1012                 return new int[]{from, from + 1, to};
1013             case 3:
1014                 return new int[]{from, from + 1, from + 2, to};
1015             default:
1016                 return IntStream.of(from, from + 1, from + 2, to / 2 - 1, to / 2, to / 2 + 1, to - 2, to - 1, to)
1017                         .filter(i -> i >= from && i <= to)
1018                         .distinct().toArray();
1019         }
1020     }
1021 
1022 
1023     // Null array reference tests
1024 
1025     @ParameterizedTest
1026     @MethodSource("arrayTypesProvider")
1027     public void testNullArrayRefs(ArrayType<?> arrayType) {
1028         Object n = null;
1029         Object a = arrayType.construct(0);
1030 
1031         Assertions.assertTrue(arrayType.equals(n, n));
1032         Assertions.assertFalse(arrayType.equals(n, a));
1033         Assertions.assertFalse(arrayType.equals(a, n));
1034 
1035         Assertions.assertEquals(0, arrayType.compare(n, n));
1036         Assertions.assertTrue(arrayType.compare(n, a) < 0);
1037         Assertions.assertTrue(arrayType.compare(a, n) > 0);
1038     }
1039 
1040 
1041     // Exception throwing tests
1042 
1043     @ParameterizedTest
1044     @MethodSource("arrayTypesProvider")
1045     public void testNPEs(ArrayType<?> arrayType) {
1046         Object[] values = new Object[]{null, arrayType.construct(0)};
1047 
1048         for (Object o1 : values) {
1049             for (Object o2 : values) {
1050                 if (o1 != null && o2 != null)
1051                     continue;
1052 
1053                 testNPE(() -> arrayType.equals(o1, 0, 0, o2, 0, 0));
1054                 testNPE(() -> arrayType.compare(o1, 0, 0, o2, 0, 0));
1055                 testNPE(() -> arrayType.mismatch(o1, o2));
1056                 testNPE(() -> arrayType.mismatch(o1, 0, 0, o2, 0, 0));
1057             }
1058         }
1059     }
1060 
1061     @Test
1062     public void testObjectNPEs() {
1063         String[][] values = new String[][]{null, new String[0]};
1064         Comparator<String> c = String::compareTo;
1065         Comparator[] cs = new Comparator[]{null, c};
1066 
1067         for (String[] o1 : values) {
1068             for (String[] o2 : values) {
1069                 for (Comparator o3 : cs) {
1070                     if (o1 != null && o2 != null && o3 != null)
1071                         continue;
1072 
1073                     if (o3 == null) {
1074                         testNPE(() -> Arrays.equals(o1, o2, o3));
1075                         testNPE(() -> Arrays.compare(o1, o2, o3));
1076                         testNPE(() -> Arrays.mismatch(o1, o2, o3));
1077                     }
1078 
1079                     testNPE(() -> Arrays.equals(o1, 0, 0, o2, 0, 0, o3));
1080                     testNPE(() -> Arrays.compare(o1, 0, 0, o2, 0, 0, o3));
1081                     testNPE(() -> Arrays.mismatch(o1, 0, 0, o2, 0, 0, o3));
1082                 }
1083             }
1084         }
1085     }
1086 
1087     @ParameterizedTest
1088     @MethodSource("arrayTypesProvider")
1089     public void testIAEs(ArrayType<?> arrayType) {
1090         List<Integer> values = Arrays.asList(0, 1);
1091 
1092         for (int s : values) {
1093             Object a = arrayType.construct(s);
1094 
1095             for (int o1 : values) {
1096                 for (int o2 : values) {
1097                     if (o1 <= o2) continue;
1098 
1099                     testIAE(() -> arrayType.equals(a, o1, 0, a, o2, 0));
1100                     testIAE(() -> arrayType.compare(a, o1, 0, a, o2, 0));
1101                     testIAE(() -> arrayType.mismatch(a, o1, 0, a, o2, 0));
1102                 }
1103             }
1104         }
1105     }
1106 
1107     @ParameterizedTest
1108     @MethodSource("arrayTypesProvider")
1109     public void testAIOBEs(ArrayType<?> arrayType) {
1110         List<Integer> froms = Arrays.asList(-1, 0);
1111 
1112         for (int s : Arrays.asList(0, 1)) {
1113             List<Integer> tos = Arrays.asList(s, s + 1);
1114             Object a = arrayType.construct(s);
1115 
1116             for (int aFrom : froms) {
1117                 for (int aTo : tos) {
1118                     for (int bFrom : froms) {
1119                         for (int bTo : tos) {
1120                             if (aFrom >= 0 && aTo <= s &&
1121                                 bFrom >= 0 && bTo <= s) continue;
1122 
1123                             testAIOBE(() -> arrayType.equals(a, aFrom, aTo, a, bFrom, bTo));
1124                             testAIOBE(() -> arrayType.compare(a, aFrom, aTo, a, bFrom, bTo));
1125                             testAIOBE(() -> arrayType.mismatch(a, aFrom, aTo, a, bFrom, bTo));
1126                         }
1127                     }
1128                 }
1129             }
1130         }
1131     }
1132 
1133     static void testNPE(Runnable r) {
1134         testThrowable(r, NullPointerException.class);
1135     }
1136 
1137     static void testIAE(Runnable r) {
1138         testThrowable(r, IllegalArgumentException.class);
1139     }
1140 
1141     static void testAIOBE(Runnable r) {
1142         testThrowable(r, ArrayIndexOutOfBoundsException.class);
1143     }
1144 
1145     static void testThrowable(Runnable r, Class<? extends Throwable> expected) {
1146         Throwable caught = null;
1147         try {
1148             r.run();
1149         }
1150         catch (Throwable t) {
1151             caught = t;
1152         }
1153         Assertions.assertNotNull(caught);
1154         Assertions.assertTrue(expected.isInstance(caught));
1155     }
1156 }