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 }