A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using Unity.Collections; 5using Unity.Collections.LowLevel.Unsafe; 6 7namespace UnityEngine.InputSystem.Utilities 8{ 9 /// <summary> 10 /// A collection of utility functions for working with arrays. 11 /// </summary> 12 /// <remarks> 13 /// The goal of this collection is to make it easy to use arrays directly rather than resorting to 14 /// <see cref="List{T}"/>. 15 /// </remarks> 16 internal static class ArrayHelpers 17 { 18 public static int LengthSafe<TValue>(this TValue[] array) 19 { 20 if (array == null) 21 return 0; 22 return array.Length; 23 } 24 25 public static void Clear<TValue>(this TValue[] array) 26 { 27 if (array == null) 28 return; 29 30 Array.Clear(array, 0, array.Length); 31 } 32 33 public static void Clear<TValue>(this TValue[] array, int count) 34 { 35 if (array == null) 36 return; 37 Array.Clear(array, 0, count); 38 } 39 40 public static void Clear<TValue>(this TValue[] array, ref int count) 41 { 42 if (array == null) 43 return; 44 45 Array.Clear(array, 0, count); 46 count = 0; 47 } 48 49 public static void EnsureCapacity<TValue>(ref TValue[] array, int count, int capacity, int capacityIncrement = 10) 50 { 51 if (capacity == 0) 52 return; 53 54 if (array == null) 55 { 56 array = new TValue[Math.Max(capacity, capacityIncrement)]; 57 return; 58 } 59 60 var currentCapacity = array.Length - count; 61 if (currentCapacity >= capacity) 62 return; 63 64 DuplicateWithCapacity(ref array, count, capacity, capacityIncrement); 65 } 66 67 public static void DuplicateWithCapacity<TValue>(ref TValue[] array, int count, int capacity, int capacityIncrement = 10) 68 { 69 if (array == null) 70 { 71 array = new TValue[Math.Max(capacity, capacityIncrement)]; 72 return; 73 } 74 75 var newSize = count + Math.Max(capacity, capacityIncrement); 76 var newArray = new TValue[newSize]; 77 Array.Copy(array, newArray, count); 78 array = newArray; 79 } 80 81 public static bool Contains<TValue>(TValue[] array, TValue value) 82 { 83 if (array == null) 84 return false; 85 86 var comparer = EqualityComparer<TValue>.Default; 87 for (var i = 0; i < array.Length; ++i) 88 if (comparer.Equals(array[i], value)) 89 return true; 90 91 return false; 92 } 93 94 public static bool ContainsReference<TValue>(this TValue[] array, TValue value) 95 where TValue : class 96 { 97 if (array == null) 98 return false; 99 100 return ContainsReference(array, array.Length, value); 101 } 102 103 public static bool ContainsReference<TFirst, TSecond>(this TFirst[] array, int count, TSecond value) 104 where TSecond : class 105 where TFirst : TSecond 106 { 107 return IndexOfReference(array, value, count) != -1; 108 } 109 110 public static bool ContainsReference<TFirst, TSecond>(this TFirst[] array, int startIndex, int count, TSecond value) 111 where TSecond : class 112 where TFirst : TSecond 113 { 114 return IndexOfReference(array, value, startIndex, count) != -1; 115 } 116 117 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Keep this for future implementation")] 118 public static bool HaveDuplicateReferences<TFirst>(this TFirst[] first, int index, int count) 119 { 120 for (var i = 0; i < count; ++i) 121 { 122 var element = first[i]; 123 for (var n = i + 1; n < count - i; ++n) 124 { 125 if (ReferenceEquals(element, first[n])) 126 return true; 127 } 128 } 129 return false; 130 } 131 132 public static bool HaveEqualElements<TValue>(TValue[] first, TValue[] second, int count = int.MaxValue) 133 { 134 if (first == null || second == null) 135 return second == first; 136 137 var lengthFirst = Math.Min(count, first.Length); 138 var lengthSecond = Math.Min(count, second.Length); 139 140 if (lengthFirst != lengthSecond) 141 return false; 142 143 var comparer = EqualityComparer<TValue>.Default; 144 for (var i = 0; i < lengthFirst; ++i) 145 if (!comparer.Equals(first[i], second[i])) 146 return false; 147 148 return true; 149 } 150 151 ////REVIEW: remove this to get rid of default equality comparer? 152 public static int IndexOf<TValue>(TValue[] array, TValue value, int startIndex = 0, int count = -1) 153 { 154 if (array == null) 155 return -1; 156 157 if (count < 0) 158 count = array.Length - startIndex; 159 var comparer = EqualityComparer<TValue>.Default; 160 for (var i = startIndex; i < startIndex + count; ++i) 161 if (comparer.Equals(array[i], value)) 162 return i; 163 164 return -1; 165 } 166 167 public static int IndexOf<TValue>(this TValue[] array, Predicate<TValue> predicate) 168 { 169 if (array == null) 170 return -1; 171 172 var length = array.Length; 173 for (var i = 0; i < length; ++i) 174 if (predicate(array[i])) 175 return i; 176 177 return -1; 178 } 179 180 public static int IndexOf<TValue>(this TValue[] array, Predicate<TValue> predicate, int startIndex = 0, int count = -1) 181 { 182 if (array == null) 183 return -1; 184 185 var end = startIndex + (count < 0 ? array.Length - startIndex : count); 186 for (var i = startIndex; i < end; ++i) 187 { 188 if (predicate(array[i])) 189 return i; 190 } 191 192 return -1; 193 } 194 195 public static int IndexOfReference<TFirst, TSecond>(this TFirst[] array, TSecond value, int count = -1) 196 where TSecond : class 197 where TFirst : TSecond 198 { 199 return IndexOfReference(array, value, 0, count); 200 } 201 202 public static int IndexOfReference<TFirst, TSecond>(this TFirst[] array, TSecond value, int startIndex, int count) 203 where TSecond : class 204 where TFirst : TSecond 205 { 206 if (array == null) 207 return -1; 208 209 if (count < 0) 210 count = array.Length - startIndex; 211 for (var i = startIndex; i < startIndex + count; ++i) 212 if (ReferenceEquals(array[i], value)) 213 return i; 214 215 return -1; 216 } 217 218 public static int IndexOfValue<TValue>(this TValue[] array, TValue value, int startIndex = 0, int count = -1) 219 where TValue : struct, IEquatable<TValue> 220 { 221 if (array == null) 222 return -1; 223 224 if (count < 0) 225 count = array.Length - startIndex; 226 for (var i = startIndex; i < startIndex + count; ++i) 227 if (value.Equals(array[i])) 228 return i; 229 230 return -1; 231 } 232 233 public static unsafe void Resize<TValue>(ref NativeArray<TValue> array, int newSize, Allocator allocator) 234 where TValue : struct 235 { 236 var oldSize = array.Length; 237 if (oldSize == newSize) 238 return; 239 240 if (newSize == 0) 241 { 242 if (array.IsCreated) 243 array.Dispose(); 244 array = new NativeArray<TValue>(); 245 return; 246 } 247 248 var newArray = new NativeArray<TValue>(newSize, allocator); 249 if (oldSize != 0) 250 { 251 // Copy contents from old array. 252 UnsafeUtility.MemCpy(newArray.GetUnsafePtr(), array.GetUnsafeReadOnlyPtr(), 253 UnsafeUtility.SizeOf<TValue>() * (newSize < oldSize ? newSize : oldSize)); 254 array.Dispose(); 255 } 256 array = newArray; 257 } 258 259 public static int Append<TValue>(ref TValue[] array, TValue value) 260 { 261 if (array == null) 262 { 263 array = new TValue[1]; 264 array[0] = value; 265 return 0; 266 } 267 268 var length = array.Length; 269 Array.Resize(ref array, length + 1); 270 array[length] = value; 271 return length; 272 } 273 274 public static int Append<TValue>(ref TValue[] array, IEnumerable<TValue> values) 275 { 276 if (array == null) 277 { 278 array = values.ToArray(); 279 return 0; 280 } 281 282 var oldLength = array.Length; 283 var valueCount = values.Count(); 284 285 Array.Resize(ref array, oldLength + valueCount); 286 287 var index = oldLength; 288 foreach (var value in values) 289 array[index++] = value; 290 291 return oldLength; 292 } 293 294 // Append to an array that is considered immutable. This allows using 'values' as is 295 // if 'array' is null. 296 // Returns the index of the first newly added element in the resulting array. 297 public static int AppendToImmutable<TValue>(ref TValue[] array, TValue[] values) 298 { 299 if (array == null) 300 { 301 array = values; 302 return 0; 303 } 304 305 if (values != null && values.Length > 0) 306 { 307 var oldCount = array.Length; 308 var valueCount = values.Length; 309 Array.Resize(ref array, oldCount + valueCount); 310 Array.Copy(values, 0, array, oldCount, valueCount); 311 return oldCount; 312 } 313 314 return array.Length; 315 } 316 317 public static int AppendWithCapacity<TValue>(ref TValue[] array, ref int count, TValue value, int capacityIncrement = 10) 318 { 319 if (array == null) 320 { 321 array = new TValue[capacityIncrement]; 322 array[0] = value; 323 ++count; 324 return 0; 325 } 326 327 var capacity = array.Length; 328 if (capacity == count) 329 { 330 capacity += capacityIncrement; 331 Array.Resize(ref array, capacity); 332 } 333 334 var index = count; 335 array[index] = value; 336 ++count; 337 338 return index; 339 } 340 341 public static int AppendListWithCapacity<TValue, TValues>(ref TValue[] array, ref int length, TValues values, int capacityIncrement = 10) 342 where TValues : IReadOnlyList<TValue> 343 { 344 var numToAdd = values.Count; 345 if (array == null) 346 { 347 var size = Math.Max(numToAdd, capacityIncrement); 348 array = new TValue[size]; 349 for (var i = 0; i < numToAdd; ++i) 350 array[i] = values[i]; 351 length += numToAdd; 352 return 0; 353 } 354 355 var capacity = array.Length; 356 if (capacity < length + numToAdd) 357 { 358 capacity += Math.Max(length + numToAdd, capacityIncrement); 359 Array.Resize(ref array, capacity); 360 } 361 362 var index = length; 363 for (var i = 0; i < numToAdd; ++i) 364 array[index + i] = values[i]; 365 length += numToAdd; 366 367 return index; 368 } 369 370 public static int AppendWithCapacity<TValue>(ref NativeArray<TValue> array, ref int count, TValue value, 371 int capacityIncrement = 10, Allocator allocator = Allocator.Persistent) 372 where TValue : struct 373 { 374 var capacity = array.Length; 375 if (capacity == count) 376 GrowBy(ref array, capacityIncrement > 1 ? capacityIncrement : 1, allocator); 377 378 var index = count; 379 array[index] = value; 380 ++count; 381 382 return index; 383 } 384 385 public static void InsertAt<TValue>(ref TValue[] array, int index, TValue value) 386 { 387 if (array == null) 388 { 389 ////REVIEW: allow growing array to specific size by inserting at arbitrary index? 390 if (index != 0) 391 throw new ArgumentOutOfRangeException(nameof(index)); 392 393 array = new TValue[1]; 394 array[0] = value; 395 return; 396 } 397 398 // Reallocate. 399 var oldLength = array.Length; 400 Array.Resize(ref array, oldLength + 1); 401 402 // Make room for element. 403 if (index != oldLength) 404 Array.Copy(array, index, array, index + 1, oldLength - index); 405 406 array[index] = value; 407 } 408 409 public static void InsertAtWithCapacity<TValue>(ref TValue[] array, ref int count, int index, TValue value, int capacityIncrement = 10) 410 { 411 EnsureCapacity(ref array, count, count + 1, capacityIncrement); 412 413 if (index != count) 414 Array.Copy(array, index, array, index + 1, count - index); 415 416 array[index] = value; 417 ++count; 418 } 419 420 public static void PutAtIfNotSet<TValue>(ref TValue[] array, int index, Func<TValue> valueFn) 421 { 422 if (array.LengthSafe() < index + 1) 423 Array.Resize(ref array, index + 1); 424 425 if (EqualityComparer<TValue>.Default.Equals(array[index], default(TValue))) 426 array[index] = valueFn(); 427 } 428 429 // Adds 'count' entries to the array. Returns first index of newly added entries. 430 public static int GrowBy<TValue>(ref TValue[] array, int count) 431 { 432 if (array == null) 433 { 434 array = new TValue[count]; 435 return 0; 436 } 437 438 var oldLength = array.Length; 439 Array.Resize(ref array, oldLength + count); 440 return oldLength; 441 } 442 443 public static unsafe int GrowBy<TValue>(ref NativeArray<TValue> array, int count, Allocator allocator = Allocator.Persistent) 444 where TValue : struct 445 { 446 var length = array.Length; 447 if (length == 0) 448 { 449 array = new NativeArray<TValue>(count, allocator); 450 return 0; 451 } 452 453 var newArray = new NativeArray<TValue>(length + count, allocator); 454 // CopyFrom() expects length to match. Copy manually. 455 UnsafeUtility.MemCpy(newArray.GetUnsafePtr(), array.GetUnsafeReadOnlyPtr(), (long)length * UnsafeUtility.SizeOf<TValue>()); 456 array.Dispose(); 457 array = newArray; 458 459 return length; 460 } 461 462 public static int GrowWithCapacity<TValue>(ref TValue[] array, ref int count, int growBy, int capacityIncrement = 10) 463 { 464 var length = array != null ? array.Length : 0; 465 if (length < count + growBy) 466 { 467 if (capacityIncrement < growBy) 468 capacityIncrement = growBy; 469 GrowBy(ref array, capacityIncrement); 470 } 471 472 var offset = count; 473 count += growBy; 474 return offset; 475 } 476 477 public static int GrowWithCapacity<TValue>(ref NativeArray<TValue> array, ref int count, int growBy, 478 int capacityIncrement = 10, Allocator allocator = Allocator.Persistent) 479 where TValue : struct 480 { 481 var length = array.Length; 482 if (length < count + growBy) 483 { 484 if (capacityIncrement < growBy) 485 capacityIncrement = growBy; 486 GrowBy(ref array, capacityIncrement, allocator); 487 } 488 489 var offset = count; 490 count += growBy; 491 return offset; 492 } 493 494 public static TValue[] Join<TValue>(TValue value, params TValue[] values) 495 { 496 // Determine length. 497 var length = 0; 498 if (value != null) 499 ++length; 500 if (values != null) 501 length += values.Length; 502 503 if (length == 0) 504 return null; 505 506 var array = new TValue[length]; 507 508 // Populate. 509 var index = 0; 510 if (value != null) 511 array[index++] = value; 512 513 if (values != null) 514 Array.Copy(values, 0, array, index, values.Length); 515 516 return array; 517 } 518 519 public static TValue[] Merge<TValue>(TValue[] first, TValue[] second) 520 where TValue : IEquatable<TValue> 521 { 522 if (first == null) 523 return second; 524 if (second == null) 525 return first; 526 527 var merged = new List<TValue>(); 528 merged.AddRange(first); 529 530 for (var i = 0; i < second.Length; ++i) 531 { 532 var secondValue = second[i]; 533 if (!merged.Exists(x => x.Equals(secondValue))) 534 { 535 merged.Add(secondValue); 536 } 537 } 538 539 return merged.ToArray(); 540 } 541 542 public static TValue[] Merge<TValue>(TValue[] first, TValue[] second, IEqualityComparer<TValue> comparer) 543 { 544 if (first == null) 545 return second; 546 if (second == null) 547 return null; 548 549 var merged = new List<TValue>(); 550 merged.AddRange(first); 551 552 for (var i = 0; i < second.Length; ++i) 553 { 554 var secondValue = second[i]; 555 if (!merged.Exists(x => comparer.Equals(secondValue))) 556 { 557 merged.Add(secondValue); 558 } 559 } 560 561 return merged.ToArray(); 562 } 563 564 public static void EraseAt<TValue>(ref TValue[] array, int index) 565 { 566 Debug.Assert(array != null); 567 Debug.Assert(index >= 0 && index < array.Length); 568 569 var length = array.Length; 570 if (index == 0 && length == 1) 571 { 572 array = null; 573 return; 574 } 575 576 if (index < length - 1) 577 Array.Copy(array, index + 1, array, index, length - index - 1); 578 579 Array.Resize(ref array, length - 1); 580 } 581 582 public static void EraseAtWithCapacity<TValue>(this TValue[] array, ref int count, int index) 583 { 584 Debug.Assert(array != null); 585 Debug.Assert(count <= array.Length); 586 Debug.Assert(index >= 0 && index < count); 587 588 // If we're erasing from the beginning or somewhere in the middle, move 589 // the array contents down from after the index. 590 if (index < count - 1) 591 { 592 Array.Copy(array, index + 1, array, index, count - index - 1); 593 } 594 595 array[count - 1] = default; // Tail has been moved down by one. 596 --count; 597 } 598 599 public static unsafe void EraseAtWithCapacity<TValue>(NativeArray<TValue> array, ref int count, int index) 600 where TValue : struct 601 { 602 Debug.Assert(array.IsCreated); 603 Debug.Assert(count <= array.Length); 604 Debug.Assert(index >= 0 && index < count); 605 606 // If we're erasing from the beginning or somewhere in the middle, move 607 // the array contents down from after the index. 608 if (index < count - 1) 609 { 610 var elementSize = UnsafeUtility.SizeOf<TValue>(); 611 var arrayPtr = (byte*)array.GetUnsafePtr(); 612 613 UnsafeUtility.MemCpy(arrayPtr + elementSize * index, arrayPtr + elementSize * (index + 1), 614 (count - index - 1) * elementSize); 615 } 616 617 --count; 618 } 619 620 public static bool Erase<TValue>(ref TValue[] array, TValue value) 621 { 622 var index = IndexOf(array, value); 623 if (index != -1) 624 { 625 EraseAt(ref array, index); 626 return true; 627 } 628 return false; 629 } 630 631 /// <summary> 632 /// Erase an element from the array by moving the tail element into its place. 633 /// </summary> 634 /// <param name="array">Array to modify. May be not <c>null</c>.</param> 635 /// <param name="count">Current number of elements inside of array. May be less than <c>array.Length</c>.</param> 636 /// <param name="index">Index of element to remove. Tail element will get moved into its place.</param> 637 /// <typeparam name="TValue"></typeparam> 638 /// <remarks> 639 /// This method does not re-allocate the array. Instead <paramref name="count"/> is used 640 /// to keep track of how many elements there actually are in the array. 641 /// </remarks> 642 public static void EraseAtByMovingTail<TValue>(TValue[] array, ref int count, int index) 643 { 644 Debug.Assert(array != null); 645 Debug.Assert(index >= 0 && index < array.Length); 646 Debug.Assert(count >= 0 && count <= array.Length); 647 Debug.Assert(index < count); 648 649 // Move tail, if necessary. 650 if (index != count - 1) 651 array[index] = array[count - 1]; 652 653 // Destroy current tail. 654 if (count >= 1) 655 array[count - 1] = default; 656 --count; 657 } 658 659 public static TValue[] Copy<TValue>(TValue[] array) 660 { 661 if (array == null) 662 return null; 663 664 var length = array.Length; 665 var result = new TValue[length]; 666 Array.Copy(array, result, length); 667 return result; 668 } 669 670 public static TValue[] Clone<TValue>(TValue[] array) 671 where TValue : ICloneable 672 { 673 if (array == null) 674 return null; 675 676 var count = array.Length; 677 var result = new TValue[count]; 678 679 for (var i = 0; i < count; ++i) 680 result[i] = (TValue)array[i].Clone(); 681 682 return result; 683 } 684 685 public static TNew[] Select<TOld, TNew>(TOld[] array, Func<TOld, TNew> converter) 686 { 687 if (array == null) 688 return null; 689 690 var length = array.Length; 691 var result = new TNew[length]; 692 693 for (var i = 0; i < length; ++i) 694 result[i] = converter(array[i]); 695 696 return result; 697 } 698 699 private static void Swap<TValue>(ref TValue first, ref TValue second) 700 { 701 var temp = first; 702 first = second; 703 second = temp; 704 } 705 706 /// <summary> 707 /// Move a slice in the array to a different place without allocating a temporary array. 708 /// </summary> 709 /// <param name="array"></param> 710 /// <param name="sourceIndex"></param> 711 /// <param name="destinationIndex"></param> 712 /// <param name="count"></param> 713 /// <typeparam name="TValue"></typeparam> 714 /// <remarks> 715 /// The slice is moved by repeatedly swapping slices until all the slices are where they 716 /// are supposed to go. This is not super efficient but avoids having to allocate a temporary 717 /// array on the heap. 718 /// </remarks> 719 public static void MoveSlice<TValue>(TValue[] array, int sourceIndex, int destinationIndex, int count) 720 { 721 if (count <= 0 || sourceIndex == destinationIndex) 722 return; 723 724 // Determine the number of elements in the window. 725 int elementCount; 726 if (destinationIndex > sourceIndex) 727 elementCount = destinationIndex + count - sourceIndex; 728 else 729 elementCount = sourceIndex + count - destinationIndex; 730 731 // If the source and target slice are right next to each other, just go 732 // and swap out the elements in both slices. 733 if (elementCount == count * 2) 734 { 735 for (var i = 0; i < count; ++i) 736 Swap(ref array[sourceIndex + i], ref array[destinationIndex + i]); 737 } 738 else 739 { 740 // There's elements in-between the two slices. 741 // 742 // The easiest way to picture this operation is as a rotation of the elements within 743 // the window given by sourceIndex, destination, and count. Within that window, we are 744 // simply treating it as a wrap-around buffer and then sliding the elements clockwise 745 // or counter-clockwise (depending on whether we move up or down, respectively) through 746 // the window. 747 // 748 // Unfortunately, we can't just memcopy the slices within that window as we have to 749 // have a temporary copy in place in order to preserve element values. So instead, we 750 // go and swap elements one by one, something that doesn't require anything other than 751 // a single value temporary copy. 752 753 // Determine the number of swaps we need to achieve the desired order. Swaps 754 // operate in pairs so it's one less than the number of elements in the range. 755 var swapCount = elementCount - 1; 756 757 // We simply take sourceIndex as fixed and do all swaps from there until all 758 // the elements in the window are in the right order. Each swap will put one 759 // element in its final place. 760 var dst = destinationIndex; 761 for (var i = 0; i < swapCount; ++i) 762 { 763 // Swap source into its destination place. This puts the current sourceIndex 764 // element in its final place. 765 Swap(ref array[dst], ref array[sourceIndex]); 766 767 // Find out where the element that we now swapped into sourceIndex should 768 // actually go. 769 if (destinationIndex > sourceIndex) 770 { 771 // Rotating clockwise. 772 dst -= count; 773 if (dst < sourceIndex) 774 dst = destinationIndex + count - Math.Abs(sourceIndex - dst); // Wrap around. 775 } 776 else 777 { 778 // Rotating counter-clockwise. 779 dst += count; 780 if (dst >= sourceIndex + count) 781 dst = destinationIndex + (dst - (sourceIndex + count)); // Wrap around. 782 } 783 } 784 } 785 } 786 787 public static void EraseSliceWithCapacity<TValue>(ref TValue[] array, ref int length, int index, int count) 788 { 789 // Move elements down. 790 if (count < length) 791 Array.Copy(array, index + count, array, index, length - index - count); 792 793 // Erase now vacant slots. 794 for (var i = 0; i < count; ++i) 795 array[length - i - 1] = default; 796 797 length -= count; 798 } 799 800 public static void SwapElements<TValue>(this TValue[] array, int index1, int index2) 801 { 802 MemoryHelpers.Swap(ref array[index1], ref array[index2]); 803 } 804 805 public static void SwapElements<TValue>(this NativeArray<TValue> array, int index1, int index2) 806 where TValue : struct 807 { 808 var temp = array[index1]; 809 array[index1] = array[index2]; 810 array[index2] = temp; 811 } 812 } 813}