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      * Checks that value is present as at least one of the elements of the array.
173      * @param array the array to check in
174      * @param value the value to check for
175      * @return true if the value is present in the array
176      */
contains(@ullable T[] array, T value)177     public static <T> boolean contains(@Nullable T[] array, T value) {
178         return indexOf(array, value) != -1;
179     }
180 
181     /**
182      * Return first index of {@code value} in {@code array}, or {@code -1} if
183      * not found.
184      */
indexOf(@ullable T[] array, T value)185     public static <T> int indexOf(@Nullable T[] array, T value) {
186         if (array == null) return -1;
187         for (int i = 0; i < array.length; i++) {
188             if (Objects.equals(array[i], value)) return i;
189         }
190         return -1;
191     }
192 
193     /**
194      * Test if all {@code check} items are contained in {@code array}.
195      */
containsAll(@ullable T[] array, T[] check)196     public static <T> boolean containsAll(@Nullable T[] array, T[] check) {
197         if (check == null) return true;
198         for (T checkItem : check) {
199             if (!contains(array, checkItem)) {
200                 return false;
201             }
202         }
203         return true;
204     }
205 
206     /**
207      * Test if any {@code check} items are contained in {@code array}.
208      */
containsAny(@ullable T[] array, T[] check)209     public static <T> boolean containsAny(@Nullable T[] array, T[] check) {
210         if (check == null) return false;
211         for (T checkItem : check) {
212             if (contains(array, checkItem)) {
213                 return true;
214             }
215         }
216         return false;
217     }
218 
contains(@ullable int[] array, int value)219     public static boolean contains(@Nullable int[] array, int value) {
220         if (array == null) return false;
221         for (int element : array) {
222             if (element == value) {
223                 return true;
224             }
225         }
226         return false;
227     }
228 
contains(@ullable long[] array, long value)229     public static boolean contains(@Nullable long[] array, long value) {
230         if (array == null) return false;
231         for (long element : array) {
232             if (element == value) {
233                 return true;
234             }
235         }
236         return false;
237     }
238 
total(@ullable long[] array)239     public static long total(@Nullable long[] array) {
240         long total = 0;
241         if (array != null) {
242             for (long value : array) {
243                 total += value;
244             }
245         }
246         return total;
247     }
248 
convertToIntArray(List<Integer> list)249     public static int[] convertToIntArray(List<Integer> list) {
250         int[] array = new int[list.size()];
251         for (int i = 0; i < list.size(); i++) {
252             array[i] = list.get(i);
253         }
254         return array;
255     }
256 
257     /**
258      * Adds value to given array if not already present, providing set-like
259      * behavior.
260      */
261     @SuppressWarnings("unchecked")
appendElement(Class<T> kind, @Nullable T[] array, T element)262     public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
263         final T[] result;
264         final int end;
265         if (array != null) {
266             if (contains(array, element)) return array;
267             end = array.length;
268             result = (T[])Array.newInstance(kind, end + 1);
269             System.arraycopy(array, 0, result, 0, end);
270         } else {
271             end = 0;
272             result = (T[])Array.newInstance(kind, 1);
273         }
274         result[end] = element;
275         return result;
276     }
277 
278     /**
279      * Removes value from given array if present, providing set-like behavior.
280      */
281     @SuppressWarnings("unchecked")
removeElement(Class<T> kind, @Nullable T[] array, T element)282     public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) {
283         if (array != null) {
284             if (!contains(array, element)) return array;
285             final int length = array.length;
286             for (int i = 0; i < length; i++) {
287                 if (Objects.equals(array[i], element)) {
288                     if (length == 1) {
289                         return null;
290                     }
291                     T[] result = (T[])Array.newInstance(kind, length - 1);
292                     System.arraycopy(array, 0, result, 0, i);
293                     System.arraycopy(array, i + 1, result, i, length - i - 1);
294                     return result;
295                 }
296             }
297         }
298         return array;
299     }
300 
301     /**
302      * Adds value to given array if not already present, providing set-like
303      * behavior.
304      */
appendInt(@ullable int[] cur, int val)305     public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
306         if (cur == null) {
307             return new int[] { val };
308         }
309         final int N = cur.length;
310         for (int i = 0; i < N; i++) {
311             if (cur[i] == val) {
312                 return cur;
313             }
314         }
315         int[] ret = new int[N + 1];
316         System.arraycopy(cur, 0, ret, 0, N);
317         ret[N] = val;
318         return ret;
319     }
320 
321     /**
322      * Removes value from given array if present, providing set-like behavior.
323      */
removeInt(@ullable int[] cur, int val)324     public static @Nullable int[] removeInt(@Nullable int[] cur, int val) {
325         if (cur == null) {
326             return null;
327         }
328         final int N = cur.length;
329         for (int i = 0; i < N; i++) {
330             if (cur[i] == val) {
331                 int[] ret = new int[N - 1];
332                 if (i > 0) {
333                     System.arraycopy(cur, 0, ret, 0, i);
334                 }
335                 if (i < (N - 1)) {
336                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
337                 }
338                 return ret;
339             }
340         }
341         return cur;
342     }
343 
344     /**
345      * Removes value from given array if present, providing set-like behavior.
346      */
removeString(@ullable String[] cur, String val)347     public static @Nullable String[] removeString(@Nullable String[] cur, String val) {
348         if (cur == null) {
349             return null;
350         }
351         final int N = cur.length;
352         for (int i = 0; i < N; i++) {
353             if (Objects.equals(cur[i], val)) {
354                 String[] ret = new String[N - 1];
355                 if (i > 0) {
356                     System.arraycopy(cur, 0, ret, 0, i);
357                 }
358                 if (i < (N - 1)) {
359                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
360                 }
361                 return ret;
362             }
363         }
364         return cur;
365     }
366 
367     /**
368      * Adds value to given array if not already present, providing set-like
369      * behavior.
370      */
appendLong(@ullable long[] cur, long val)371     public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
372         if (cur == null) {
373             return new long[] { val };
374         }
375         final int N = cur.length;
376         for (int i = 0; i < N; i++) {
377             if (cur[i] == val) {
378                 return cur;
379             }
380         }
381         long[] ret = new long[N + 1];
382         System.arraycopy(cur, 0, ret, 0, N);
383         ret[N] = val;
384         return ret;
385     }
386 
387     /**
388      * Removes value from given array if present, providing set-like behavior.
389      */
removeLong(@ullable long[] cur, long val)390     public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
391         if (cur == null) {
392             return null;
393         }
394         final int N = cur.length;
395         for (int i = 0; i < N; i++) {
396             if (cur[i] == val) {
397                 long[] ret = new long[N - 1];
398                 if (i > 0) {
399                     System.arraycopy(cur, 0, ret, 0, i);
400                 }
401                 if (i < (N - 1)) {
402                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
403                 }
404                 return ret;
405             }
406         }
407         return cur;
408     }
409 
cloneOrNull(@ullable long[] array)410     public static @Nullable long[] cloneOrNull(@Nullable long[] array) {
411         return (array != null) ? array.clone() : null;
412     }
413 
cloneOrNull(@ullable ArraySet<T> array)414     public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
415         return (array != null) ? new ArraySet<T>(array) : null;
416     }
417 
add(@ullable ArraySet<T> cur, T val)418     public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
419         if (cur == null) {
420             cur = new ArraySet<>();
421         }
422         cur.add(val);
423         return cur;
424     }
425 
remove(@ullable ArraySet<T> cur, T val)426     public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) {
427         if (cur == null) {
428             return null;
429         }
430         cur.remove(val);
431         if (cur.isEmpty()) {
432             return null;
433         } else {
434             return cur;
435         }
436     }
437 
contains(@ullable ArraySet<T> cur, T val)438     public static <T> boolean contains(@Nullable ArraySet<T> cur, T val) {
439         return (cur != null) ? cur.contains(val) : false;
440     }
441 
add(@ullable ArrayList<T> cur, T val)442     public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, T val) {
443         if (cur == null) {
444             cur = new ArrayList<>();
445         }
446         cur.add(val);
447         return cur;
448     }
449 
remove(@ullable ArrayList<T> cur, T val)450     public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) {
451         if (cur == null) {
452             return null;
453         }
454         cur.remove(val);
455         if (cur.isEmpty()) {
456             return null;
457         } else {
458             return cur;
459         }
460     }
461 
contains(@ullable Collection<T> cur, T val)462     public static <T> boolean contains(@Nullable Collection<T> cur, T val) {
463         return (cur != null) ? cur.contains(val) : false;
464     }
465 
trimToSize(@ullable T[] array, int size)466     public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
467         if (array == null || size == 0) {
468             return null;
469         } else if (array.length == size) {
470             return array;
471         } else {
472             return Arrays.copyOf(array, size);
473         }
474     }
475 
476     /**
477      * Returns true if the two ArrayLists are equal with respect to the objects they contain.
478      * The objects must be in the same order and be reference equal (== not .equals()).
479      */
referenceEquals(ArrayList<T> a, ArrayList<T> b)480     public static <T> boolean referenceEquals(ArrayList<T> a, ArrayList<T> b) {
481         if (a == b) {
482             return true;
483         }
484 
485         final int sizeA = a.size();
486         final int sizeB = b.size();
487         if (a == null || b == null || sizeA != sizeB) {
488             return false;
489         }
490 
491         boolean diff = false;
492         for (int i = 0; i < sizeA && !diff; i++) {
493             diff |= a.get(i) != b.get(i);
494         }
495         return !diff;
496     }
497 
498     /**
499      * Removes elements that match the predicate in an efficient way that alters the order of
500      * elements in the collection. This should only be used if order is not important.
501      * @param collection The ArrayList from which to remove elements.
502      * @param predicate The predicate that each element is tested against.
503      * @return the number of elements removed.
504      */
unstableRemoveIf(@ullable ArrayList<T> collection, @NonNull java.util.function.Predicate<T> predicate)505     public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection,
506                                            @NonNull java.util.function.Predicate<T> predicate) {
507         if (collection == null) {
508             return 0;
509         }
510 
511         final int size = collection.size();
512         int leftIdx = 0;
513         int rightIdx = size - 1;
514         while (leftIdx <= rightIdx) {
515             // Find the next element to remove moving left to right.
516             while (leftIdx < size && !predicate.test(collection.get(leftIdx))) {
517                 leftIdx++;
518             }
519 
520             // Find the next element to keep moving right to left.
521             while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) {
522                 rightIdx--;
523             }
524 
525             if (leftIdx >= rightIdx) {
526                 // Done.
527                 break;
528             }
529 
530             Collections.swap(collection, leftIdx, rightIdx);
531             leftIdx++;
532             rightIdx--;
533         }
534 
535         // leftIdx is now at the end.
536         for (int i = size - 1; i >= leftIdx; i--) {
537             collection.remove(i);
538         }
539         return size - leftIdx;
540     }
541 }
542