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 }