Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/MonoMod.Backports.Shims/MonoMod.Backports.Shims.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.Build.NoTargets">
<Project Sdk="Microsoft.Build.NoTargets">

<PropertyGroup>
<IsPackable>true</IsPackable>
Expand All @@ -18,6 +18,10 @@
<PackInference Remove="@(PackInference)" />
</ItemGroup>

<ItemDefinitionGroup>
<ExtraShimPackageVersion Visible="false" />
</ItemDefinitionGroup>

<!-- Packages we're going to shim -->
<ItemGroup>
<PackageReference Include="Microsoft.Bcl.HashCode" Version="6.0.0" Shim="true" />
Expand Down
2 changes: 1 addition & 1 deletion src/MonoMod.Backports/MonoMod.Backports.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<!-- target frameworks are defined in Common.props -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@
[assembly: TypeForwardedTo(typeof(Utf8Parser))]

[assembly: TypeForwardedTo(typeof(ArrayPool<>))]

[assembly: TypeForwardedTo(typeof(SpanAction<,>))]
[assembly: TypeForwardedTo(typeof(ReadOnlySpanAction<,>))]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace System.Buffers
{
public delegate void SpanAction<T, in TArg>(Span<T> span, TArg arg);
public delegate void ReadOnlySpanAction<T, in TArg>(ReadOnlySpan<T> span, TArg arg);
}
91 changes: 91 additions & 0 deletions src/MonoMod.Backports/System/CharEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
namespace System
{
public static class CharEx
{
// there is no real reason to forward these, they are all very simple
extension(char)
{
/// <summary>Indicates whether a character is categorized as an ASCII letter.</summary>
/// <param name="c">The character to evaluate.</param>
/// <returns>true if <paramref name="c"/> is an ASCII letter; otherwise, false.</returns>
/// <remarks>
/// This determines whether the character is in the range 'A' through 'Z', inclusive,
/// or 'a' through 'z', inclusive.
/// </remarks>
public static bool IsAsciiLetter(char c) => (uint)((c | 0x20) - 'a') <= 'z' - 'a';

/// <summary>Indicates whether a character is categorized as a lowercase ASCII letter.</summary>
/// <param name="c">The character to evaluate.</param>
/// <returns>true if <paramref name="c"/> is a lowercase ASCII letter; otherwise, false.</returns>
/// <remarks>
/// This determines whether the character is in the range 'a' through 'z', inclusive.
/// </remarks>
public static bool IsAsciiLetterLower(char c) => IsBetween(c, 'a', 'z');

/// <summary>Indicates whether a character is categorized as an uppercase ASCII letter.</summary>
/// <param name="c">The character to evaluate.</param>
/// <returns>true if <paramref name="c"/> is an uppercase ASCII letter; otherwise, false.</returns>
/// <remarks>
/// This determines whether the character is in the range 'A' through 'Z', inclusive.
/// </remarks>
public static bool IsAsciiLetterUpper(char c) => IsBetween(c, 'A', 'Z');

/// <summary>Indicates whether a character is categorized as an ASCII digit.</summary>
/// <param name="c">The character to evaluate.</param>
/// <returns>true if <paramref name="c"/> is an ASCII digit; otherwise, false.</returns>
/// <remarks>
/// This determines whether the character is in the range '0' through '9', inclusive.
/// </remarks>
public static bool IsAsciiDigit(char c) => IsBetween(c, '0', '9');

/// <summary>Indicates whether a character is categorized as an ASCII letter or digit.</summary>
/// <param name="c">The character to evaluate.</param>
/// <returns>true if <paramref name="c"/> is an ASCII letter or digit; otherwise, false.</returns>
/// <remarks>
/// This determines whether the character is in the range 'A' through 'Z', inclusive,
/// 'a' through 'z', inclusive, or '0' through '9', inclusive.
/// </remarks>
public static bool IsAsciiLetterOrDigit(char c) => IsAsciiLetter(c) | IsBetween(c, '0', '9');

/// <summary>Indicates whether a character is categorized as an ASCII hexadecimal digit.</summary>
/// <param name="c">The character to evaluate.</param>
/// <returns>true if <paramref name="c"/> is a hexadecimal digit; otherwise, false.</returns>
/// <remarks>
/// This determines whether the character is in the range '0' through '9', inclusive,
/// 'A' through 'F', inclusive, or 'a' through 'f', inclusive.
/// </remarks>
public static bool IsAsciiHexDigit(char c) => IsAsciiDigit(c) || IsBetween(c, 'a', 'f') || IsBetween(c, 'A', 'F');

/// <summary>Indicates whether a character is categorized as an ASCII upper-case hexadecimal digit.</summary>
/// <param name="c">The character to evaluate.</param>
/// <returns>true if <paramref name="c"/> is a hexadecimal digit; otherwise, false.</returns>
/// <remarks>
/// This determines whether the character is in the range '0' through '9', inclusive,
/// or 'A' through 'F', inclusive.
/// </remarks>
public static bool IsAsciiHexDigitUpper(char c) => IsAsciiDigit(c) || IsBetween(c, 'A', 'F');

/// <summary>Indicates whether a character is categorized as an ASCII lower-case hexadecimal digit.</summary>
/// <param name="c">The character to evaluate.</param>
/// <returns>true if <paramref name="c"/> is a lower-case hexadecimal digit; otherwise, false.</returns>
/// <remarks>
/// This determines whether the character is in the range '0' through '9', inclusive,
/// or 'a' through 'f', inclusive.
/// </remarks>
public static bool IsAsciiHexDigitLower(char c) => IsAsciiDigit(c) || IsBetween(c, 'a', 'f');

/// <summary>Indicates whether a character is within the specified inclusive range.</summary>
/// <param name="c">The character to evaluate.</param>
/// <param name="minInclusive">The lower bound, inclusive.</param>
/// <param name="maxInclusive">The upper bound, inclusive.</param>
/// <returns>true if <paramref name="c"/> is within the specified range; otherwise, false.</returns>
/// <remarks>
/// The method does not validate that <paramref name="maxInclusive"/> is greater than or equal
/// to <paramref name="minInclusive"/>. If <paramref name="maxInclusive"/> is less than
/// <paramref name="minInclusive"/>, the behavior is undefined.
/// </remarks>
public static bool IsBetween(char c, char minInclusive, char maxInclusive) =>
(uint)(c - minInclusive) <= (uint)(maxInclusive - minInclusive);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#if NET8_0_OR_GREATER
#define HAS_LISTSPANMETHODS
#endif
#if NET7_0_OR_GREATER
#define HAS_ASREADONLY
#endif

using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
#if !HAS_LISTSPANMETHODS
using System.Runtime.InteropServices;
#endif

namespace System.Collections.Generic
{
[SuppressMessage("Design", "CA1002:Do not expose generic lists",
Justification = "Replicating existing APIs")]
public static class CollectionExtensionsEx
{
public static void AddRange<T>(
#if !HAS_LISTSPANMETHODS
this
#endif
List<T> list, params ReadOnlySpan<T> source
)
{
#if HAS_LISTSPANMETHODS
list.AddRange(source);
#else
ThrowHelper.ThrowIfArgumentNull(list, ExceptionArgument.list);
if (source.IsEmpty)
{
return;
}
var currentCount = list.Count;
CollectionsMarshal.SetCount(list, currentCount + source.Length);
source.CopyTo(CollectionsMarshal.AsSpan(list).Slice(currentCount + 1));
#endif
}

public static void InsertRange<T>(
#if !HAS_LISTSPANMETHODS
this
#endif
List<T> list, int index, params ReadOnlySpan<T> source
)
{
#if HAS_LISTSPANMETHODS
list.InsertRange(index, source);
#else
ThrowHelper.ThrowIfArgumentNull(list, ExceptionArgument.list);
if ((uint)index > (uint)list.Count)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
}
if (source.IsEmpty)
{
return;
}
var currentCount = list.Count;
CollectionsMarshal.SetCount(list, currentCount + source.Length);
var items = CollectionsMarshal.AsSpan(list);
if (index < currentCount)
{
items.Slice(index, currentCount - index).CopyTo(items.Slice(index + source.Length));
}
source.CopyTo(items.Slice(index));
#endif
}

public static void CopyTo<T>(
#if !HAS_LISTSPANMETHODS
this
#endif
List<T> list, Span<T> destination
)
{
#if HAS_LISTSPANMETHODS
list.CopyTo(destination);
#else
ThrowHelper.ThrowIfArgumentNull(list, ExceptionArgument.list);
CollectionsMarshal.AsSpan(list).CopyTo(destination);
#endif
}

public static ReadOnlyCollection<T> AsReadOnly<T>(
#if !HAS_ASREADONLY
this
#endif
IList<T> list
) => new(list);

public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(
#if !HAS_ASREADONLY
this
#endif
IDictionary<TKey, TValue> list
) where TKey : notnull => new(list);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;

[assembly: TypeForwardedTo(typeof(ReadOnlyDictionary<,>))]
Loading