File: src\nuget-client\build\Shared\SharedExtensions.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.PackageManagement\NuGet.PackageManagement.csproj (NuGet.PackageManagement)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable enable

using System;
using System.Collections.Generic;
using System.Text;

namespace NuGet.Shared
{
    internal static class Extensions
    {
        /// <summary>
        /// Return the enumerable as a List of T, copying if required. Optimized for common case where it is an List of T.
        /// Avoid mutating the return value.
        /// </summary>
        /// <remarks>https://aspnetwebstack.codeplex.com/SourceControl/latest#src/Common/CollectionExtensions.cs</remarks>
        public static List<T> AsList<T>(this IEnumerable<T> enumerable)
        {
            var list = enumerable as List<T>;
            if (list != null)
            {
                return list;
            }

            return new List<T>(enumerable);
        }

        /// <summary>
        /// Return the ISet as a HashSet of T, copying if required. Optimized for common case where it is a HashSet of T.
        /// Avoid mutating the return value.
        /// </summary>
        public static HashSet<T>? AsHashSet<T>(this ISet<T> enumerable, IEqualityComparer<T>? comparer = null)
        {
            if (enumerable == null)
            {
                return null;
            }

            var set = enumerable as HashSet<T>;
            if (set != null)
            {
                return set;
            }
            else
            {
                if (comparer == null)
                {
                    comparer = EqualityComparer<T>.Default;
                }

                return new HashSet<T>(enumerable, comparer);
            }
        }

        public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
        {
            foreach (var item in enumeration)
            {
                action(item);
            }
        }


        /// <summary>
        /// Helper function to append an <see cref="int"/> to a <see cref="StringBuilder"/>. Calling
        /// <see cref="StringBuilder.Append(int)"/> directly causes an allocation by first converting the
        /// <see cref="int"/> to a string and then appending that result:
        /// <code>
        /// public StringBuilder Append(int value)
        /// {
        ///     return Append(value.ToString(CultureInfo.CurrentCulture));
        /// }
        /// </code>
        ///
        /// Note that this uses the current culture to do the conversion while <see cref="AppendInt(StringBuilder, int)"/> does
        /// not do any cultural sensitive conversion.
        /// </summary>
        /// <param name="sb">The <see cref="StringBuilder"/> to append to.</param>
        /// <param name="value">The <see cref="int"/> to append.</param>
        public static void AppendInt(this StringBuilder sb, int value)
        {
            if (value == 0)
            {
                sb.Append('0');
                return;
            }

            // special case min value since it'll overflow if we negate it
            if (value == int.MinValue)
            {
                sb.Append("-2147483648");
                return;
            }

            // do all math with positive integers
            if (value < 0)
            {
                sb.Append('-');
                value = -value;
            }

            // upper range of int is 1 billion, so we start dividing by that to get the digit at that position
            int divisor = 1_000_000_000;

            // remember when we found our first digit so we can keep adding intermediate zeroes
            bool digitFound = false;
            while (divisor > 0)
            {
                if (digitFound || value >= divisor)
                {
                    digitFound = true;
                    int digit = value / divisor;
                    value -= digit * divisor;

                    // convert the digit to char by adding the value to '0'.
                    // '0' + 0 = 48 + 0 = 48 = '0'
                    // '0' + 1 = 48 + 1 = 49 = '1'
                    // '0' + 2 = 48 + 2 = 50 = '2'
                    // etc...
                    sb.Append((char)('0' + digit));
                }

                divisor /= 10;
            }
        }

        public static int SingleIndex<T>(this IList<T> lst, Predicate<T> isMatch)
        {
            var foundIndex = -1;

            for (var index = 0; index < lst.Count; index++)
            {
                var item = lst[index];
                if (isMatch(item))
                {
                    if (foundIndex != -1)
                    {
                        throw new InvalidOperationException();
                    }

                    foundIndex = index;
                }
            }

            if (foundIndex == -1)
            {
                throw new InvalidOperationException();
            }

            return foundIndex;
        }

        public static int FirstIndex<T>(this IList<T> lst, Predicate<T> isMatch)
        {
            for (var index = 0; index < lst.Count; index++)
            {
                var item = lst[index];
                if (isMatch(item))
                {
                    return index;
                }
            }

            throw new InvalidOperationException();
        }

        public static bool Any<T>(this IReadOnlyList<T> lst, Predicate<T> isMatch)
        {
            for (var index = 0; index < lst.Count; index++)
            {
                var item = lst[index];
                if (isMatch(item))
                {
                    return true;
                }
            }

            return false;
        }
    }
}