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