File: SpanExtensions.cs
Web Access
Project: src\src\Razor\src\Shared\Microsoft.AspNetCore.Razor.Utilities.Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj (Microsoft.AspNetCore.Razor.Utilities.Shared)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#if !NET8_0_OR_GREATER
using System.Runtime.InteropServices;
#endif
 
#if !NET9_0_OR_GREATER
using System.Runtime.CompilerServices;
#endif
 
namespace System;
 
internal static class SpanExtensions
{
#if !NET8_0_OR_GREATER
    public static unsafe void Replace(this ReadOnlySpan<char> source, Span<char> destination, char oldValue, char newValue)
    {
        var length = source.Length;
        if (length == 0)
        {
            return;
        }
 
        if (length > destination.Length)
        {
            throw new ArgumentException(SR.Destination_is_too_short, nameof(destination));
        }
 
        ref var src = ref MemoryMarshal.GetReference(source);
        ref var dst = ref MemoryMarshal.GetReference(destination);
 
        for (var i = 0; i < length; i++)
        {
            var original = Unsafe.Add(ref src, i);
            Unsafe.Add(ref dst, i) = original == oldValue ? newValue : original;
        }
    }
 
    public static unsafe void Replace(this Span<char> span, char oldValue, char newValue)
    {
        var length = span.Length;
        if (length == 0)
        {
            return;
        }
 
        ref var src = ref MemoryMarshal.GetReference(span);
 
        for (var i = 0; i < length; i++)
        {
            ref var slot = ref Unsafe.Add(ref src, i);
 
            if (slot == oldValue)
            {
                slot = newValue;
            }
        }
    }
#endif
 
#if !NET9_0_OR_GREATER
    /// <summary>
    /// Determines whether the specified value appears at the start of the span.
    /// </summary>
    /// <param name="span">The span to search.</param>
    /// <param name="value">The value to compare.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool StartsWith<T>(this ReadOnlySpan<T> span, T value)
        where T : IEquatable<T>? =>
        span.Length != 0 && (span[0]?.Equals(value) ?? (object?)value is null);
 
    /// <summary>
    /// Determines whether the specified value appears at the end of the span.
    /// </summary>
    /// <param name="span">The span to search.</param>
    /// <param name="value">The value to compare.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool EndsWith<T>(this ReadOnlySpan<T> span, T value)
        where T : IEquatable<T>? =>
        span.Length != 0 && (span[^1]?.Equals(value) ?? (object?)value is null);
#endif
 
    extension<T>(Span<T> span)
    {
        public ReversedEnumerable<T> Reversed => new(span);
    }
 
    extension<T>(ReadOnlySpan<T> span)
    {
        public ReversedEnumerable<T> Reversed => new(span);
    }
 
    public readonly ref struct ReversedEnumerable<T>(ReadOnlySpan<T> span)
    {
        private readonly ReadOnlySpan<T> _span = span;
 
        public ReverseEnumerator<T> GetEnumerator() => new(_span);
    }
 
    public ref struct ReverseEnumerator<T>(ReadOnlySpan<T> span)
    {
        private readonly ReadOnlySpan<T> _span = span;
        private int _index = span.Length - 1;
        private T _current = default!;
 
        public readonly T Current => _current;
 
        public bool MoveNext()
        {
            if (_index >= 0)
            {
                _current = _span[_index];
                _index--;
                return true;
            }
 
            return false;
        }
 
        public void Reset()
        {
            _index = _span.Length - 1;
            _current = default!;
        }
    }
}