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