meziantou / Meziantou.Polyfill

MIT License
43 stars 1 forks source link

Consider adding the rest of the methods in the MemoryExtensions class #29

Closed dotlogix closed 10 months ago

dotlogix commented 10 months ago

Provide a link to the method or type you want to polyfill

https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.indexofanyexcept?view=net-8.0

Other information

I noticed a lot of the methods in System.MemoryExtensions are missing, unfortunately. I think it would be a great addition to this library.

Some examples are: ContainsAny ContainsAnyInRange ContainsAnyExcept ContainsAnyExceptInRange

IndexOfAnyExcept LastIndexOfAnyExcept

Support for SearchValues (.Net8) would be neat but is more effort I guess because of how it is implemented internally

meziantou commented 10 months ago

That would be good additions! Would you like to implement it?

Support for SearchValues (.Net8) would be neat but is more effort I guess because of how it is implemented internally

The implementation doesn't need to be performant. The idea of Polyfill is to provide the feature, so it's easier to multitarget. A dummy implementation would be ok at first. If there is a need, it can be improved later.

dotlogix commented 10 months ago

These are the software fallbacks of the original reference source. I don't know how to integrate that with your library or if you want to add extensive testing for these.

using System.Collections.Generic;
using System.Runtime.CompilerServices;

// ReSharper disable once CheckNamespace
namespace System;

public static class MemoryExtensions {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool Contains<T>(this Span<T> span, T value) where T : IEquatable<T>? {
        return span.IndexOf(value) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool Contains<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>? {
        return span.IndexOf(value) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAny<T>(this Span<T> span, T value0, T value1) where T : IEquatable<T>? {
        return span.IndexOfAny(value0, value1) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAny<T>(this Span<T> span, T value0, T value1, T value2) where T : IEquatable<T>? {
        return span.IndexOfAny(value0, value1, value2) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAny<T>(this Span<T> span, ReadOnlySpan<T> values) where T : IEquatable<T>? {
        return span.IndexOfAny(values) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>? {
        return span.IndexOfAnyExcept(value) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1) where T : IEquatable<T>? {
        return span.IndexOfAnyExcept(value0, value1) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2) where T : IEquatable<T>? {
        return span.IndexOfAnyExcept(value0, value1, value2) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values) where T : IEquatable<T>? {
        return span.IndexOfAnyExcept(values) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAnyInRange<T>(this ReadOnlySpan<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        return span.IndexOfAnyInRange(lowInclusive, highInclusive) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ContainsAnyExceptInRange<T>(this ReadOnlySpan<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        return span.IndexOfAnyExceptInRange(lowInclusive, highInclusive) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOfAnyExcept<T>(this Span<T> span, T value) where T : IEquatable<T>? {
        return IndexOfAnyExcept((ReadOnlySpan<T>)span, value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOfAnyExcept<T>(this Span<T> span, T value0, T value1) where T : IEquatable<T>? {
        return IndexOfAnyExcept((ReadOnlySpan<T>)span, value0, value1);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOfAnyExcept<T>(this Span<T> span, T value0, T value1, T value2) where T : IEquatable<T>? {
        return IndexOfAnyExcept((ReadOnlySpan<T>)span, value0, value1, value2);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOfAnyExcept<T>(this Span<T> span, ReadOnlySpan<T> values) where T : IEquatable<T>? {
        return IndexOfAnyExcept((ReadOnlySpan<T>)span, values);
    }

    public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>? {
        var equalityComparer = EqualityComparer<T>.Default;
        for(var i = 0; i < span.Length; i++) {
            if(
                !equalityComparer.Equals(value, span[i])
            ) {
                return i;
            }
        }

        return -1;
    }

    public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1) where T : IEquatable<T>? {
        var equalityComparer = EqualityComparer<T>.Default;
        for(var i = 0; i < span.Length; i++) {
            if(
                !equalityComparer.Equals(value0, span[i]) || !equalityComparer.Equals(value1, span[i])
            ) {
                return i;
            }
        }

        return -1;
    }

    public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2) where T : IEquatable<T>? {
        var equalityComparer = EqualityComparer<T>.Default;
        for(var i = 0; i < span.Length; i++) {
            if(
                !equalityComparer.Equals(value0, span[i]) || !equalityComparer.Equals(value1, span[i]) || !equalityComparer.Equals(value2, span[i])
            ) {
                return i;
            }
        }

        return -1;
    }

    private static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2, T value3) where T : IEquatable<T>? {
        var equalityComparer = EqualityComparer<T>.Default;
        for(var i = 0; i < span.Length; i++) {
            if(
                !equalityComparer.Equals(value0, span[i]) || !equalityComparer.Equals(value1, span[i]) || !equalityComparer.Equals(value2, span[i]) || !equalityComparer.Equals(value3, span[i])
            ) {
                return i;
            }
        }

        return -1;
    }

    public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values) where T : IEquatable<T>? {
        switch(values.Length) {
            case 0:
                return span.IsEmpty ? -1 : 0;
            case 1:
                return IndexOfAnyExcept(span, values[0]);
            case 2:
                return IndexOfAnyExcept(span, values[0], values[1]);
            case 3:
                return IndexOfAnyExcept(span, values[0], values[1], values[2]);
            case 4:
                return IndexOfAnyExcept(span, values[0], values[1], values[2], values[3]);
            default:
                for(var i = 0; i < span.Length; i++) {
                    if(!values.Contains(span[i])) return i;
                }

                return -1;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int LastIndexOfAnyExcept<T>(this Span<T> span, T value) where T : IEquatable<T>? {
        return LastIndexOfAnyExcept((ReadOnlySpan<T>)span, value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int LastIndexOfAnyExcept<T>(this Span<T> span, T value0, T value1) where T : IEquatable<T>? {
        return LastIndexOfAnyExcept((ReadOnlySpan<T>)span, value0, value1);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int LastIndexOfAnyExcept<T>(this Span<T> span, T value0, T value1, T value2) where T : IEquatable<T>? {
        return LastIndexOfAnyExcept((ReadOnlySpan<T>)span, value0, value1, value2);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int LastIndexOfAnyExcept<T>(this Span<T> span, ReadOnlySpan<T> values) where T : IEquatable<T>? {
        return LastIndexOfAnyExcept((ReadOnlySpan<T>)span, values);
    }

    public static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>? {
        for(var i = span.Length - 1; i >= 0; i--) {
            if(
                !EqualityComparer<T>.Default.Equals(value, span[i])
            ) {
                return i;
            }
        }

        return -1;
    }

    public static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1) where T : IEquatable<T>? {
        for(var i = span.Length - 1; i >= 0; i--) {
            if(
                !EqualityComparer<T>.Default.Equals(value0, span[i]) || !EqualityComparer<T>.Default.Equals(value1, span[i])
            ) {
                return i;
            }
        }

        return -1;
    }

    public static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2) where T : IEquatable<T>? {
        for(var i = span.Length - 1; i >= 0; i--) {
            if(
                !EqualityComparer<T>.Default.Equals(value0, span[i]) || !EqualityComparer<T>.Default.Equals(value1, span[i]) || !EqualityComparer<T>.Default.Equals(value2, span[i])
            ) {
                return i;
            }
        }

        return -1;
    }

    private static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2, T value3) where T : IEquatable<T>? {
        for(var i = span.Length - 1; i >= 0; i--) {
            if(
                !EqualityComparer<T>.Default.Equals(value0, span[i]) || !EqualityComparer<T>.Default.Equals(value1, span[i]) || !EqualityComparer<T>.Default.Equals(value2, span[i]) || !EqualityComparer<T>.Default.Equals(value3, span[i])
            ) {
                return i;
            }
        }

        return -1;
    }

    public static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values) where T : IEquatable<T>? {
        switch(values.Length) {
            case 0:
                return span.IsEmpty ? -1 : 0;
            case 1:
                return LastIndexOfAnyExcept(span, values[0]);
            case 2:
                return LastIndexOfAnyExcept(span, values[0], values[1]);
            case 3:
                return LastIndexOfAnyExcept(span, values[0], values[1], values[2]);
            case 4:
                return LastIndexOfAnyExcept(span, values[0], values[1], values[2], values[3]);
            default:
                for(var i = span.Length - 1; i >= 0; i--) {
                    if(!values.Contains(span[i])) return i;
                }

                return -1;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOfAnyInRange<T>(this Span<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        return IndexOfAnyInRange((ReadOnlySpan<T>)span, lowInclusive, highInclusive);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOfAnyInRange<T>(this ReadOnlySpan<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        if(lowInclusive is null) throw new ArgumentNullException(nameof(lowInclusive));
        if(highInclusive is null) throw new ArgumentNullException(nameof(highInclusive));

        for(var i = 0; i < span.Length; i++) {
            var current = span[i];
            if((lowInclusive.CompareTo(current) <= 0) && (highInclusive.CompareTo(current) >= 0)) {
                return i;
            }
        }

        return -1;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOfAnyExceptInRange<T>(this Span<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        return IndexOfAnyExceptInRange((ReadOnlySpan<T>)span, lowInclusive, highInclusive);
    }

    public static int IndexOfAnyExceptInRange<T>(this ReadOnlySpan<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        if(lowInclusive is null) throw new ArgumentNullException(nameof(lowInclusive));
        if(highInclusive is null) throw new ArgumentNullException(nameof(highInclusive));

        for(var i = 0; i < span.Length; i++) {
            var current = span[i];
            if((lowInclusive.CompareTo(current) > 0) || (highInclusive.CompareTo(current) < 0)) {
                return i;
            }
        }

        return -1;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int LastIndexOfAnyInRange<T>(this Span<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        return LastIndexOfAnyInRange((ReadOnlySpan<T>)span, lowInclusive, highInclusive);
    }

    public static int LastIndexOfAnyInRange<T>(this ReadOnlySpan<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        if(lowInclusive is null) throw new ArgumentNullException(nameof(lowInclusive));
        if(highInclusive is null) throw new ArgumentNullException(nameof(highInclusive));

        for(var i = span.Length - 1; i >= 0; i--) {
            var current = span[i];
            if((lowInclusive.CompareTo(current) <= 0) && (highInclusive.CompareTo(current) >= 0)) {
                return i;
            }
        }

        return -1;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int LastIndexOfAnyExceptInRange<T>(this Span<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        return LastIndexOfAnyExceptInRange((ReadOnlySpan<T>)span, lowInclusive, highInclusive);
    }

    public static int LastIndexOfAnyExceptInRange<T>(this ReadOnlySpan<T> span, T lowInclusive, T highInclusive) where T : IComparable<T> {
        if(lowInclusive is null) throw new ArgumentNullException(nameof(lowInclusive));
        if(highInclusive is null) throw new ArgumentNullException(nameof(highInclusive));

        for(var i = span.Length - 1; i >= 0; i--) {
            var current = span[i];
            if((lowInclusive.CompareTo(current) > 0) || (highInclusive.CompareTo(current) < 0)) {
                return i;
            }
        }

        return -1;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int CommonPrefixLength<T>(this Span<T> span, ReadOnlySpan<T> other) {
        return CommonPrefixLength((ReadOnlySpan<T>)span, other);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int CommonPrefixLength<T>(this Span<T> span, ReadOnlySpan<T> other, IEqualityComparer<T>? comparer) {
        return CommonPrefixLength((ReadOnlySpan<T>)span, other, comparer);
    }

    public static int CommonPrefixLength<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other) {
        var minLength = Math.Min(span.Length, other.Length);
        for(var i = 0; i < minLength; i++) {
            if(!EqualityComparer<T>.Default.Equals(span[i], other[i])) {
                return i;
            }
        }

        return minLength;
    }

    public static int CommonPrefixLength<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other, IEqualityComparer<T>? comparer) {
        if(typeof(T).IsValueType && (comparer is null || ReferenceEquals(comparer, EqualityComparer<T>.Default))) {
            return CommonPrefixLength(span, other);
        }

        var minLength = Math.Min(span.Length, other.Length);
        comparer ??= EqualityComparer<T>.Default;
        for(var i = 0; i < minLength; i++) {
            if(!comparer.Equals(span[i], other[i])) {
                return i;
            }
        }

        return minLength;
    }
}
meziantou commented 10 months ago

I've added most methods in https://github.com/meziantou/Meziantou.Polyfill/commit/b3aca56eb6c348ef75c70253d1680bf179f07223 and https://github.com/meziantou/Meziantou.Polyfill/commit/b4d1124090974729a80d5c409db46aab37b7cfd4. Feel free to submit a PR to add more methods if needed. You can check the commits and the contribution guide to get started https://github.com/meziantou/Meziantou.Polyfill?tab=readme-ov-file#contribution. You can also ask questions if you need help.