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