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