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