mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 11:09:54 +00:00
Enum members whose value duplicates an earlier member now reference it (Item2B = Item2A), [Flags] members combine earlier single-bit members (All = Item1 | Item2 | Item3) or their complement (NotItem1 = ~Item1) instead of showing a bare number. Several guardrails keep the output faithful to how such enums are written by hand: only previously declared members are referenced (field row order); a multi-bit value lying entirely within a larger, earlier member is a field encoding inside that mask, not a flag union, and stays numeric, as do zero-valued members of [Flags] enums, which routinely have several unrelated zero members. The ~X form is suppressed in byte/ushort enum declarations, where the initializer constant folds in int and would not compile. Enums with unusual underlying types (bool, native int) keep the plain constant conversion. With these rules, decompiling System.Private.CoreLib reproduces the hand-written declarations of TypeAttributes, MethodAttributes, AttributeTargets and FileAttributes almost verbatim. Assisted-by: Claude:claude-fable-5:Claude Code
463 lines
13 KiB
C#
463 lines
13 KiB
C#
// Copyright (c) 2014 Daniel Grunwald
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
// software and associated documentation files (the "Software"), to deal in the Software
|
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all copies or
|
|
// substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
#nullable enable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
|
|
namespace ICSharpCode.Decompiler.Util
|
|
{
|
|
static class CollectionExtensions
|
|
{
|
|
public static void Deconstruct<K, V>(this KeyValuePair<K, V> pair, out K key, out V value)
|
|
{
|
|
key = pair.Key;
|
|
value = pair.Value;
|
|
}
|
|
|
|
#if !NET8_0_OR_GREATER
|
|
public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
|
|
{
|
|
return input1.Zip(input2, (a, b) => (a, b));
|
|
}
|
|
#endif
|
|
|
|
public static IEnumerable<(int, A, B)> ZipWithIndex<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
|
|
{
|
|
int index = 0;
|
|
return input1.Zip(input2, (a, b) => (index++, a, b));
|
|
}
|
|
|
|
public static IEnumerable<(A?, B?)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
|
|
{
|
|
using (var it1 = input1.GetEnumerator())
|
|
{
|
|
using (var it2 = input2.GetEnumerator())
|
|
{
|
|
bool hasElements1 = true;
|
|
bool hasElements2 = true;
|
|
while (true)
|
|
{
|
|
if (hasElements1)
|
|
hasElements1 = it1.MoveNext();
|
|
if (hasElements2)
|
|
hasElements2 = it2.MoveNext();
|
|
if (!(hasElements1 || hasElements2))
|
|
break;
|
|
yield return ((hasElements1 ? it1.Current : default), (hasElements2 ? it2.Current : default));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<T> Slice<T>(this IReadOnlyList<T> input, int offset, int length)
|
|
{
|
|
for (int i = offset; i < offset + length; i++)
|
|
{
|
|
yield return input[i];
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<T> Slice<T>(this IReadOnlyList<T> input, int offset)
|
|
{
|
|
int length = input.Count;
|
|
for (int i = offset; i < length; i++)
|
|
{
|
|
yield return input[i];
|
|
}
|
|
}
|
|
|
|
#if !NET8_0_OR_GREATER
|
|
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input)
|
|
{
|
|
return new HashSet<T>(input);
|
|
}
|
|
#endif
|
|
|
|
public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count)
|
|
{
|
|
return input.Take(input.Count - count);
|
|
}
|
|
|
|
public static IEnumerable<T> TakeLast<T>(this IReadOnlyCollection<T> input, int count)
|
|
{
|
|
return input.Skip(input.Count - count);
|
|
}
|
|
|
|
public static T? PopOrDefault<T>(this Stack<T> stack)
|
|
{
|
|
if (stack.Count == 0)
|
|
return default(T);
|
|
return stack.Pop();
|
|
}
|
|
|
|
public static T? PeekOrDefault<T>(this Stack<T> stack)
|
|
{
|
|
if (stack.Count == 0)
|
|
return default(T);
|
|
return stack.Peek();
|
|
}
|
|
|
|
public static int MaxOrDefault<T>(this IEnumerable<T> input, Func<T, int> selector, int defaultValue = 0)
|
|
{
|
|
int max = defaultValue;
|
|
foreach (var element in input)
|
|
{
|
|
int value = selector(element);
|
|
if (value > max)
|
|
max = value;
|
|
}
|
|
return max;
|
|
}
|
|
|
|
public static int IndexOf<T>(this IReadOnlyList<T> collection, T value)
|
|
{
|
|
var comparer = EqualityComparer<T>.Default;
|
|
int index = 0;
|
|
foreach (T item in collection)
|
|
{
|
|
if (comparer.Equals(item, value))
|
|
{
|
|
return index;
|
|
}
|
|
index++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> input)
|
|
{
|
|
foreach (T item in input)
|
|
collection.Add(item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
|
|
/// use of the input collection's known size.
|
|
/// </summary>
|
|
public static U[] SelectArray<T, U>(this ICollection<T> collection, Func<T, U> func)
|
|
{
|
|
U[] result = new U[collection.Count];
|
|
int index = 0;
|
|
foreach (var element in collection)
|
|
{
|
|
result[index++] = func(element);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equivalent to <code>collection.Select(func).ToImmutableArray()</code>, but more efficient as it makes
|
|
/// use of the input collection's known size.
|
|
/// </summary>
|
|
public static ImmutableArray<U> SelectImmutableArray<T, U>(this IReadOnlyCollection<T> collection, Func<T, U> func)
|
|
{
|
|
var builder = ImmutableArray.CreateBuilder<U>(collection.Count);
|
|
foreach (var element in collection)
|
|
{
|
|
builder.Add(func(element));
|
|
}
|
|
return builder.MoveToImmutable();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
|
|
/// use of the input collection's known size.
|
|
/// </summary>
|
|
public static U[] SelectReadOnlyArray<T, U>(this IReadOnlyCollection<T> collection, Func<T, U> func)
|
|
{
|
|
U[] result = new U[collection.Count];
|
|
int index = 0;
|
|
foreach (var element in collection)
|
|
{
|
|
result[index++] = func(element);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
|
|
/// use of the input collection's known size.
|
|
/// </summary>
|
|
public static U[] SelectArray<T, U>(this List<T> collection, Func<T, U> func)
|
|
{
|
|
U[] result = new U[collection.Count];
|
|
int index = 0;
|
|
foreach (var element in collection)
|
|
{
|
|
result[index++] = func(element);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
|
|
/// use of the input collection's known size.
|
|
/// </summary>
|
|
public static U[] SelectArray<T, U>(this T[] collection, Func<T, U> func)
|
|
{
|
|
U[] result = new U[collection.Length];
|
|
int index = 0;
|
|
foreach (var element in collection)
|
|
{
|
|
result[index++] = func(element);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equivalent to <code>collection.Select(func).ToList()</code>, but more efficient as it makes
|
|
/// use of the input collection's known size.
|
|
/// </summary>
|
|
public static List<U> SelectList<T, U>(this ICollection<T> collection, Func<T, U> func)
|
|
{
|
|
List<U> result = new List<U>(collection.Count);
|
|
foreach (var element in collection)
|
|
{
|
|
result.Add(func(element));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static IEnumerable<U> SelectWithIndex<T, U>(this IEnumerable<T> source, Func<int, T, U> func)
|
|
{
|
|
int index = 0;
|
|
foreach (var element in source)
|
|
yield return func(index++, element);
|
|
}
|
|
|
|
public static IEnumerable<(int, T)> WithIndex<T>(this IEnumerable<T> source)
|
|
{
|
|
int index = 0;
|
|
foreach (var item in source)
|
|
{
|
|
yield return (index, item);
|
|
index++;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters out null elements, narrowing the element type to non-nullable.
|
|
/// </summary>
|
|
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class
|
|
{
|
|
foreach (var item in source)
|
|
{
|
|
if (item != null)
|
|
yield return item;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters out null elements and unwraps the remaining values.
|
|
/// </summary>
|
|
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : struct
|
|
{
|
|
foreach (var item in source)
|
|
{
|
|
if (item.HasValue)
|
|
yield return item.GetValueOrDefault();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The merge step of merge sort.
|
|
/// </summary>
|
|
public static IEnumerable<T> Merge<T>(this IEnumerable<T> input1, IEnumerable<T> input2, Comparison<T> comparison)
|
|
{
|
|
using (var enumA = input1.GetEnumerator())
|
|
using (var enumB = input2.GetEnumerator())
|
|
{
|
|
bool moreA = enumA.MoveNext();
|
|
bool moreB = enumB.MoveNext();
|
|
while (moreA && moreB)
|
|
{
|
|
if (comparison(enumA.Current, enumB.Current) <= 0)
|
|
{
|
|
yield return enumA.Current;
|
|
moreA = enumA.MoveNext();
|
|
}
|
|
else
|
|
{
|
|
yield return enumB.Current;
|
|
moreB = enumB.MoveNext();
|
|
}
|
|
}
|
|
while (moreA)
|
|
{
|
|
yield return enumA.Current;
|
|
moreA = enumA.MoveNext();
|
|
}
|
|
while (moreB)
|
|
{
|
|
yield return enumB.Current;
|
|
moreB = enumB.MoveNext();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the minimum element.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">The input sequence is empty</exception>
|
|
public static T MinBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector) where K : IComparable<K>
|
|
{
|
|
return source.MinBy(keySelector, Comparer<K>.Default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the minimum element.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">The input sequence is empty</exception>
|
|
public static T MinBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector, IComparer<K>? keyComparer)
|
|
{
|
|
if (source == null)
|
|
throw new ArgumentNullException(nameof(source));
|
|
if (keySelector == null)
|
|
throw new ArgumentNullException(nameof(keySelector));
|
|
if (keyComparer == null)
|
|
keyComparer = Comparer<K>.Default;
|
|
using (var enumerator = source.GetEnumerator())
|
|
{
|
|
if (!enumerator.MoveNext())
|
|
throw new InvalidOperationException("Sequence contains no elements");
|
|
T minElement = enumerator.Current;
|
|
K minKey = keySelector(minElement);
|
|
while (enumerator.MoveNext())
|
|
{
|
|
T element = enumerator.Current;
|
|
K key = keySelector(element);
|
|
if (keyComparer.Compare(key, minKey) < 0)
|
|
{
|
|
minElement = element;
|
|
minKey = key;
|
|
}
|
|
}
|
|
return minElement;
|
|
}
|
|
}
|
|
|
|
#if !NET8_0_OR_GREATER
|
|
/// <summary>
|
|
/// Returns the maximum element.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">The input sequence is empty</exception>
|
|
public static T MaxBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector) where K : IComparable<K>
|
|
{
|
|
return source.MaxBy(keySelector, Comparer<K>.Default);
|
|
}
|
|
#endif
|
|
/// <summary>
|
|
/// Returns the maximum element.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">The input sequence is empty</exception>
|
|
public static T MaxBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector, IComparer<K>? keyComparer)
|
|
{
|
|
if (source == null)
|
|
throw new ArgumentNullException(nameof(source));
|
|
if (keySelector == null)
|
|
throw new ArgumentNullException(nameof(keySelector));
|
|
if (keyComparer == null)
|
|
keyComparer = Comparer<K>.Default;
|
|
using (var enumerator = source.GetEnumerator())
|
|
{
|
|
if (!enumerator.MoveNext())
|
|
throw new InvalidOperationException("Sequence contains no elements");
|
|
T maxElement = enumerator.Current;
|
|
K maxKey = keySelector(maxElement);
|
|
while (enumerator.MoveNext())
|
|
{
|
|
T element = enumerator.Current;
|
|
K key = keySelector(element);
|
|
if (keyComparer.Compare(key, maxKey) > 0)
|
|
{
|
|
maxElement = element;
|
|
maxKey = key;
|
|
}
|
|
}
|
|
return maxElement;
|
|
}
|
|
}
|
|
|
|
public static void RemoveLast<T>(this IList<T> list)
|
|
{
|
|
if (list == null)
|
|
throw new ArgumentNullException(nameof(list));
|
|
list.RemoveAt(list.Count - 1);
|
|
}
|
|
|
|
public static T? OnlyOrDefault<T>(this IEnumerable<T> source, Func<T, bool> predicate) => OnlyOrDefault(source.Where(predicate));
|
|
|
|
public static T? OnlyOrDefault<T>(this IEnumerable<T> source)
|
|
{
|
|
bool any = false;
|
|
T? first = default;
|
|
foreach (var t in source)
|
|
{
|
|
if (any)
|
|
return default(T);
|
|
first = t;
|
|
any = true;
|
|
}
|
|
|
|
return first;
|
|
}
|
|
|
|
#if !NET8_0_OR_GREATER
|
|
public static int EnsureCapacity<T>(this List<T> list, int capacity)
|
|
{
|
|
if (capacity < 0)
|
|
throw new ArgumentOutOfRangeException(nameof(capacity));
|
|
if (list.Capacity < capacity)
|
|
{
|
|
const int DefaultCapacity = 4;
|
|
const int MaxLength = 0X7FFFFFC7;
|
|
|
|
int newcapacity = list.Capacity == 0 ? DefaultCapacity : 2 * list.Capacity;
|
|
|
|
if ((uint)newcapacity > MaxLength)
|
|
newcapacity = MaxLength;
|
|
|
|
if (newcapacity < capacity)
|
|
newcapacity = capacity;
|
|
|
|
list.Capacity = newcapacity;
|
|
}
|
|
|
|
return list.Capacity;
|
|
}
|
|
#endif
|
|
|
|
#region Aliases/shortcuts for Enumerable extension methods
|
|
public static bool Any<T>(this ICollection<T> list) => list.Count > 0;
|
|
public static bool Any<T>(this T[] array, Predicate<T> match) => Array.Exists(array, match);
|
|
public static bool Any<T>(this List<T> list, Predicate<T> match) => list.Exists(match);
|
|
|
|
public static bool All<T>(this T[] array, Predicate<T> match) => Array.TrueForAll(array, match);
|
|
public static bool All<T>(this List<T> list, Predicate<T> match) => list.TrueForAll(match);
|
|
|
|
public static T? FirstOrDefault<T>(this T[] array, Predicate<T> predicate) => Array.Find(array, predicate);
|
|
public static T? FirstOrDefault<T>(this List<T> list, Predicate<T> predicate) => list.Find(predicate);
|
|
|
|
public static T Last<T>(this IList<T> list) => list[list.Count - 1];
|
|
#endregion
|
|
}
|
|
}
|