1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.os.Build;
23 import android.util.ArraySet;
24 
25 import dalvik.system.VMRuntime;
26 
27 import libcore.util.EmptyArray;
28 
29 import java.io.File;
30 import java.lang.reflect.Array;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Objects;
38 import java.util.Set;
39 import java.util.function.IntFunction;
40 
41 /**
42  * ArrayUtils contains some methods that you can call to find out
43  * the most efficient increments by which to grow arrays.
44  */
45 public class ArrayUtils {
46     private static final int CACHE_SIZE = 73;
47     private static Object[] sCache = new Object[CACHE_SIZE];
48 
49     public static final File[] EMPTY_FILE = new File[0];
50 
ArrayUtils()51     private ArrayUtils() { /* cannot be instantiated */ }
52 
newUnpaddedByteArray(int minLen)53     public static byte[] newUnpaddedByteArray(int minLen) {
54         return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
55     }
56 
newUnpaddedCharArray(int minLen)57     public static char[] newUnpaddedCharArray(int minLen) {
58         return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
59     }
60 
61     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
newUnpaddedIntArray(int minLen)62     public static int[] newUnpaddedIntArray(int minLen) {
63         return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
64     }
65 
newUnpaddedBooleanArray(int minLen)66     public static boolean[] newUnpaddedBooleanArray(int minLen) {
67         return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
68     }
69 
newUnpaddedLongArray(int minLen)70     public static long[] newUnpaddedLongArray(int minLen) {
71         return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
72     }
73 
newUnpaddedFloatArray(int minLen)74     public static float[] newUnpaddedFloatArray(int minLen) {
75         return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
76     }
77 
newUnpaddedObjectArray(int minLen)78     public static Object[] newUnpaddedObjectArray(int minLen) {
79         return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
80     }
81 
82     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
83     @SuppressWarnings("unchecked")
newUnpaddedArray(Class<T> clazz, int minLen)84     public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
85         return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen);
86     }
87 
88     /**
89      * Checks if the beginnings of two byte arrays are equal.
90      *
91      * @param array1 the first byte array
92      * @param array2 the second byte array
93      * @param length the number of bytes to check
94      * @return true if they're equal, false otherwise
95      */
equals(byte[] array1, byte[] array2, int length)96     public static boolean equals(byte[] array1, byte[] array2, int length) {
97         if (length < 0) {
98             throw new IllegalArgumentException();
99         }
100 
101         if (array1 == array2) {
102             return true;
103         }
104         if (array1 == null || array2 == null || array1.length < length || array2.length < length) {
105             return false;
106         }
107         for (int i = 0; i < length; i++) {
108             if (array1[i] != array2[i]) {
109                 return false;
110             }
111         }
112         return true;
113     }
114 
115     /**
116      * Returns an empty array of the specified type.  The intent is that
117      * it will return the same empty array every time to avoid reallocation,
118      * although this is not guaranteed.
119      */
120     @UnsupportedAppUsage
121     @SuppressWarnings("unchecked")
emptyArray(Class<T> kind)122     public static <T> T[] emptyArray(Class<T> kind) {
123         if (kind == Object.class) {
124             return (T[]) EmptyArray.OBJECT;
125         }
126 
127         int bucket = (kind.hashCode() & 0x7FFFFFFF) % CACHE_SIZE;
128         Object cache = sCache[bucket];
129 
130         if (cache == null || cache.getClass().getComponentType() != kind) {
131             cache = Array.newInstance(kind, 0);
132             sCache[bucket] = cache;
133 
134             // Log.e("cache", "new empty " + kind.getName() + " at " + bucket);
135         }
136 
137         return (T[]) cache;
138     }
139 
140     /**
141      * Returns the same array or an empty one if it's null.
142      */
emptyIfNull(@ullable T[] items, Class<T> kind)143     public static @NonNull <T> T[] emptyIfNull(@Nullable T[] items, Class<T> kind) {
144         return items != null ? items : emptyArray(kind);
145     }
146 
147     /**
148      * Checks if given array is null or has zero elements.
149      */
isEmpty(@ullable Collection<?> array)150     public static boolean isEmpty(@Nullable Collection<?> array) {
151         return array == null || array.isEmpty();
152     }
153 
154     /**
155      * Checks if given map is null or has zero elements.
156      */
isEmpty(@ullable Map<?, ?> map)157     public static boolean isEmpty(@Nullable Map<?, ?> map) {
158         return map == null || map.isEmpty();
159     }
160 
161     /**
162      * Checks if given array is null or has zero elements.
163      */
164     @UnsupportedAppUsage
isEmpty(@ullable T[] array)165     public static <T> boolean isEmpty(@Nullable T[] array) {
166         return array == null || array.length == 0;
167     }
168 
169     /**
170      * Checks if given array is null or has zero elements.
171      */
isEmpty(@ullable int[] array)172     public static boolean isEmpty(@Nullable int[] array) {
173         return array == null || array.length == 0;
174     }
175 
176     /**
177      * Checks if given array is null or has zero elements.
178      */
isEmpty(@ullable long[] array)179     public static boolean isEmpty(@Nullable long[] array) {
180         return array == null || array.length == 0;
181     }
182 
183     /**
184      * Checks if given array is null or has zero elements.
185      */
isEmpty(@ullable byte[] array)186     public static boolean isEmpty(@Nullable byte[] array) {
187         return array == null || array.length == 0;
188     }
189 
190     /**
191      * Checks if given array is null or has zero elements.
192      */
isEmpty(@ullable boolean[] array)193     public static boolean isEmpty(@Nullable boolean[] array) {
194         return array == null || array.length == 0;
195     }
196 
197     /**
198      * Length of the given array or 0 if it's null.
199      */
size(@ullable Object[] array)200     public static int size(@Nullable Object[] array) {
201         return array == null ? 0 : array.length;
202     }
203 
204     /**
205      * Length of the given collection or 0 if it's null.
206      */
size(@ullable Collection<?> collection)207     public static int size(@Nullable Collection<?> collection) {
208         return collection == null ? 0 : collection.size();
209     }
210 
211     /**
212      * Length of the given map or 0 if it's null.
213      */
size(@ullable Map<?, ?> map)214     public static int size(@Nullable Map<?, ?> map) {
215         return map == null ? 0 : map.size();
216     }
217 
218     /**
219      * Checks that value is present as at least one of the elements of the array.
220      * @param array the array to check in
221      * @param value the value to check for
222      * @return true if the value is present in the array
223      */
224     @UnsupportedAppUsage
contains(@ullable T[] array, T value)225     public static <T> boolean contains(@Nullable T[] array, T value) {
226         return indexOf(array, value) != -1;
227     }
228 
229     /**
230      * Return first index of {@code value} in {@code array}, or {@code -1} if
231      * not found.
232      */
233     @UnsupportedAppUsage
indexOf(@ullable T[] array, T value)234     public static <T> int indexOf(@Nullable T[] array, T value) {
235         if (array == null) return -1;
236         for (int i = 0; i < array.length; i++) {
237             if (Objects.equals(array[i], value)) return i;
238         }
239         return -1;
240     }
241 
242     /**
243      * Test if all {@code check} items are contained in {@code array}.
244      */
containsAll(@ullable T[] array, T[] check)245     public static <T> boolean containsAll(@Nullable T[] array, T[] check) {
246         if (check == null) return true;
247         for (T checkItem : check) {
248             if (!contains(array, checkItem)) {
249                 return false;
250             }
251         }
252         return true;
253     }
254 
255     /**
256      * Test if any {@code check} items are contained in {@code array}.
257      */
containsAny(@ullable T[] array, T[] check)258     public static <T> boolean containsAny(@Nullable T[] array, T[] check) {
259         if (check == null) return false;
260         for (T checkItem : check) {
261             if (contains(array, checkItem)) {
262                 return true;
263             }
264         }
265         return false;
266     }
267 
268     @UnsupportedAppUsage
contains(@ullable int[] array, int value)269     public static boolean contains(@Nullable int[] array, int value) {
270         if (array == null) return false;
271         for (int element : array) {
272             if (element == value) {
273                 return true;
274             }
275         }
276         return false;
277     }
278 
contains(@ullable long[] array, long value)279     public static boolean contains(@Nullable long[] array, long value) {
280         if (array == null) return false;
281         for (long element : array) {
282             if (element == value) {
283                 return true;
284             }
285         }
286         return false;
287     }
288 
contains(@ullable char[] array, char value)289     public static boolean contains(@Nullable char[] array, char value) {
290         if (array == null) return false;
291         for (char element : array) {
292             if (element == value) {
293                 return true;
294             }
295         }
296         return false;
297     }
298 
299     /**
300      * Test if all {@code check} items are contained in {@code array}.
301      */
containsAll(@ullable char[] array, char[] check)302     public static <T> boolean containsAll(@Nullable char[] array, char[] check) {
303         if (check == null) return true;
304         for (char checkItem : check) {
305             if (!contains(array, checkItem)) {
306                 return false;
307             }
308         }
309         return true;
310     }
311 
total(@ullable long[] array)312     public static long total(@Nullable long[] array) {
313         long total = 0;
314         if (array != null) {
315             for (long value : array) {
316                 total += value;
317             }
318         }
319         return total;
320     }
321 
322     /**
323      * @deprecated use {@code IntArray} instead
324      */
325     @Deprecated
convertToIntArray(List<Integer> list)326     public static int[] convertToIntArray(List<Integer> list) {
327         int[] array = new int[list.size()];
328         for (int i = 0; i < list.size(); i++) {
329             array[i] = list.get(i);
330         }
331         return array;
332     }
333 
convertToLongArray(@ullable int[] intArray)334     public static @Nullable long[] convertToLongArray(@Nullable int[] intArray) {
335         if (intArray == null) return null;
336         long[] array = new long[intArray.length];
337         for (int i = 0; i < intArray.length; i++) {
338             array[i] = (long) intArray[i];
339         }
340         return array;
341     }
342 
343     /**
344      * Combine multiple arrays into a single array.
345      *
346      * @param kind The class of the array elements
347      * @param arrays The arrays to combine
348      * @param <T> The class of the array elements (inferred from kind).
349      * @return A single array containing all the elements of the parameter arrays.
350      */
351     @SuppressWarnings("unchecked")
concatElements(Class<T> kind, @Nullable T[]... arrays)352     public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) {
353         if (arrays == null || arrays.length == 0) {
354             return createEmptyArray(kind);
355         }
356 
357         int totalLength = 0;
358         for (T[] item : arrays) {
359             if (item == null) {
360                 continue;
361             }
362 
363             totalLength += item.length;
364         }
365 
366         // Optimization for entirely empty arrays.
367         if (totalLength == 0) {
368             return createEmptyArray(kind);
369         }
370 
371         final T[] all = (T[]) Array.newInstance(kind, totalLength);
372         int pos = 0;
373         for (T[] item : arrays) {
374             if (item == null || item.length == 0) {
375                 continue;
376             }
377             System.arraycopy(item, 0, all, pos, item.length);
378             pos += item.length;
379         }
380         return all;
381     }
382 
createEmptyArray(Class<T> kind)383     private static @NonNull <T> T[] createEmptyArray(Class<T> kind) {
384         if (kind == String.class) {
385             return (T[]) EmptyArray.STRING;
386         } else if (kind == Object.class) {
387             return (T[]) EmptyArray.OBJECT;
388         }
389 
390         return (T[]) Array.newInstance(kind, 0);
391     }
392 
393 
394     /**
395      * Adds value to given array if not already present, providing set-like
396      * behavior.
397      */
398     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
399     @SuppressWarnings("unchecked")
appendElement(Class<T> kind, @Nullable T[] array, T element)400     public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
401         return appendElement(kind, array, element, false);
402     }
403 
404     /**
405      * Adds value to given array.
406      */
407     @SuppressWarnings("unchecked")
appendElement(Class<T> kind, @Nullable T[] array, T element, boolean allowDuplicates)408     public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
409             boolean allowDuplicates) {
410         final T[] result;
411         final int end;
412         if (array != null) {
413             if (!allowDuplicates && contains(array, element)) return array;
414             end = array.length;
415             result = (T[])Array.newInstance(kind, end + 1);
416             System.arraycopy(array, 0, result, 0, end);
417         } else {
418             end = 0;
419             result = (T[])Array.newInstance(kind, 1);
420         }
421         result[end] = element;
422         return result;
423     }
424 
425     /**
426      * Removes value from given array if present, providing set-like behavior.
427      */
428     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
429     @SuppressWarnings("unchecked")
removeElement(Class<T> kind, @Nullable T[] array, T element)430     public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) {
431         if (array != null) {
432             if (!contains(array, element)) return array;
433             final int length = array.length;
434             for (int i = 0; i < length; i++) {
435                 if (Objects.equals(array[i], element)) {
436                     if (length == 1) {
437                         return null;
438                     }
439                     T[] result = (T[])Array.newInstance(kind, length - 1);
440                     System.arraycopy(array, 0, result, 0, i);
441                     System.arraycopy(array, i + 1, result, i, length - i - 1);
442                     return result;
443                 }
444             }
445         }
446         return array;
447     }
448 
449     /**
450      * Adds value to given array.
451      */
appendInt(@ullable int[] cur, int val, boolean allowDuplicates)452     public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
453             boolean allowDuplicates) {
454         if (cur == null) {
455             return new int[] { val };
456         }
457         final int N = cur.length;
458         if (!allowDuplicates) {
459             for (int i = 0; i < N; i++) {
460                 if (cur[i] == val) {
461                     return cur;
462                 }
463             }
464         }
465         int[] ret = new int[N + 1];
466         System.arraycopy(cur, 0, ret, 0, N);
467         ret[N] = val;
468         return ret;
469     }
470 
471     /**
472      * Adds value to given array if not already present, providing set-like
473      * behavior.
474      */
475     @UnsupportedAppUsage
appendInt(@ullable int[] cur, int val)476     public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
477         return appendInt(cur, val, false);
478     }
479 
480     /**
481      * Removes value from given array if present, providing set-like behavior.
482      */
removeInt(@ullable int[] cur, int val)483     public static @Nullable int[] removeInt(@Nullable int[] cur, int val) {
484         if (cur == null) {
485             return null;
486         }
487         final int N = cur.length;
488         for (int i = 0; i < N; i++) {
489             if (cur[i] == val) {
490                 int[] ret = new int[N - 1];
491                 if (i > 0) {
492                     System.arraycopy(cur, 0, ret, 0, i);
493                 }
494                 if (i < (N - 1)) {
495                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
496                 }
497                 return ret;
498             }
499         }
500         return cur;
501     }
502 
503     /**
504      * Removes value from given array if present, providing set-like behavior.
505      */
removeString(@ullable String[] cur, String val)506     public static @Nullable String[] removeString(@Nullable String[] cur, String val) {
507         if (cur == null) {
508             return null;
509         }
510         final int N = cur.length;
511         for (int i = 0; i < N; i++) {
512             if (Objects.equals(cur[i], val)) {
513                 String[] ret = new String[N - 1];
514                 if (i > 0) {
515                     System.arraycopy(cur, 0, ret, 0, i);
516                 }
517                 if (i < (N - 1)) {
518                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
519                 }
520                 return ret;
521             }
522         }
523         return cur;
524     }
525 
526     /**
527      * Adds value to given array if not already present, providing set-like
528      * behavior.
529      */
appendLong(@ullable long[] cur, long val, boolean allowDuplicates)530     public static @NonNull long[] appendLong(@Nullable long[] cur, long val,
531             boolean allowDuplicates) {
532         if (cur == null) {
533             return new long[] { val };
534         }
535         final int N = cur.length;
536         if (!allowDuplicates) {
537             for (int i = 0; i < N; i++) {
538                 if (cur[i] == val) {
539                     return cur;
540                 }
541             }
542         }
543         long[] ret = new long[N + 1];
544         System.arraycopy(cur, 0, ret, 0, N);
545         ret[N] = val;
546         return ret;
547     }
548 
549     /**
550      * Adds value to given array if not already present, providing set-like
551      * behavior.
552      */
appendLong(@ullable long[] cur, long val)553     public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
554         return appendLong(cur, val, false);
555     }
556 
557     /**
558      * Removes value from given array if present, providing set-like behavior.
559      */
removeLong(@ullable long[] cur, long val)560     public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
561         if (cur == null) {
562             return null;
563         }
564         final int N = cur.length;
565         for (int i = 0; i < N; i++) {
566             if (cur[i] == val) {
567                 long[] ret = new long[N - 1];
568                 if (i > 0) {
569                     System.arraycopy(cur, 0, ret, 0, i);
570                 }
571                 if (i < (N - 1)) {
572                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
573                 }
574                 return ret;
575             }
576         }
577         return cur;
578     }
579 
cloneOrNull(@ullable long[] array)580     public static @Nullable long[] cloneOrNull(@Nullable long[] array) {
581         return (array != null) ? array.clone() : null;
582     }
583 
584     /**
585      * Clones an array or returns null if the array is null.
586      */
cloneOrNull(@ullable T[] array)587     public static @Nullable <T> T[] cloneOrNull(@Nullable T[] array) {
588         return (array != null) ? array.clone() : null;
589     }
590 
cloneOrNull(@ullable ArraySet<T> array)591     public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
592         return (array != null) ? new ArraySet<T>(array) : null;
593     }
594 
add(@ullable ArraySet<T> cur, T val)595     public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
596         if (cur == null) {
597             cur = new ArraySet<>();
598         }
599         cur.add(val);
600         return cur;
601     }
602 
603     /**
604      * Similar to {@link Set#addAll(Collection)}}, but with support for set values of {@code null}.
605      */
addAll(@ullable ArraySet<T> cur, @Nullable Collection<T> val)606     public static @NonNull <T> ArraySet<T> addAll(@Nullable ArraySet<T> cur,
607             @Nullable Collection<T> val) {
608         if (cur == null) {
609             cur = new ArraySet<>();
610         }
611         if (val != null) {
612             cur.addAll(val);
613         }
614         return cur;
615     }
616 
remove(@ullable ArraySet<T> cur, T val)617     public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) {
618         if (cur == null) {
619             return null;
620         }
621         cur.remove(val);
622         if (cur.isEmpty()) {
623             return null;
624         } else {
625             return cur;
626         }
627     }
628 
add(@ullable ArrayList<T> cur, T val)629     public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, T val) {
630         if (cur == null) {
631             cur = new ArrayList<>();
632         }
633         cur.add(val);
634         return cur;
635     }
636 
add(@ullable ArrayList<T> cur, int index, T val)637     public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, int index, T val) {
638         if (cur == null) {
639             cur = new ArrayList<>();
640         }
641         cur.add(index, val);
642         return cur;
643     }
644 
remove(@ullable ArrayList<T> cur, T val)645     public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) {
646         if (cur == null) {
647             return null;
648         }
649         cur.remove(val);
650         if (cur.isEmpty()) {
651             return null;
652         } else {
653             return cur;
654         }
655     }
656 
contains(@ullable Collection<T> cur, T val)657     public static <T> boolean contains(@Nullable Collection<T> cur, T val) {
658         return (cur != null) ? cur.contains(val) : false;
659     }
660 
trimToSize(@ullable T[] array, int size)661     public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
662         if (array == null || size == 0) {
663             return null;
664         } else if (array.length == size) {
665             return array;
666         } else {
667             return Arrays.copyOf(array, size);
668         }
669     }
670 
671     /**
672      * Returns true if the two ArrayLists are equal with respect to the objects they contain.
673      * The objects must be in the same order and be reference equal (== not .equals()).
674      */
referenceEquals(ArrayList<T> a, ArrayList<T> b)675     public static <T> boolean referenceEquals(ArrayList<T> a, ArrayList<T> b) {
676         if (a == b) {
677             return true;
678         }
679 
680         final int sizeA = a.size();
681         final int sizeB = b.size();
682         if (a == null || b == null || sizeA != sizeB) {
683             return false;
684         }
685 
686         boolean diff = false;
687         for (int i = 0; i < sizeA && !diff; i++) {
688             diff |= a.get(i) != b.get(i);
689         }
690         return !diff;
691     }
692 
693     /**
694      * Removes elements that match the predicate in an efficient way that alters the order of
695      * elements in the collection. This should only be used if order is not important.
696      * @param collection The ArrayList from which to remove elements.
697      * @param predicate The predicate that each element is tested against.
698      * @return the number of elements removed.
699      */
unstableRemoveIf(@ullable ArrayList<T> collection, @NonNull java.util.function.Predicate<T> predicate)700     public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection,
701                                            @NonNull java.util.function.Predicate<T> predicate) {
702         if (collection == null) {
703             return 0;
704         }
705 
706         final int size = collection.size();
707         int leftIdx = 0;
708         int rightIdx = size - 1;
709         while (leftIdx <= rightIdx) {
710             // Find the next element to remove moving left to right.
711             while (leftIdx < size && !predicate.test(collection.get(leftIdx))) {
712                 leftIdx++;
713             }
714 
715             // Find the next element to keep moving right to left.
716             while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) {
717                 rightIdx--;
718             }
719 
720             if (leftIdx >= rightIdx) {
721                 // Done.
722                 break;
723             }
724 
725             Collections.swap(collection, leftIdx, rightIdx);
726             leftIdx++;
727             rightIdx--;
728         }
729 
730         // leftIdx is now at the end.
731         for (int i = size - 1; i >= leftIdx; i--) {
732             collection.remove(i);
733         }
734         return size - leftIdx;
735     }
736 
defeatNullable(@ullable int[] val)737     public static @NonNull int[] defeatNullable(@Nullable int[] val) {
738         return (val != null) ? val : EmptyArray.INT;
739     }
740 
defeatNullable(@ullable String[] val)741     public static @NonNull String[] defeatNullable(@Nullable String[] val) {
742         return (val != null) ? val : EmptyArray.STRING;
743     }
744 
defeatNullable(@ullable File[] val)745     public static @NonNull File[] defeatNullable(@Nullable File[] val) {
746         return (val != null) ? val : EMPTY_FILE;
747     }
748 
749     /**
750      * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
751      *
752      * @param len length of the array. Must be non-negative
753      * @param index the index to check
754      * @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array
755      */
checkBounds(int len, int index)756     public static void checkBounds(int len, int index) {
757         if (index < 0 || len <= index) {
758             throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
759         }
760     }
761 
762     /**
763      * Throws {@link ArrayIndexOutOfBoundsException} if the range is out of bounds.
764      * @param len length of the array. Must be non-negative
765      * @param offset start index of the range. Must be non-negative
766      * @param count length of the range. Must be non-negative
767      * @throws ArrayIndexOutOfBoundsException if the range from {@code offset} with length
768      * {@code count} is out of bounds of the array
769      */
throwsIfOutOfBounds(int len, int offset, int count)770     public static void throwsIfOutOfBounds(int len, int offset, int count) {
771         if (len < 0) {
772             throw new ArrayIndexOutOfBoundsException("Negative length: " + len);
773         }
774 
775         if ((offset | count) < 0 || offset > len - count) {
776             throw new ArrayIndexOutOfBoundsException(
777                     "length=" + len + "; regionStart=" + offset + "; regionLength=" + count);
778         }
779     }
780 
781     /**
782      * Returns an array with values from {@code val} minus {@code null} values
783      *
784      * @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new}
785      */
filterNotNull(T[] val, IntFunction<T[]> arrayConstructor)786     public static <T> T[] filterNotNull(T[] val, IntFunction<T[]> arrayConstructor) {
787         int nullCount = 0;
788         int size = size(val);
789         for (int i = 0; i < size; i++) {
790             if (val[i] == null) {
791                 nullCount++;
792             }
793         }
794         if (nullCount == 0) {
795             return val;
796         }
797         T[] result = arrayConstructor.apply(size - nullCount);
798         int outIdx = 0;
799         for (int i = 0; i < size; i++) {
800             if (val[i] != null) {
801                 result[outIdx++] = val[i];
802             }
803         }
804         return result;
805     }
806 
807     /**
808      * Returns an array containing elements from the given one that match the given predicate.
809      * The returned array may, in some cases, be the reference to the input array.
810      */
filter(@ullable T[] items, @NonNull IntFunction<T[]> arrayConstructor, @NonNull java.util.function.Predicate<T> predicate)811     public static @Nullable <T> T[] filter(@Nullable T[] items,
812             @NonNull IntFunction<T[]> arrayConstructor,
813             @NonNull java.util.function.Predicate<T> predicate) {
814         if (isEmpty(items)) {
815             return items;
816         }
817 
818         int matchesCount = 0;
819         int size = size(items);
820         final boolean[] tests = new boolean[size];
821         for (int i = 0; i < size; i++) {
822             tests[i] = predicate.test(items[i]);
823             if (tests[i]) {
824                 matchesCount++;
825             }
826         }
827         if (matchesCount == items.length) {
828             return items;
829         }
830         T[] result = arrayConstructor.apply(matchesCount);
831         if (matchesCount == 0) {
832             return result;
833         }
834         int outIdx = 0;
835         for (int i = 0; i < size; i++) {
836             if (tests[i]) {
837                 result[outIdx++] = items[i];
838             }
839         }
840         return result;
841     }
842 
startsWith(byte[] cur, byte[] val)843     public static boolean startsWith(byte[] cur, byte[] val) {
844         if (cur == null || val == null) return false;
845         if (cur.length < val.length) return false;
846         for (int i = 0; i < val.length; i++) {
847             if (cur[i] != val[i]) return false;
848         }
849         return true;
850     }
851 
852     /**
853      * Returns the first element from the array for which
854      * condition {@code predicate} is true, or null if there is no such element
855      */
find(@ullable T[] items, @NonNull java.util.function.Predicate<T> predicate)856     public static @Nullable <T> T find(@Nullable T[] items,
857             @NonNull java.util.function.Predicate<T> predicate) {
858         if (isEmpty(items)) return null;
859         for (final T item : items) {
860             if (predicate.test(item)) return item;
861         }
862         return null;
863     }
864 
deepToString(Object value)865     public static String deepToString(Object value) {
866         if (value != null && value.getClass().isArray()) {
867             if (value.getClass() == boolean[].class) {
868                 return Arrays.toString((boolean[]) value);
869             } else if (value.getClass() == byte[].class) {
870                 return Arrays.toString((byte[]) value);
871             } else if (value.getClass() == char[].class) {
872                 return Arrays.toString((char[]) value);
873             } else if (value.getClass() == double[].class) {
874                 return Arrays.toString((double[]) value);
875             } else if (value.getClass() == float[].class) {
876                 return Arrays.toString((float[]) value);
877             } else if (value.getClass() == int[].class) {
878                 return Arrays.toString((int[]) value);
879             } else if (value.getClass() == long[].class) {
880                 return Arrays.toString((long[]) value);
881             } else if (value.getClass() == short[].class) {
882                 return Arrays.toString((short[]) value);
883             } else {
884                 return Arrays.deepToString((Object[]) value);
885             }
886         } else {
887             return String.valueOf(value);
888         }
889     }
890 
firstOrNull(T[] items)891     public static @Nullable <T> T firstOrNull(T[] items) {
892         return items.length > 0 ? items[0] : null;
893     }
894 }
895