using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.InteropServices; namespace Robust.Shared.Utility { public static class Extensions { /// /// Ensures that the specified array has the specified length. /// public static void EnsureLength(ref T[] array, int length) { if (array.Length >= length) return; Array.Resize(ref array, length); } public static IList Clone(this IList listToClone) where T : ICloneable { var clone = new List(listToClone.Count); foreach (var value in listToClone) { clone.Add((T) value.Clone()); } return clone; } /// /// Creates a shallow clone of a list. /// Basically a new list with all the same elements. /// /// The list to shallow clone. /// The type of the list's elements. /// A new list with the same elements as . public static List ShallowClone(this List self) { return new List(self); } public static Dictionary ShallowClone(this Dictionary self) where TKey : notnull { return new Dictionary(self, self.Comparer); } /// /// Compares the entries inside of 2 dictionaries to check equality. /// /// /// The base Equals implementation checks references hence this. /// /// /// /// /// /// public static bool DictionaryEquals( this IReadOnlyDictionary self, IReadOnlyDictionary other) where TKey : notnull { if (ReferenceEquals(self, other)) return true; if (self.Count != other.Count) return false; var valueComparer = EqualityComparer.Default; foreach (var (key, value) in self) { if (!other.TryGetValue(key, out var otherValue) || !valueComparer.Equals(value, otherValue)) return false; } return true; } public static bool TryGetValue(this IList list, int index, out T value) { if (index >= 0 && list.Count > index) { value = list[index]; return true; } value = default!; return false; } /// /// Remove an item from the list, replacing it with the one at the very end of the list. /// This means that the order will not be preserved, but it should be an O(1) operation. /// /// The index to remove /// The removed element public static T RemoveSwap(this IList list, int index) { // This method has no implementation details, // and changing the result of an operation is a breaking change. var old = list[index]; var replacement = list[list.Count - 1]; list[index] = replacement; // TODO: Any more efficient way to pop the last element off? list.RemoveAt(list.Count - 1); return old; } /// /// Pop an element from the end of a list, removing it from the list and returning it. /// /// The list to pop from. /// The type of the elements of the list. /// The popped off element. /// /// Thrown if the list is empty. /// public static T Pop(this IList list) { if (list.Count == 0) { throw new InvalidOperationException(); } var t = list[list.Count - 1]; list.RemoveAt(list.Count-1); return t; } /// /// Just like but returns null for value types as well. /// /// An to return an element from. /// A function to test each element for a condition. /// The type of the elements of . /// null if is empty or if no element passes the test specified by ; otherwise, the first element in that passes the test specified by . /// /// or is . public static TSource? FirstOrNull( this IEnumerable source, Func predicate) where TSource: struct { if (source == null) throw new ArgumentNullException(nameof (source)); if (predicate == null) throw new ArgumentNullException(nameof (predicate)); foreach (TSource source1 in source) { if (predicate(source1)) return source1; } return null; } /// /// Just like but returns null for value types as well. /// /// /// An to return the first element from. /// /// The type of the elements of . /// /// if is empty, otherwise, /// the first element in . /// /// /// is . public static TSource? FirstOrNull(this IEnumerable source) where TSource : struct { if (source == null) throw new ArgumentNullException(nameof (source)); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) { return null; } return enumerator.Current; } /// /// Just like but returns null for value types as well. /// /// An to return an element from. /// A function to test each element for a condition. /// The type of the elements of . /// True if an element has been found. /// /// or is . public static bool TryFirstOrNull(this IEnumerable source, Func predicate, [NotNullWhen(true)] out TSource? element) where TSource : struct { element = source.FirstOrNull(predicate); return element != null; } /// /// Just like but returns null for value types as well. /// /// An to return an element from. /// The type of the elements of . /// True if an element has been found. /// /// is . public static bool TryFirstOrNull(this IEnumerable source, [NotNullWhen(true)] out TSource? element) where TSource : struct { element = source.FirstOrNull(); return element != null; } /// /// Wraps Linq's FirstOrDefault. /// /// An to return an element from. /// A function to test each element for a condition. /// The type of the elements of . /// True if an element has been found. /// /// or is . public static bool TryFirstOrDefault(this IEnumerable source, Func predicate, [NotNullWhen(true)] out TSource? element) where TSource : class { element = source.FirstOrDefault(predicate); return element != null; } /// /// Wraps Linq's FirstOrDefault. /// /// An to return an element from. /// The type of the elements of . /// True if an element has been found. /// /// is . public static bool TryFirstOrDefault(this IEnumerable source, [NotNullWhen(true)] out TSource? element) where TSource : class { element = source.FirstOrDefault(); return element != null; } public static TValue GetOrNew(this IDictionary dict, TKey key) where TValue : new() where TKey : notnull { if (!dict.TryGetValue(key, out var value)) { value = new TValue(); dict.Add(key, value); } return value; } public static TValue GetOrNew(this Dictionary dict, TKey key) where TValue : new() where TKey : notnull { ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out var exists); if (!exists) entry = new TValue(); return entry!; } public static TValue GetOrNew(this Dictionary dict, TKey key, out bool exists) where TValue : new() where TKey : notnull { ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out exists); if (!exists) entry = new TValue(); return entry!; } [Pure] public static KeyValuePair[] ToArray(this Dictionary dict) where TKey : notnull { // Faster than the old custom implementation // 0 count faster, 1 count slower, any higher faster (on my 7800X3D). return Enumerable.ToArray(dict); } /// /// Tries to get a value from a dictionary and checks if that value is of type T /// /// The type that sould be casted to /// Whether the value was present in the dictionary and of the required type public static bool TryCastValue(this Dictionary dict, TKey key, [NotNullWhen(true)] out T? value) where TKey : notnull { if (dict.TryGetValue(key, out var untypedValue) && untypedValue is T typedValue) { value = typedValue; return true; } value = default; return false; } } }