Closed dotlogix closed 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.
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;
}
}
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.
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