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.util.ArraySet;
22 
23 import dalvik.system.VMRuntime;
24 
25 import libcore.util.EmptyArray;
26 
27 import java.lang.reflect.Array;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Objects;
34 
35 /**
36  * ArrayUtils contains some methods that you can call to find out
37  * the most efficient increments by which to grow arrays.
38  */
39 public class ArrayUtils {
40     private static final int CACHE_SIZE = 73;
41     private static Object[] sCache = new Object[CACHE_SIZE];
42 
ArrayUtils()43     private ArrayUtils() { /* cannot be instantiated */ }
44 
newUnpaddedByteArray(int minLen)45     public static byte[] newUnpaddedByteArray(int minLen) {
46         return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
47     }
48 
newUnpaddedCharArray(int minLen)49     public static char[] newUnpaddedCharArray(int minLen) {
50         return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
51     }
52 
newUnpaddedIntArray(int minLen)53     public static int[] newUnpaddedIntArray(int minLen) {
54         return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
55     }
56 
newUnpaddedBooleanArray(int minLen)57     public static boolean[] newUnpaddedBooleanArray(int minLen) {
58         return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
59     }
60 
newUnpaddedLongArray(int minLen)61     public static long[] newUnpaddedLongArray(int minLen) {
62         return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
63     }
64 
newUnpaddedFloatArray(int minLen)65     public static float[] newUnpaddedFloatArray(int minLen) {
66         return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
67     }
68 
newUnpaddedObjectArray(int minLen)69     public static Object[] newUnpaddedObjectArray(int minLen) {
70         return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
71     }
72 
73     @SuppressWarnings("unchecked")
newUnpaddedArray(Class<T> clazz, int minLen)74     public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
75         return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen);
76     }
77 
78     /**
79      * Checks if the beginnings of two byte arrays are equal.
80      *
81      * @param array1 the first byte array
82      * @param array2 the second byte array
83      * @param length the number of bytes to check
84      * @return true if they're equal, false otherwise
85      */
equals(byte[] array1, byte[] array2, int length)86     public static boolean equals(byte[] array1, byte[] array2, int length) {
87         if (length < 0) {
88             throw new IllegalArgumentException();
89         }
90 
91         if (array1 == array2) {
92             return true;
93         }
94         if (array1 == null || array2 == null || array1.length < length || array2.length < length) {
95             return false;
96         }
97         for (int i = 0; i < length; i++) {
98             if (array1[i] != array2[i]) {
99                 return false;
100             }
101         }
102         return true;
103     }
104 
105     /**
106      * Returns an empty array of the specified type.  The intent is that
107      * it will return the same empty array every time to avoid reallocation,
108      * although this is not guaranteed.
109      */
110     @SuppressWarnings("unchecked")
emptyArray(Class<T> kind)111     public static <T> T[] emptyArray(Class<T> kind) {
112         if (kind == Object.class) {
113             return (T[]) EmptyArray.OBJECT;
114         }
115 
116         int bucket = (kind.hashCode() & 0x7FFFFFFF) % CACHE_SIZE;
117         Object cache = sCache[bucket];
118 
119         if (cache == null || cache.getClass().getComponentType() != kind) {
120             cache = Array.newInstance(kind, 0);
121             sCache[bucket] = cache;
122 
123             // Log.e("cache", "new empty " + kind.getName() + " at " + bucket);
124         }
125 
126         return (T[]) cache;
127     }
128 
129     /**
130      * Checks if given array is null or has zero elements.
131      */
isEmpty(@ullable Collection<?> array)132     public static boolean isEmpty(@Nullable Collection<?> array) {
133         return array == null || array.isEmpty();
134     }
135 
136     /**
137      * Checks if given array is null or has zero elements.
138      */
isEmpty(@ullable T[] array)139     public static <T> boolean isEmpty(@Nullable T[] array) {
140         return array == null || array.length == 0;
141     }
142 
143     /**
144      * Checks if given array is null or has zero elements.
145      */
isEmpty(@ullable int[] array)146     public static boolean isEmpty(@Nullable int[] array) {
147         return array == null || array.length == 0;
148     }
149 
150     /**
151      * Checks if given array is null or has zero elements.
152      */
isEmpty(@ullable long[] array)153     public static boolean isEmpty(@Nullable long[] array) {
154         return array == null || array.length == 0;
155     }
156 
157     /**
158      * Checks if given array is null or has zero elements.
159      */
isEmpty(@ullable byte[] array)160     public static boolean isEmpty(@Nullable byte[] array) {
161         return array == null || array.length == 0;
162     }
163 
164     /**
165      * Checks if given array is null or has zero elements.
166      */
isEmpty(@ullable boolean[] array)167     public static boolean isEmpty(@Nullable boolean[] array) {
168         return array == null || array.length == 0;
169     }
170 
171     /**
172      * Length of the given array or 0 if it's null.
173      */
size(@ullable Object[] array)174     public static int size(@Nullable Object[] array) {
175         return array == null ? 0 : array.length;
176     }
177 
178     /**
179      * Checks that value is present as at least one of the elements of the array.
180      * @param array the array to check in
181      * @param value the value to check for
182      * @return true if the value is present in the array
183      */
contains(@ullable T[] array, T value)184     public static <T> boolean contains(@Nullable T[] array, T value) {
185         return indexOf(array, value) != -1;
186     }
187 
188     /**
189      * Return first index of {@code value} in {@code array}, or {@code -1} if
190      * not found.
191      */
indexOf(@ullable T[] array, T value)192     public static <T> int indexOf(@Nullable T[] array, T value) {
193         if (array == null) return -1;
194         for (int i = 0; i < array.length; i++) {
195             if (Objects.equals(array[i], value)) return i;
196         }
197         return -1;
198     }
199 
200     /**
201      * Test if all {@code check} items are contained in {@code array}.
202      */
containsAll(@ullable T[] array, T[] check)203     public static <T> boolean containsAll(@Nullable T[] array, T[] check) {
204         if (check == null) return true;
205         for (T checkItem : check) {
206             if (!contains(array, checkItem)) {
207                 return false;
208             }
209         }
210         return true;
211     }
212 
213     /**
214      * Test if any {@code check} items are contained in {@code array}.
215      */
containsAny(@ullable T[] array, T[] check)216     public static <T> boolean containsAny(@Nullable T[] array, T[] check) {
217         if (check == null) return false;
218         for (T checkItem : check) {
219             if (contains(array, checkItem)) {
220                 return true;
221             }
222         }
223         return false;
224     }
225 
contains(@ullable int[] array, int value)226     public static boolean contains(@Nullable int[] array, int value) {
227         if (array == null) return false;
228         for (int element : array) {
229             if (element == value) {
230                 return true;
231             }
232         }
233         return false;
234     }
235 
contains(@ullable long[] array, long value)236     public static boolean contains(@Nullable long[] array, long value) {
237         if (array == null) return false;
238         for (long element : array) {
239             if (element == value) {
240                 return true;
241             }
242         }
243         return false;
244     }
245 
contains(@ullable char[] array, char value)246     public static boolean contains(@Nullable char[] array, char value) {
247         if (array == null) return false;
248         for (char element : array) {
249             if (element == value) {
250                 return true;
251             }
252         }
253         return false;
254     }
255 
256     /**
257      * Test if all {@code check} items are contained in {@code array}.
258      */
containsAll(@ullable char[] array, char[] check)259     public static <T> boolean containsAll(@Nullable char[] array, char[] check) {
260         if (check == null) return true;
261         for (char checkItem : check) {
262             if (!contains(array, checkItem)) {
263                 return false;
264             }
265         }
266         return true;
267     }
268 
total(@ullable long[] array)269     public static long total(@Nullable long[] array) {
270         long total = 0;
271         if (array != null) {
272             for (long value : array) {
273                 total += value;
274             }
275         }
276         return total;
277     }
278 
convertToIntArray(List<Integer> list)279     public static int[] convertToIntArray(List<Integer> list) {
280         int[] array = new int[list.size()];
281         for (int i = 0; i < list.size(); i++) {
282             array[i] = list.get(i);
283         }
284         return array;
285     }
286 
287     /**
288      * Adds value to given array if not already present, providing set-like
289      * behavior.
290      */
291     @SuppressWarnings("unchecked")
appendElement(Class<T> kind, @Nullable T[] array, T element)292     public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
293         return appendElement(kind, array, element, false);
294     }
295 
296     /**
297      * Adds value to given array.
298      */
299     @SuppressWarnings("unchecked")
appendElement(Class<T> kind, @Nullable T[] array, T element, boolean allowDuplicates)300     public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
301             boolean allowDuplicates) {
302         final T[] result;
303         final int end;
304         if (array != null) {
305             if (!allowDuplicates && contains(array, element)) return array;
306             end = array.length;
307             result = (T[])Array.newInstance(kind, end + 1);
308             System.arraycopy(array, 0, result, 0, end);
309         } else {
310             end = 0;
311             result = (T[])Array.newInstance(kind, 1);
312         }
313         result[end] = element;
314         return result;
315     }
316 
317     /**
318      * Removes value from given array if present, providing set-like behavior.
319      */
320     @SuppressWarnings("unchecked")
removeElement(Class<T> kind, @Nullable T[] array, T element)321     public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) {
322         if (array != null) {
323             if (!contains(array, element)) return array;
324             final int length = array.length;
325             for (int i = 0; i < length; i++) {
326                 if (Objects.equals(array[i], element)) {
327                     if (length == 1) {
328                         return null;
329                     }
330                     T[] result = (T[])Array.newInstance(kind, length - 1);
331                     System.arraycopy(array, 0, result, 0, i);
332                     System.arraycopy(array, i + 1, result, i, length - i - 1);
333                     return result;
334                 }
335             }
336         }
337         return array;
338     }
339 
340     /**
341      * Adds value to given array.
342      */
appendInt(@ullable int[] cur, int val, boolean allowDuplicates)343     public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
344             boolean allowDuplicates) {
345         if (cur == null) {
346             return new int[] { val };
347         }
348         final int N = cur.length;
349         if (!allowDuplicates) {
350             for (int i = 0; i < N; i++) {
351                 if (cur[i] == val) {
352                     return cur;
353                 }
354             }
355         }
356         int[] ret = new int[N + 1];
357         System.arraycopy(cur, 0, ret, 0, N);
358         ret[N] = val;
359         return ret;
360     }
361 
362     /**
363      * Adds value to given array if not already present, providing set-like
364      * behavior.
365      */
appendInt(@ullable int[] cur, int val)366     public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
367         return appendInt(cur, val, false);
368     }
369 
370     /**
371      * Removes value from given array if present, providing set-like behavior.
372      */
removeInt(@ullable int[] cur, int val)373     public static @Nullable int[] removeInt(@Nullable int[] cur, int val) {
374         if (cur == null) {
375             return null;
376         }
377         final int N = cur.length;
378         for (int i = 0; i < N; i++) {
379             if (cur[i] == val) {
380                 int[] ret = new int[N - 1];
381                 if (i > 0) {
382                     System.arraycopy(cur, 0, ret, 0, i);
383                 }
384                 if (i < (N - 1)) {
385                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
386                 }
387                 return ret;
388             }
389         }
390         return cur;
391     }
392 
393     /**
394      * Removes value from given array if present, providing set-like behavior.
395      */
removeString(@ullable String[] cur, String val)396     public static @Nullable String[] removeString(@Nullable String[] cur, String val) {
397         if (cur == null) {
398             return null;
399         }
400         final int N = cur.length;
401         for (int i = 0; i < N; i++) {
402             if (Objects.equals(cur[i], val)) {
403                 String[] ret = new String[N - 1];
404                 if (i > 0) {
405                     System.arraycopy(cur, 0, ret, 0, i);
406                 }
407                 if (i < (N - 1)) {
408                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
409                 }
410                 return ret;
411             }
412         }
413         return cur;
414     }
415 
416     /**
417      * Adds value to given array if not already present, providing set-like
418      * behavior.
419      */
appendLong(@ullable long[] cur, long val)420     public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
421         if (cur == null) {
422             return new long[] { val };
423         }
424         final int N = cur.length;
425         for (int i = 0; i < N; i++) {
426             if (cur[i] == val) {
427                 return cur;
428             }
429         }
430         long[] ret = new long[N + 1];
431         System.arraycopy(cur, 0, ret, 0, N);
432         ret[N] = val;
433         return ret;
434     }
435 
436     /**
437      * Removes value from given array if present, providing set-like behavior.
438      */
removeLong(@ullable long[] cur, long val)439     public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
440         if (cur == null) {
441             return null;
442         }
443         final int N = cur.length;
444         for (int i = 0; i < N; i++) {
445             if (cur[i] == val) {
446                 long[] ret = new long[N - 1];
447                 if (i > 0) {
448                     System.arraycopy(cur, 0, ret, 0, i);
449                 }
450                 if (i < (N - 1)) {
451                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
452                 }
453                 return ret;
454             }
455         }
456         return cur;
457     }
458 
cloneOrNull(@ullable long[] array)459     public static @Nullable long[] cloneOrNull(@Nullable long[] array) {
460         return (array != null) ? array.clone() : null;
461     }
462 
cloneOrNull(@ullable ArraySet<T> array)463     public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
464         return (array != null) ? new ArraySet<T>(array) : null;
465     }
466 
add(@ullable ArraySet<T> cur, T val)467     public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
468         if (cur == null) {
469             cur = new ArraySet<>();
470         }
471         cur.add(val);
472         return cur;
473     }
474 
remove(@ullable ArraySet<T> cur, T val)475     public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) {
476         if (cur == null) {
477             return null;
478         }
479         cur.remove(val);
480         if (cur.isEmpty()) {
481             return null;
482         } else {
483             return cur;
484         }
485     }
486 
add(@ullable ArrayList<T> cur, T val)487     public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, T val) {
488         if (cur == null) {
489             cur = new ArrayList<>();
490         }
491         cur.add(val);
492         return cur;
493     }
494 
remove(@ullable ArrayList<T> cur, T val)495     public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) {
496         if (cur == null) {
497             return null;
498         }
499         cur.remove(val);
500         if (cur.isEmpty()) {
501             return null;
502         } else {
503             return cur;
504         }
505     }
506 
contains(@ullable Collection<T> cur, T val)507     public static <T> boolean contains(@Nullable Collection<T> cur, T val) {
508         return (cur != null) ? cur.contains(val) : false;
509     }
510 
trimToSize(@ullable T[] array, int size)511     public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
512         if (array == null || size == 0) {
513             return null;
514         } else if (array.length == size) {
515             return array;
516         } else {
517             return Arrays.copyOf(array, size);
518         }
519     }
520 
521     /**
522      * Returns true if the two ArrayLists are equal with respect to the objects they contain.
523      * The objects must be in the same order and be reference equal (== not .equals()).
524      */
referenceEquals(ArrayList<T> a, ArrayList<T> b)525     public static <T> boolean referenceEquals(ArrayList<T> a, ArrayList<T> b) {
526         if (a == b) {
527             return true;
528         }
529 
530         final int sizeA = a.size();
531         final int sizeB = b.size();
532         if (a == null || b == null || sizeA != sizeB) {
533             return false;
534         }
535 
536         boolean diff = false;
537         for (int i = 0; i < sizeA && !diff; i++) {
538             diff |= a.get(i) != b.get(i);
539         }
540         return !diff;
541     }
542 
543     /**
544      * Removes elements that match the predicate in an efficient way that alters the order of
545      * elements in the collection. This should only be used if order is not important.
546      * @param collection The ArrayList from which to remove elements.
547      * @param predicate The predicate that each element is tested against.
548      * @return the number of elements removed.
549      */
unstableRemoveIf(@ullable ArrayList<T> collection, @NonNull java.util.function.Predicate<T> predicate)550     public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection,
551                                            @NonNull java.util.function.Predicate<T> predicate) {
552         if (collection == null) {
553             return 0;
554         }
555 
556         final int size = collection.size();
557         int leftIdx = 0;
558         int rightIdx = size - 1;
559         while (leftIdx <= rightIdx) {
560             // Find the next element to remove moving left to right.
561             while (leftIdx < size && !predicate.test(collection.get(leftIdx))) {
562                 leftIdx++;
563             }
564 
565             // Find the next element to keep moving right to left.
566             while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) {
567                 rightIdx--;
568             }
569 
570             if (leftIdx >= rightIdx) {
571                 // Done.
572                 break;
573             }
574 
575             Collections.swap(collection, leftIdx, rightIdx);
576             leftIdx++;
577             rightIdx--;
578         }
579 
580         // leftIdx is now at the end.
581         for (int i = size - 1; i >= leftIdx; i--) {
582             collection.remove(i);
583         }
584         return size - leftIdx;
585     }
586 
defeatNullable(@ullable String[] val)587     public static @NonNull String[] defeatNullable(@Nullable String[] val) {
588         return (val != null) ? val : EmptyArray.STRING;
589     }
590 }
591