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