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