File: Data\ReadOnlyMemoryUtils.cs
Web Access
Project: src\src\Microsoft.ML.Core\Microsoft.ML.Core.csproj (Microsoft.ML.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.ML.Internal.Utilities;
using Microsoft.ML.Runtime;
 
namespace Microsoft.ML.Data;
 
[BestFriend]
internal static class ReadOnlyMemoryUtils
{
 
    /// <summary>
    /// Compare equality with the given system string value.
    /// </summary>
    public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)
    {
        Contracts.CheckValueOrNull(s);
 
        if (s == null)
            return memory.Length == 0;
 
        if (s.Length != memory.Length)
            return false;
 
        return memory.Span.SequenceEqual(s.AsSpan());
    }
 
    public static IEnumerable<ReadOnlyMemory<char>> Split(ReadOnlyMemory<char> memory, char[] separators)
    {
        Contracts.CheckValueOrNull(separators);
 
        if (memory.IsEmpty)
            yield break;
 
        if (separators == null || separators.Length == 0)
        {
            yield return memory;
            yield break;
        }
 
        var span = memory.Span;
        if (separators.Length == 1)
        {
            char chSep = separators[0];
            for (int ichCur = 0; ;)
            {
                int nextSep = span.IndexOf(chSep);
                if (nextSep == -1)
                {
                    yield return memory.Slice(ichCur);
                    yield break;
                }
 
                yield return memory.Slice(ichCur, nextSep);
 
                // Skip the separator.
                ichCur += nextSep + 1;
                span = memory.Slice(ichCur).Span;
            }
        }
        else
        {
            for (int ichCur = 0; ;)
            {
                int nextSep = span.IndexOfAny(separators);
                if (nextSep == -1)
                {
                    yield return memory.Slice(ichCur);
                    yield break;
                }
 
                yield return memory.Slice(ichCur, nextSep);
 
                // Skip the separator.
                ichCur += nextSep + 1;
                span = memory.Slice(ichCur).Span;
            }
        }
    }
 
    /// <summary>
    /// Splits <paramref name="memory"/> on the left-most occurrence of separator and produces the left
    /// and right <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> values. If <paramref name="memory"/> does not contain the separator character,
    /// this returns false and sets <paramref name="left"/> to this instance and <paramref name="right"/>
    /// to the default <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> value.
    /// </summary>
    public static bool SplitOne(ReadOnlyMemory<char> memory, char separator, out ReadOnlyMemory<char> left, out ReadOnlyMemory<char> right)
    {
        if (memory.IsEmpty)
        {
            left = memory;
            right = default;
            return false;
        }
 
        int index = memory.Span.IndexOf(separator);
        if (index == -1)
        {
            left = memory;
            right = default;
            return false;
        }
 
        left = memory.Slice(0, index);
        right = memory.Slice(index + 1, memory.Length - index - 1);
        return true;
    }
 
    /// <summary>
    /// Splits <paramref name="memory"/> on the left-most occurrence of an element of separators character array and
    /// produces the left and right <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> values. If <paramref name="memory"/> does not contain any of the
    /// characters in separators, this return false and initializes <paramref name="left"/> to this instance
    /// and <paramref name="right"/> to the default <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> value.
    /// </summary>
    public static bool SplitOne(ReadOnlyMemory<char> memory, char[] separators, out ReadOnlyMemory<char> left, out ReadOnlyMemory<char> right)
    {
        Contracts.CheckValueOrNull(separators);
 
        if (memory.IsEmpty || separators == null || separators.Length == 0)
        {
            left = memory;
            right = default;
            return false;
        }
 
        int index;
        if (separators.Length == 1)
            index = memory.Span.IndexOf(separators[0]);
        else
            index = memory.Span.IndexOfAny(separators);
 
        if (index == -1)
        {
            left = memory;
            right = default;
            return false;
        }
 
        left = memory.Slice(0, index);
        right = memory.Slice(index + 1, memory.Length - index - 1);
        return true;
    }
 
    /// <summary>
    /// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with leading and trailing spaces trimmed. Note that this
    /// will remove only spaces, not any form of whitespace.
    /// </summary>
    public static ReadOnlyMemory<char> TrimSpaces(ReadOnlyMemory<char> memory)
    {
        if (memory.IsEmpty)
            return memory;
 
        int ichLim = memory.Length;
        int ichMin = 0;
        var span = memory.Span;
        if (span[ichMin] != ' ' && span[ichLim - 1] != ' ')
            return memory;
 
        while (ichMin < ichLim && span[ichMin] == ' ')
            ichMin++;
        while (ichMin < ichLim && span[ichLim - 1] == ' ')
            ichLim--;
        return memory.Slice(ichMin, ichLim - ichMin);
    }
 
    /// <summary>
    /// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with leading and trailing whitespace trimmed.
    /// </summary>
    public static ReadOnlyMemory<char> TrimWhiteSpace(ReadOnlyMemory<char> memory)
    {
        if (memory.IsEmpty)
            return memory;
 
        int ichMin = 0;
        int ichLim = memory.Length;
        var span = memory.Span;
        if (!char.IsWhiteSpace(span[ichMin]) && !char.IsWhiteSpace(span[ichLim - 1]))
            return memory;
 
        while (ichMin < ichLim && char.IsWhiteSpace(span[ichMin]))
            ichMin++;
        while (ichMin < ichLim && char.IsWhiteSpace(span[ichLim - 1]))
            ichLim--;
 
        return memory.Slice(ichMin, ichLim - ichMin);
    }
 
    /// <summary>
    /// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
    /// </summary>
    public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory)
    {
        if (memory.IsEmpty)
            return memory;
 
        int ichLim = memory.Length;
        var span = memory.Span;
        if (!char.IsWhiteSpace(span[ichLim - 1]))
            return memory;
 
        while (0 < ichLim && char.IsWhiteSpace(span[ichLim - 1]))
            ichLim--;
 
        return memory.Slice(0, ichLim);
    }
 
    public static void AddLowerCaseToStringBuilder(ReadOnlySpan<char> span, StringBuilder sb)
    {
        Contracts.CheckValue(sb, nameof(sb));
 
        if (!span.IsEmpty)
        {
            int min = 0;
            int j;
            for (j = min; j < span.Length; j++)
            {
                char ch = CharUtils.ToLowerInvariant(span[j]);
                if (ch != span[j])
                {
                    sb.AppendSpan(span.Slice(min, j - min)).Append(ch);
                    min = j + 1;
                }
            }
 
            Contracts.Assert(j == span.Length);
            if (min != j)
                sb.AppendSpan(span.Slice(min, j - min));
        }
    }
 
    public static StringBuilder AppendMemory(this StringBuilder sb, ReadOnlyMemory<char> memory)
    {
        Contracts.CheckValue(sb, nameof(sb));
        if (!memory.IsEmpty)
            sb.AppendSpan(memory.Span);
 
        return sb;
    }
 
    public static StringBuilder AppendSpan(this StringBuilder sb, ReadOnlySpan<char> span)
    {
        unsafe
        {
            fixed (char* valueChars = &MemoryMarshal.GetReference(span))
            {
                sb.Append(valueChars, span.Length);
            }
        }
 
        return sb;
    }
 
    public sealed class ReadonlyMemoryCharComparer : IEqualityComparer<ReadOnlyMemory<char>>
    {
        public bool Equals(ReadOnlyMemory<char> x, ReadOnlyMemory<char> y)
        {
            return x.Span.SequenceEqual(y.Span);
        }
 
        public int GetHashCode(ReadOnlyMemory<char> obj)
        {
            return (int)Hashing.HashString(obj.Span);
        }
    }
}