File: src\Shared\StringSplit\StringSplitExtensions.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.Diagnostics.ResourceMonitoring\Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj (Microsoft.Extensions.Diagnostics.ResourceMonitoring)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#if NET6_0
 
using System;
using Microsoft.Shared.Diagnostics;
 
namespace Microsoft.Shared.StringSplit;
 
#if !SHARED_PROJECT
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal static class StringSplitExtensions
{
    /// <summary>
    /// Splits a string into a number of string segments.
    /// </summary>
    /// <param name="input">The string to split.</param>
    /// <param name="separator">The string that delimits the substrings in this instance.</param>
    /// <param name="result">A span to receive the individual string segments.</param>
    /// <param name="numSegments">The number of string segments copied to the output.</param>
    /// <param name="comparison">The kind of string comparison to apply to the separator strings.</param>
    /// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.</param>
    /// <returns><see langword="true" /> if there was enough space in the output array; otherwise, <see langword="false" />.</returns>
    public static bool TrySplit(
        this ReadOnlySpan<char> input,
        string separator,
        Span<StringRange> result,
        out int numSegments,
        StringComparison comparison = StringComparison.Ordinal,
        StringSplitOptions options = StringSplitOptions.None)
    {
        _ = Throw.IfNull(separator);
        CheckStringSplitOptions(options);
 
        numSegments = 0;
 
        int start = 0;
        while (true)
        {
            int index = -1;
            int separatorLen = 0;
 
            int found = input.Slice(start).IndexOf(separator.AsSpan(), comparison);
            if (found >= 0)
            {
                if (found < index || index < 0)
                {
                    separatorLen = separator.Length;
                    index = found;
                }
            }
 
            var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
 
            var rangeStart = start;
            if ((options & StringSplitOptions.TrimEntries) != 0)
            {
                var len = sp.Length;
                sp = sp.TrimStart();
                rangeStart = start + len - sp.Length;
                sp = sp.TrimEnd();
            }
 
            if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
            {
                if (numSegments >= result.Length)
                {
                    return false;
                }
 
                result[numSegments++] = new StringRange(rangeStart, sp.Length);
            }
 
            if (index < 0)
            {
                return true;
            }
 
            start += index + separatorLen;
        }
    }
 
    private static void CheckStringSplitOptions(StringSplitOptions options)
    {
        const StringSplitOptions AllValidFlags = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries;
 
        if ((options & ~AllValidFlags) != 0)
        {
            // at least one invalid flag was set
            Throw.ArgumentException(nameof(options), "Invalid split options specified");
        }
    }
}
 
#endif