diff --git a/PolyShim.Tests/Net70/StreamTests.cs b/PolyShim.Tests/Net70/StreamTests.cs index 7b98fb4..0eef481 100644 --- a/PolyShim.Tests/Net70/StreamTests.cs +++ b/PolyShim.Tests/Net70/StreamTests.cs @@ -47,7 +47,7 @@ public void ReadAtLeast_Array_SilentOverflow_Test() // Assert bytesRead.Should().Be(5); - buffer.Should().StartWith(new byte[] { 1, 2, 3, 4, 5 }); + buffer.Should().StartWith([1, 2, 3, 4, 5]); } [Fact] @@ -118,7 +118,7 @@ public async Task ReadAtLeastAsync_Array_SilentOverflow_Test() // Assert bytesRead.Should().Be(5); - buffer.Should().StartWith(new byte[] { 1, 2, 3, 4, 5 }); + buffer.Should().StartWith([1, 2, 3, 4, 5]); } [Fact] @@ -163,7 +163,7 @@ public void ReadAtLeast_Span_Test() // Assert bytesRead.Should().BeGreaterOrEqualTo(3); - buffer.Should().StartWith(new byte[] { 1, 2, 3 }); + buffer.Should().StartWith([1, 2, 3]); } finally { @@ -203,7 +203,7 @@ public void ReadAtLeast_Span_SilentOverflow_Test() // Assert bytesRead.Should().BeGreaterOrEqualTo(5); - buffer.Should().StartWith(new byte[] { 1, 2, 3, 4, 5 }); + buffer.Should().StartWith([1, 2, 3, 4, 5]); } finally { @@ -224,7 +224,7 @@ public void ReadExactly_Span_Test() stream.ReadExactly(buffer.AsSpan()[..3]); // Assert - buffer.Should().StartWith(new byte[] { 1, 2, 3 }); + buffer.Should().StartWith([1, 2, 3]); } finally { @@ -244,7 +244,7 @@ public async Task ReadAtLeastAsync_Memory_Test() // Assert bytesRead.Should().BeGreaterOrEqualTo(3); - buffer.Memory.ToArray().Should().StartWith(new byte[] { 1, 2, 3 }); + buffer.Memory.ToArray().Should().StartWith([1, 2, 3]); } [Fact] @@ -272,7 +272,7 @@ public async Task ReadAtLeastAsync_Memory_SilentOverflow_Test() // Assert bytesRead.Should().BeGreaterOrEqualTo(5); - buffer.Memory.ToArray().Should().StartWith(new byte[] { 1, 2, 3, 4 }); + buffer.Memory.ToArray().Should().StartWith([1, 2, 3, 4]); } [Fact] @@ -286,6 +286,6 @@ public async Task ReadExactlyAsync_Memory_Test() await stream.ReadExactlyAsync(buffer.Memory[..3]); // Assert - buffer.Memory.ToArray().Should().StartWith(new byte[] { 1, 2, 3 }); + buffer.Memory.ToArray().Should().StartWith([1, 2, 3]); } } diff --git a/PolyShim.Tests/Net90/FileTests.cs b/PolyShim.Tests/Net90/FileTests.cs index 4fd98c2..0a7b732 100644 --- a/PolyShim.Tests/Net90/FileTests.cs +++ b/PolyShim.Tests/Net90/FileTests.cs @@ -18,7 +18,7 @@ public void AppendAllBytes_Test() File.WriteAllBytes(tempFilePath, [0x00, 0x01, 0x02]); // Act - File.AppendAllBytes(tempFilePath, new byte[] { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E }); + File.AppendAllBytes(tempFilePath, [0x0A, 0x0B, 0x0C, 0x0D, 0x0E]); // Assert var readBytes = File.ReadAllBytes(tempFilePath); diff --git a/PolyShim.Tests/NetCore21/MemoryTests.cs b/PolyShim.Tests/NetCore21/MemoryTests.cs new file mode 100644 index 0000000..708698a --- /dev/null +++ b/PolyShim.Tests/NetCore21/MemoryTests.cs @@ -0,0 +1,59 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace PolyShim.Tests.NetCore21; + +public class MemoryTests +{ + [Fact] + public void ImplicitConversion_Test() + { + // Act + Memory memory = new byte[] { 1, 2, 3, 4, 5 }; + + // Assert + memory.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void Slice_Test() + { + // Arrange + Memory memory = new byte[] { 10, 20, 30, 40, 50 }; + + // Act & Assert + memory.Slice(0, 2).ToArray().Should().Equal(10, 20); + memory.Slice(2, 2).ToArray().Should().Equal(30, 40); + memory.Slice(4, 1).ToArray().Should().Equal(50); + } + + [Fact] + public void CopyTo_Test() + { + // Arrange + Memory source = new byte[] { 1, 2, 3, 4, 5 }; + Memory destination = new byte[5]; + + // Act + source.CopyTo(destination); + + // Assert + destination.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void TryCopyTo_Test() + { + // Arrange + Memory source = new byte[] { 1, 2, 3, 4, 5 }; + Memory destination = new byte[5]; + + // Act + var result = source.TryCopyTo(destination); + + // Assert + result.Should().BeTrue(); + destination.ToArray().Should().Equal(1, 2, 3, 4, 5); + } +} diff --git a/PolyShim.Tests/NetCore21/RandomTests.cs b/PolyShim.Tests/NetCore21/RandomTests.cs index f4b03c9..5f884e2 100644 --- a/PolyShim.Tests/NetCore21/RandomTests.cs +++ b/PolyShim.Tests/NetCore21/RandomTests.cs @@ -21,9 +21,7 @@ public void NextBytes_Span_Test() // Assert buffer .Should() - .StartWith( - new byte[] { 0x30, 0xE6, 0x63, 0xCA, 0x5F, 0x41, 0x21, 0x48, 0x1F, 0x8F } - ); + .StartWith([0x30, 0xE6, 0x63, 0xCA, 0x5F, 0x41, 0x21, 0x48, 0x1F, 0x8F]); } finally { diff --git a/PolyShim.Tests/NetCore21/ReadOnlyMemoryTests.cs b/PolyShim.Tests/NetCore21/ReadOnlyMemoryTests.cs new file mode 100644 index 0000000..b57970c --- /dev/null +++ b/PolyShim.Tests/NetCore21/ReadOnlyMemoryTests.cs @@ -0,0 +1,59 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace PolyShim.Tests.NetCore21; + +public class ReadOnlyMemoryTests +{ + [Fact] + public void ImplicitConversion_Test() + { + // Act + ReadOnlyMemory memory = new byte[] { 1, 2, 3, 4, 5 }; + + // Assert + memory.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void Slice_Test() + { + // Arrange + ReadOnlyMemory memory = new byte[] { 10, 20, 30, 40, 50 }; + + // Act & Assert + memory.Slice(0, 2).ToArray().Should().Equal(10, 20); + memory.Slice(2, 2).ToArray().Should().Equal(30, 40); + memory.Slice(4, 1).ToArray().Should().Equal(50); + } + + [Fact] + public void CopyTo_Test() + { + // Arrange + ReadOnlyMemory source = new byte[] { 1, 2, 3, 4, 5 }; + Memory destination = new byte[5]; + + // Act + source.CopyTo(destination); + + // Assert + destination.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void TryCopyTo_Test() + { + // Arrange + ReadOnlyMemory source = new byte[] { 1, 2, 3, 4, 5 }; + Memory destination = new byte[5]; + + // Act + var result = source.TryCopyTo(destination); + + // Assert + result.Should().BeTrue(); + destination.ToArray().Should().Equal(1, 2, 3, 4, 5); + } +} diff --git a/PolyShim.Tests/NetCore21/ReadOnlySpanTests.cs b/PolyShim.Tests/NetCore21/ReadOnlySpanTests.cs new file mode 100644 index 0000000..67e8084 --- /dev/null +++ b/PolyShim.Tests/NetCore21/ReadOnlySpanTests.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using FluentAssertions; +using Xunit; + +namespace PolyShim.Tests.NetCore21; + +public class ReadOnlySpanTests +{ + [Fact] + public void ImplicitConversion_Test() + { + // Act + ReadOnlySpan readOnlySpan = [1, 2, 3, 4, 5]; + + // Assert + readOnlySpan.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void Indexer_Test() + { + // Arrange + ReadOnlySpan readOnlySpan = [10, 20, 30, 40, 50]; + + // Act & Assert + readOnlySpan[0].Should().Be(10); + readOnlySpan[1].Should().Be(20); + readOnlySpan[2].Should().Be(30); + readOnlySpan[3].Should().Be(40); + readOnlySpan[4].Should().Be(50); + } + + [Fact] + public void Slice_Test() + { + // Arrange + ReadOnlySpan readOnlySpan = [10, 20, 30, 40, 50]; + + // Act + var slice = readOnlySpan.Slice(1, 3); + + // Assert + slice.ToArray().Should().Equal(20, 30, 40); + } + + [Fact] + public void CopyTo_Test() + { + // Arrange + ReadOnlySpan source = [1, 2, 3, 4, 5]; + Span destination = new byte[5]; + + // Act + source.CopyTo(destination); + + // Assert + destination.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void TryCopyTo_Test() + { + // Arrange + ReadOnlySpan source = [1, 2, 3, 4, 5]; + Span destination = new byte[5]; + + // Act + var result = source.TryCopyTo(destination); + + // Assert + result.Should().BeTrue(); + destination.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void Enumeration_Test() + { + // Arrange + ReadOnlySpan readOnlySpan = [1, 2, 3, 4, 5]; + var result = new List(); + + // Act + foreach (var item in readOnlySpan) + result.Add(item); + + // Assert + result.Should().Equal(1, 2, 3, 4, 5); + } +} diff --git a/PolyShim.Tests/NetCore21/SpanTests.cs b/PolyShim.Tests/NetCore21/SpanTests.cs new file mode 100644 index 0000000..30f9a23 --- /dev/null +++ b/PolyShim.Tests/NetCore21/SpanTests.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using FluentAssertions; +using Xunit; + +namespace PolyShim.Tests.NetCore21; + +public class SpanTests +{ + [Fact] + public void ImplicitConversion_Test() + { + // Act + Span span = [1, 2, 3, 4, 5]; + + // Assert + span.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void Indexer_Test() + { + // Arrange + Span span = [10, 20, 30, 40, 50]; + + // Act & Assert + span[0].Should().Be(10); + span[1].Should().Be(20); + span[2].Should().Be(30); + span[3].Should().Be(40); + span[4].Should().Be(50); + } + + [Fact] + public void Slice_Test() + { + // Arrange + Span span = [1, 2, 3, 4, 5]; + + // Act + var slice = span.Slice(1, 3); + + // Assert + slice.ToArray().Should().Equal(2, 3, 4); + } + + [Fact] + public void Fill_Test() + { + // Arrange + Span span = [1, 2, 3, 4, 5]; + + // Act + span.Fill(42); + + // Assert + span.ToArray().Should().Equal(42, 42, 42, 42, 42); + } + + [Fact] + public void Clear_Test() + { + // Arrange + Span span = [1, 2, 3, 4, 5]; + + // Act + span.Clear(); + + // Assert + span.ToArray().Should().Equal(0, 0, 0, 0, 0); + } + + [Fact] + public void CopyTo_Test() + { + // Arrange + Span source = [1, 2, 3, 4, 5]; + Span destination = stackalloc byte[5]; + + // Act + source.CopyTo(destination); + + // Assert + destination.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void TryCopyTo_Test() + { + // Arrange + Span source = [1, 2, 3, 4, 5]; + Span destination = stackalloc byte[5]; + + // Act + var result = source.TryCopyTo(destination); + + // Assert + result.Should().BeTrue(); + destination.ToArray().Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void Enumeration_Test() + { + // Arrange + Span span = [1, 2, 3, 4, 5]; + var collected = new List(); + + // Act + foreach (var item in span) + collected.Add(item); + + // Assert + collected.Should().Equal(1, 2, 3, 4, 5); + } +} diff --git a/PolyShim.Tests/NetCore21/StreamReaderTests.cs b/PolyShim.Tests/NetCore21/StreamReaderTests.cs index 75d0fe7..95033a2 100644 --- a/PolyShim.Tests/NetCore21/StreamReaderTests.cs +++ b/PolyShim.Tests/NetCore21/StreamReaderTests.cs @@ -10,39 +10,7 @@ namespace PolyShim.Tests.NetCore21; public class StreamReaderTests { [Fact] - public void Read_Array_Test() - { - // Arrange - using var stream = new MemoryStream("Hello world"u8.ToArray()); - using var reader = new StreamReader(stream); - var buffer = new char[stream.Length]; - - // Act - var charsRead = reader.Read(buffer); - - // Assert - charsRead.Should().Be(11); - buffer.Should().StartWith("Hello world"); - } - - [Fact] - public async Task ReadAsync_Array_Test() - { - // Arrange - using var stream = new MemoryStream("Hello world"u8.ToArray()); - using var reader = new StreamReader(stream); - var buffer = new char[stream.Length]; - - // Act - var charsRead = await reader.ReadAsync(buffer); - - // Assert - charsRead.Should().Be(11); - buffer.Should().StartWith("Hello world"); - } - - [Fact] - public void Read_Span_Test() + public void Read_Test() { // Arrange using var stream = new MemoryStream("Hello world"u8.ToArray()); @@ -65,7 +33,7 @@ public void Read_Span_Test() } [Fact] - public async Task ReadAsync_Memory_Test() + public async Task ReadAsync_Test() { // Arrange using var stream = new MemoryStream("Hello world"u8.ToArray()); diff --git a/PolyShim.Tests/NetCore21/StreamTests.cs b/PolyShim.Tests/NetCore21/StreamTests.cs index 746ca5e..373d53f 100644 --- a/PolyShim.Tests/NetCore21/StreamTests.cs +++ b/PolyShim.Tests/NetCore21/StreamTests.cs @@ -9,35 +9,6 @@ namespace PolyShim.Tests.NetCore21; public class StreamTests { - [Fact] - public void Read_Array_Test() - { - // Arrange - using var stream = new MemoryStream([1, 2, 3, 4, 5]); - var buffer = new byte[5]; - - // Act - var bytesRead = stream.Read(buffer); - - // Assert - bytesRead.Should().Be(5); - buffer.Should().Equal(1, 2, 3, 4, 5); - } - - [Fact] - public void Write_Array_Test() - { - // Arrange - using var stream = new MemoryStream(); - var buffer = new byte[] { 1, 2, 3, 4, 5 }; - - // Act - stream.Write(buffer); - - // Assert - stream.ToArray().Should().Equal(1, 2, 3, 4, 5); - } - [Fact] public async Task CopyToAsync_Test() { @@ -53,36 +24,7 @@ public async Task CopyToAsync_Test() } [Fact] - public async Task ReadAsync_Array_Test() - { - // Arrange - using var stream = new MemoryStream([1, 2, 3, 4, 5]); - var buffer = new byte[stream.Length]; - - // Act - var bytesRead = await stream.ReadAsync(buffer); - - // Assert - bytesRead.Should().Be(5); - buffer.Should().Equal(1, 2, 3, 4, 5); - } - - [Fact] - public async Task WriteAsync_Array_Test() - { - // Arrange - using var stream = new MemoryStream(); - var buffer = new byte[] { 1, 2, 3, 4, 5 }; - - // Act - await stream.WriteAsync(buffer); - - // Assert - stream.ToArray().Should().Equal(1, 2, 3, 4, 5); - } - - [Fact] - public void Read_Span_Test() + public void Read_Test() { // Arrange using var stream = new MemoryStream([1, 2, 3, 4, 5]); @@ -95,7 +37,7 @@ public void Read_Span_Test() // Assert bytesRead.Should().BeGreaterOrEqualTo(5); - buffer.Should().StartWith(new byte[] { 1, 2, 3, 4, 5 }); + buffer.Should().StartWith([1, 2, 3, 4, 5]); } finally { @@ -104,7 +46,7 @@ public void Read_Span_Test() } [Fact] - public void Write_Span_Test() + public void Write_Test() { // Arrange using var stream = new MemoryStream(); @@ -122,7 +64,7 @@ public void Write_Span_Test() stream.Write(buffer.AsSpan()); // Assert - stream.ToArray().Should().StartWith(new byte[] { 1, 2, 3, 4, 5 }); + stream.ToArray().Should().StartWith([1, 2, 3, 4, 5]); } finally { @@ -131,7 +73,7 @@ public void Write_Span_Test() } [Fact] - public async Task ReadAsync_Memory_Test() + public async Task ReadAsync_Test() { // Arrange using var stream = new MemoryStream([1, 2, 3, 4, 5]); @@ -142,11 +84,11 @@ public async Task ReadAsync_Memory_Test() // Assert bytesRead.Should().BeGreaterOrEqualTo(5); - buffer.Memory.ToArray().Should().StartWith(new byte[] { 1, 2, 3, 4, 5 }); + buffer.Memory.ToArray().Should().StartWith([1, 2, 3, 4, 5]); } [Fact] - public async Task WriteAsync_Memory_Test() + public async Task WriteAsync_Test() { // Arrange using var stream = new MemoryStream(); @@ -162,6 +104,6 @@ public async Task WriteAsync_Memory_Test() await stream.WriteAsync(buffer.Memory); // Assert - stream.ToArray().Should().StartWith(new byte[] { 1, 2, 3, 4, 5 }); + stream.ToArray().Should().StartWith([1, 2, 3, 4, 5]); } } diff --git a/PolyShim.Tests/NetCore21/StreamWriterTests.cs b/PolyShim.Tests/NetCore21/StreamWriterTests.cs index 3c21efc..1ab1165 100644 --- a/PolyShim.Tests/NetCore21/StreamWriterTests.cs +++ b/PolyShim.Tests/NetCore21/StreamWriterTests.cs @@ -10,7 +10,7 @@ namespace PolyShim.Tests.NetCore21; public class StreamWriterTests { [Fact] - public void Write_Span_Test() + public void Write_Test() { // Arrange using var stream = new MemoryStream(); @@ -45,7 +45,7 @@ public void Write_Span_Test() } [Fact] - public async Task WriteAsync_Memory_Test() + public async Task WriteAsync_Test() { // Arrange using var stream = new MemoryStream(); diff --git a/PolyShim.Tests/PolyShim.Tests.csproj b/PolyShim.Tests/PolyShim.Tests.csproj index 9948bc9..21f9003 100644 --- a/PolyShim.Tests/PolyShim.Tests.csproj +++ b/PolyShim.Tests/PolyShim.Tests.csproj @@ -43,8 +43,6 @@ - - diff --git a/PolyShim/Net100/Random.cs b/PolyShim/Net100/Random.cs index 1bf2bdc..4c08d59 100644 --- a/PolyShim/Net100/Random.cs +++ b/PolyShim/Net100/Random.cs @@ -6,7 +6,6 @@ // ReSharper disable PartialTypeWithSinglePart using System; -using System.Collections.Generic; using System.Text; internal static partial class PolyfillExtensions @@ -23,11 +22,10 @@ public string GetHexString(int stringLength, bool lowercase = false) return hex.Substring(0, stringLength); } - // Signature-compatible replacement for GetString(ReadOnlySpan, int) // https://learn.microsoft.com/dotnet/api/system.random.getstring - public string GetString(char[] choices, int length) + public string GetString(ReadOnlySpan choices, int length) { - var buffer = new StringBuilder(); + var buffer = new StringBuilder(length); for (var i = 0; i < length; i++) { @@ -37,12 +35,6 @@ public string GetString(char[] choices, int length) return buffer.ToString(); } - -#if FEATURE_MEMORY - // https://learn.microsoft.com/dotnet/api/system.random.getstring - public string GetString(ReadOnlySpan choices, int length) => - random.GetString(choices.ToArray(), length); -#endif } } #endif diff --git a/PolyShim/Net70/Stream.cs b/PolyShim/Net70/Stream.cs index d8336db..5b1f37b 100644 --- a/PolyShim/Net70/Stream.cs +++ b/PolyShim/Net70/Stream.cs @@ -14,31 +14,6 @@ internal static partial class PolyfillExtensions { extension(Stream stream) { - // Signature-compatible replacement for ReadAtLeast(Span, ...) - // https://learn.microsoft.com/dotnet/api/system.io.stream.readatleast - public int ReadAtLeast(byte[] buffer, int minimumBytes, bool throwOnEndOfStream = true) - { - var totalBytesRead = 0; - while (totalBytesRead < buffer.Length) - { - var bytesRead = stream.Read( - buffer, - totalBytesRead, - Math.Min(minimumBytes, buffer.Length - totalBytesRead) - ); - - if (bytesRead <= 0) - break; - - totalBytesRead += bytesRead; - } - - if (totalBytesRead < minimumBytes && throwOnEndOfStream) - throw new EndOfStreamException(); - - return totalBytesRead; - } - // https://learn.microsoft.com/dotnet/api/system.io.stream.readexactly#system-io-stream-readexactly(system-byte()-system-int32-system-int32) public void ReadExactly(byte[] buffer, int offset, int count) { @@ -58,44 +33,7 @@ public void ReadExactly(byte[] buffer, int offset, int count) } } - // Signature-compatible replacement for ReadExactly(Span) - // https://learn.microsoft.com/dotnet/api/system.io.stream.readexactly#system-io-stream-readexactly(system-span((system-byte))) - public void ReadExactly(byte[] buffer) => stream.ReadExactly(buffer, 0, buffer.Length); - #if FEATURE_TASK - // Signature-compatible replacement for ReadAtLeastAsync(Memory, ...) - // https://learn.microsoft.com/dotnet/api/system.io.stream.readatleastasync - public async Task ReadAtLeastAsync( - byte[] buffer, - int minimumBytes, - bool throwOnEndOfStream = true, - CancellationToken cancellationToken = default - ) - { - var totalBytesRead = 0; - while (totalBytesRead < buffer.Length) - { - var bytesRead = await stream - .ReadAsync( - buffer, - totalBytesRead, - Math.Min(minimumBytes, buffer.Length - totalBytesRead), - cancellationToken - ) - .ConfigureAwait(false); - - if (bytesRead <= 0) - break; - - totalBytesRead += bytesRead; - } - - if (totalBytesRead < minimumBytes && throwOnEndOfStream) - throw new EndOfStreamException(); - - return totalBytesRead; - } - // https://learn.microsoft.com/dotnet/api/system.io.stream.readexactlyasync#system-io-stream-readexactlyasync(system-byte()-system-int32-system-int32-system-threading-cancellationtoken) public async Task ReadExactlyAsync( byte[] buffer, @@ -122,19 +60,8 @@ public async Task ReadExactlyAsync( totalBytesRead += bytesRead; } } - - // Signature-compatible replacement for ReadExactlyAsync(Memory, ...) - // https://learn.microsoft.com/dotnet/api/system.io.stream.readexactlyasync#system-io-stream-readexactlyasync(system-memory((system-byte))-system-threading-cancellationtoken) - public async Task ReadExactlyAsync( - byte[] buffer, - CancellationToken cancellationToken = default - ) => - await stream - .ReadExactlyAsync(buffer, 0, buffer.Length, cancellationToken) - .ConfigureAwait(false); #endif -#if FEATURE_MEMORY // https://learn.microsoft.com/dotnet/api/system.io.stream.readatleast public int ReadAtLeast(Span buffer, int minimumBytes, bool throwOnEndOfStream = true) { @@ -161,9 +88,8 @@ public void ReadExactly(Span buffer) stream.ReadExactly(bufferArray, 0, bufferArray.Length); bufferArray.CopyTo(buffer); } -#endif -#if FEATURE_TASK && FEATURE_MEMORY +#if FEATURE_TASK // https://learn.microsoft.com/dotnet/api/system.io.stream.readatleastasync public async Task ReadAtLeastAsync( Memory buffer, diff --git a/PolyShim/NetCore10/ArrayPool.cs b/PolyShim/NetCore10/ArrayPool.cs new file mode 100644 index 0000000..3f02218 --- /dev/null +++ b/PolyShim/NetCore10/ArrayPool.cs @@ -0,0 +1,35 @@ +#if !FEATURE_ARRAYPOOL +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System.Diagnostics.CodeAnalysis; + +namespace System.Buffers; + +// https://learn.microsoft.com/dotnet/api/system.buffers.arraypool-1 +[ExcludeFromCodeCoverage] +internal class ArrayPool +{ + public static ArrayPool Shared { get; } = new(); + + public T[] Rent(int minimumLength) + { + if (minimumLength < 0) + throw new ArgumentOutOfRangeException(nameof(minimumLength)); + + return new T[minimumLength]; + } + + public void Return(T[] array, bool clearArray = false) + { + if (array is null) + throw new ArgumentNullException(nameof(array)); + + if (clearArray) + Array.Clear(array, 0, array.Length); + } +} +#endif diff --git a/PolyShim/NetCore21/IMemoryOwner.cs b/PolyShim/NetCore21/IMemoryOwner.cs new file mode 100644 index 0000000..52b6f7d --- /dev/null +++ b/PolyShim/NetCore21/IMemoryOwner.cs @@ -0,0 +1,15 @@ +#if !FEATURE_MEMORY +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +namespace System.Buffers; + +// https://learn.microsoft.com/dotnet/api/system.buffers.imemoryowner-1 +internal interface IMemoryOwner : IDisposable +{ + Memory Memory { get; } +} +#endif diff --git a/PolyShim/NetCore21/Memory.cs b/PolyShim/NetCore21/Memory.cs new file mode 100644 index 0000000..029da50 --- /dev/null +++ b/PolyShim/NetCore21/Memory.cs @@ -0,0 +1,78 @@ +#if !FEATURE_MEMORY +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace System; + +// https://learn.microsoft.com/dotnet/api/system.memory-1 +[ExcludeFromCodeCoverage] +internal readonly struct Memory : IEquatable> +{ + private readonly T[]? _array; + private readonly int _offset; + + public Memory(T[]? array, int start, int length) + { + if (array is null) + return; + + if (start < 0 || start > array.Length) + throw new ArgumentOutOfRangeException(nameof(start)); + if (length < 0 || start + length > array.Length) + throw new ArgumentOutOfRangeException(nameof(length)); + + _array = array; + _offset = start; + Length = length; + } + + public Memory(T[]? array) + : this(array, 0, array?.Length ?? 0) { } + + public int Length { get; } + + public bool IsEmpty => Length == 0; + + public Span Span => new(_array, _offset, Length); + + public Memory Slice(int start, int length) + { + if (start > Length || length > Length - start) + throw new ArgumentOutOfRangeException(); + + return new Memory(_array, _offset + start, length); + } + + public Memory Slice(int start) => Slice(start, Length - start); + + public void CopyTo(Memory destination) => Span.CopyTo(destination.Span); + + public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); + + public T[] ToArray() => Span.ToArray(); + + public override bool Equals(object? obj) => obj is Memory memory && Equals(memory); + + public bool Equals(Memory other) => + _array == other._array && _offset == other._offset && Length == other.Length; + + public override int GetHashCode() => HashCode.Combine(_array, _offset, Length); + + public static Memory Empty => default; + + public static implicit operator Memory(T[] array) => new(array); + + public static implicit operator Memory(ArraySegment segment) => + new(segment.Array, segment.Offset, segment.Count); + + public static implicit operator ReadOnlyMemory(Memory memory) => + new(memory._array, memory._offset, memory.Length); +} +#endif diff --git a/PolyShim/NetCore21/MemoryExtensions.cs b/PolyShim/NetCore21/MemoryExtensions.cs new file mode 100644 index 0000000..460dc8f --- /dev/null +++ b/PolyShim/NetCore21/MemoryExtensions.cs @@ -0,0 +1,151 @@ +#if !FEATURE_MEMORY +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System.Diagnostics.CodeAnalysis; + +namespace System; + +// https://learn.microsoft.com/dotnet/api/system.memoryextensions +[ExcludeFromCodeCoverage] +internal static class MemoryExtensions +{ + extension(T[]? array) + { + public Span AsSpan(int start, int length) => new(array, start, length); + + public Span AsSpan(int start) => array.AsSpan(start, array?.Length ?? 0 - start); + + public Span AsSpan() => array.AsSpan(0); + + public Memory AsMemory(int start, int length) => new(array, start, length); + + public Memory AsMemory(int start) => array.AsMemory(start, array?.Length ?? 0 - start); + + public Memory AsMemory() => array.AsMemory(0); + + public void CopyTo(Span destination) => array.AsSpan().CopyTo(destination); + + public void CopyTo(Memory destination) => array.AsSpan().CopyTo(destination.Span); + } + + extension(ArraySegment? segment) + { + public Span AsSpan() => new(segment?.Array, segment?.Offset ?? 0, segment?.Count ?? 0); + } + + extension(string text) + { + public ReadOnlySpan AsSpan(int start, int length) => + new(text.ToCharArray(), start, length); + + public ReadOnlySpan AsSpan(int start) => text.AsSpan(start, text.Length - start); + + public ReadOnlySpan AsSpan() => text.AsSpan(0); + + public ReadOnlyMemory AsMemory(int start, int length) => + new(text.ToCharArray(), start, length); + + public ReadOnlyMemory AsMemory(int start) => + text.AsMemory(start, text.Length - start); + + public ReadOnlyMemory AsMemory() => text.AsMemory(0); + } + + extension(Span span) + where T : IEquatable + { + public bool Contains(T value) + { + foreach (var item in span) + { + if (item.Equals(value)) + return true; + } + + return false; + } + + public int IndexOf(T value) + { + for (var i = 0; i < span.Length; i++) + { + if (span[i].Equals(value)) + return i; + } + + return -1; + } + + public bool SequenceEqual(ReadOnlySpan other) + { + if (span.Length != other.Length) + return false; + + for (var i = 0; i < span.Length; i++) + { + if (!span[i].Equals(other[i])) + return false; + } + + return true; + } + + public void Reverse() + { + var i = 0; + var j = span.Length - 1; + + while (i < j) + { + (span[i], span[j]) = (span[j], span[i]); + i++; + j--; + } + } + } + + extension(ReadOnlySpan span) + where T : IEquatable + { + public bool Contains(T value) + { + foreach (var item in span) + { + if (item.Equals(value)) + return true; + } + + return false; + } + + public int IndexOf(T value) + { + for (var i = 0; i < span.Length; i++) + { + if (span[i].Equals(value)) + return i; + } + + return -1; + } + + public bool SequenceEqual(ReadOnlySpan other) + { + if (span.Length != other.Length) + return false; + + for (var i = 0; i < span.Length; i++) + { + if (!span[i].Equals(other[i])) + return false; + } + + return true; + } + } +} +#endif diff --git a/PolyShim/NetCore21/MemoryPool.cs b/PolyShim/NetCore21/MemoryPool.cs new file mode 100644 index 0000000..237eedd --- /dev/null +++ b/PolyShim/NetCore21/MemoryPool.cs @@ -0,0 +1,38 @@ +#if !FEATURE_MEMORY +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System.Diagnostics.CodeAnalysis; + +namespace System.Buffers; + +// https://learn.microsoft.com/dotnet/api/system.buffers.memorypool-1 +[ExcludeFromCodeCoverage] +internal partial class MemoryPool : IDisposable +{ + public static MemoryPool Shared { get; } = new(); + + public IMemoryOwner Rent(int minBufferSize = -1) + { + if (minBufferSize < -1) + throw new ArgumentOutOfRangeException(nameof(minBufferSize)); + + return new MemoryOwner(minBufferSize < 0 ? 0 : minBufferSize); + } + + public void Dispose() { } +} + +internal partial class MemoryPool +{ + private class MemoryOwner(int length) : IMemoryOwner + { + public Memory Memory { get; } = new(new T[length]); + + public void Dispose() { } + } +} +#endif diff --git a/PolyShim/NetCore21/Random.cs b/PolyShim/NetCore21/Random.cs index c943612..081a2bf 100644 --- a/PolyShim/NetCore21/Random.cs +++ b/PolyShim/NetCore21/Random.cs @@ -9,7 +9,6 @@ internal static partial class PolyfillExtensions { -#if FEATURE_MEMORY extension(Random random) { // https://learn.microsoft.com/dotnet/api/system.random.nextbytes#system-random-nextbytes(system-span((system-byte))) @@ -20,6 +19,5 @@ public void NextBytes(Span buffer) bufferArray.CopyTo(buffer); } } -#endif } #endif diff --git a/PolyShim/NetCore21/ReadOnlyMemory.cs b/PolyShim/NetCore21/ReadOnlyMemory.cs new file mode 100644 index 0000000..d4e9f8f --- /dev/null +++ b/PolyShim/NetCore21/ReadOnlyMemory.cs @@ -0,0 +1,75 @@ +#if !FEATURE_MEMORY +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace System; + +// https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 +[ExcludeFromCodeCoverage] +internal readonly struct ReadOnlyMemory : IEquatable> +{ + private readonly T[]? _array; + private readonly int _offset; + + public ReadOnlyMemory(T[]? array, int start, int length) + { + if (array is null) + return; + + if (start < 0 || start > array.Length) + throw new ArgumentOutOfRangeException(nameof(start)); + if (length < 0 || start + length > array.Length) + throw new ArgumentOutOfRangeException(nameof(length)); + + _array = array; + _offset = start; + Length = length; + } + + public ReadOnlyMemory(T[]? array) + : this(array, 0, array?.Length ?? 0) { } + + public int Length { get; } + + public bool IsEmpty => Length == 0; + + public ReadOnlySpan Span => new(_array, _offset, Length); + + public ReadOnlyMemory Slice(int start, int length) + { + if (start > Length || length > Length - start) + throw new ArgumentOutOfRangeException(); + + return new ReadOnlyMemory(_array, _offset + start, length); + } + + public ReadOnlyMemory Slice(int start) => Slice(start, Length - start); + + public void CopyTo(Memory destination) => Span.CopyTo(destination.Span); + + public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); + + public T[] ToArray() => Span.ToArray(); + + public override bool Equals(object? obj) => obj is ReadOnlyMemory memory && Equals(memory); + + public bool Equals(ReadOnlyMemory other) => + _array == other._array && _offset == other._offset && Length == other.Length; + + public override int GetHashCode() => HashCode.Combine(_array, _offset, Length); + + public static ReadOnlyMemory Empty => default; + + public static implicit operator ReadOnlyMemory(T[] array) => new(array); + + public static implicit operator ReadOnlyMemory(ArraySegment segment) => + new(segment.Array, segment.Offset, segment.Count); +} +#endif diff --git a/PolyShim/NetCore21/ReadOnlySpan.cs b/PolyShim/NetCore21/ReadOnlySpan.cs new file mode 100644 index 0000000..24125d7 --- /dev/null +++ b/PolyShim/NetCore21/ReadOnlySpan.cs @@ -0,0 +1,105 @@ +#if !FEATURE_MEMORY +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace System; + +// https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 +[ExcludeFromCodeCoverage] +internal readonly ref struct ReadOnlySpan +{ + private readonly T[]? _array; + private readonly int _offset; + + public ReadOnlySpan(T[]? array, int start, int length) + { + if (array is null) + return; + + if (start < 0 || start > array.Length) + throw new ArgumentOutOfRangeException(nameof(start)); + if (length < 0 || start + length > array.Length) + throw new ArgumentOutOfRangeException(nameof(length)); + + _array = array; + _offset = start; + Length = length; + } + + public ReadOnlySpan(T[]? array) + : this(array, 0, array?.Length ?? 0) { } + + public T this[int index] + { + get + { + if (_array is null || index < 0 || index >= Length) + throw new IndexOutOfRangeException(); + + return _array[_offset + index]; + } + } + + public int Length { get; } + + public bool IsEmpty => Length == 0; + + private Span Span => new(_array, _offset, Length); + + public ReadOnlySpan Slice(int start, int length) + { + if (start > Length || length > Length - start) + throw new ArgumentOutOfRangeException(); + + return new ReadOnlySpan(_array, _offset + start, length); + } + + public ReadOnlySpan Slice(int start) => Slice(start, Length - start); + + public void CopyTo(Span destination) => Span.CopyTo(destination); + + public bool TryCopyTo(Span destination) => Span.TryCopyTo(destination); + + public T[] ToArray() => Span.ToArray(); + + public static ReadOnlySpan Empty => default; + + public static implicit operator ReadOnlySpan(T[] array) => new(array); + + public static implicit operator ReadOnlySpan(ArraySegment segment) => + new(segment.Array, segment.Offset, segment.Count); + + public Enumerator GetEnumerator() => new(this); + + public ref struct Enumerator + { + private readonly ReadOnlySpan _span; + private int _index; + + internal Enumerator(ReadOnlySpan span) + { + _span = span; + _index = -1; + } + + public T Current => _span[_index]; + + public bool MoveNext() + { + var index = _index + 1; + if (index >= _span.Length) + return false; + + _index = index; + return true; + } + } +} +#endif diff --git a/PolyShim/NetCore21/Span.cs b/PolyShim/NetCore21/Span.cs new file mode 100644 index 0000000..5b59536 --- /dev/null +++ b/PolyShim/NetCore21/Span.cs @@ -0,0 +1,158 @@ +#if !FEATURE_MEMORY +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace System; + +// https://learn.microsoft.com/dotnet/api/system.span-1 +[ExcludeFromCodeCoverage] +internal readonly ref struct Span +{ + private readonly T[]? _array; + private readonly int _offset; + + public Span(T[]? array, int start, int length) + { + if (array is null) + return; + + if (start < 0 || start > array.Length) + throw new ArgumentOutOfRangeException(nameof(start)); + if (length < 0 || start + length > array.Length) + throw new ArgumentOutOfRangeException(nameof(length)); + + _array = array; + _offset = start; + Length = length; + } + + public Span(T[]? array) + : this(array, 0, array?.Length ?? 0) { } + +#if !NETFRAMEWORK || NET46_OR_GREATER + public unsafe Span(void* pointer, int length) + : this(new T[length]) + { + for (var i = 0; i < length; i++) + _array![i] = Unsafe.Read((byte*)pointer + i * Unsafe.SizeOf()); + } +#endif + + public ref T this[int index] + { + get + { + if (_array is null || index < 0 || index >= Length) + throw new IndexOutOfRangeException(); + + return ref _array[_offset + index]; + } + } + + public int Length { get; } + + public bool IsEmpty => Length == 0; + + public Span Slice(int start, int length) + { + if (start > Length || length > Length - start) + throw new ArgumentOutOfRangeException(); + + return new Span(_array, _offset + start, length); + } + + public Span Slice(int start) => Slice(start, Length - start); + + public void Clear() + { + if (_array is null) + return; + + Array.Clear(_array, _offset, Length); + } + + public void Fill(T value) + { + if (_array is null) + return; + + for (var i = 0; i < Length; i++) + _array[_offset + i] = value; + } + + public void CopyTo(Span destination) + { + if (Length > destination.Length) + throw new ArgumentException("Destination is too short.", nameof(destination)); + + if (_array is not null && destination._array is not null) + Array.Copy(_array, _offset, destination._array, destination._offset, Length); + } + + public bool TryCopyTo(Span destination) + { + if (Length > destination.Length) + return false; + + if (_array is not null && destination._array is not null) + Array.Copy(_array, _offset, destination._array, destination._offset, Length); + + return true; + } + + public T[] ToArray() + { + if (Length == 0 || _array is null) + return []; + + var result = new T[Length]; + Array.Copy(_array, _offset, result, 0, Length); + + return result; + } + + public static Span Empty => default; + + public static implicit operator Span(T[] array) => new(array); + + public static implicit operator Span(ArraySegment segment) => + new(segment.Array, segment.Offset, segment.Count); + + public static implicit operator ReadOnlySpan(Span span) => + new(span._array, span._offset, span.Length); + + public Enumerator GetEnumerator() => new(this); + + public ref struct Enumerator + { + private readonly Span _span; + private int _index; + + internal Enumerator(Span span) + { + _span = span; + _index = -1; + } + + public ref T Current => ref _span[_index]; + + public bool MoveNext() + { + var index = _index + 1; + if (index >= _span.Length) + return false; + + _index = index; + return true; + } + } +} +#endif diff --git a/PolyShim/NetCore21/Stream.cs b/PolyShim/NetCore21/Stream.cs index 696361a..bd2333e 100644 --- a/PolyShim/NetCore21/Stream.cs +++ b/PolyShim/NetCore21/Stream.cs @@ -14,43 +14,14 @@ internal static partial class PolyfillExtensions { extension(Stream stream) { - // Signature-compatible replacement for Read(Span) - // https://learn.microsoft.com/dotnet/api/system.io.stream.read#system-io-stream-read(system-span((system-byte))) - public int Read(byte[] buffer) => stream.Read(buffer, 0, buffer.Length); - - // Signature-compatible replacement for Write(ReadOnlySpan) - // https://learn.microsoft.com/dotnet/api/system.io.stream.write#system-io-stream-write(system-readonlyspan((system-byte))) - public void Write(byte[] buffer) => stream.Write(buffer, 0, buffer.Length); - #if FEATURE_TASK // https://learn.microsoft.com/dotnet/api/system.io.stream.copytoasync#system-io-stream-copytoasync(system-io-stream-system-threading-cancellationtoken) public async Task CopyToAsync( Stream destination, CancellationToken cancellationToken = default ) => await stream.CopyToAsync(destination, 81920, cancellationToken).ConfigureAwait(false); - - // Signature-compatible replacement for ReadAsync(Memory, ...) - // https://learn.microsoft.com/dotnet/api/system.io.stream.readasync#system-io-stream-readasync(system-memory((system-byte))-system-threading-cancellationtoken) - public async Task ReadAsync( - byte[] buffer, - CancellationToken cancellationToken = default - ) => - await stream - .ReadAsync(buffer, 0, buffer.Length, cancellationToken) - .ConfigureAwait(false); - - // Signature-compatible replacement for WriteAsync(ReadOnlyMemory, ...) - // https://learn.microsoft.com/dotnet/api/system.io.stream.writeasync#system-io-stream-writeasync(system-readonlymemory((system-byte))-system-threading-cancellationtoken) - public async Task WriteAsync( - byte[] buffer, - CancellationToken cancellationToken = default - ) => - await stream - .WriteAsync(buffer, 0, buffer.Length, cancellationToken) - .ConfigureAwait(false); #endif -#if FEATURE_MEMORY // https://learn.microsoft.com/dotnet/api/system.io.stream.read#system-io-stream-read(system-span((system-byte))) public int Read(Span buffer) { @@ -67,9 +38,8 @@ public void Write(ReadOnlySpan buffer) var bufferArray = buffer.ToArray(); stream.Write(bufferArray, 0, bufferArray.Length); } -#endif -#if FEATURE_TASK && FEATURE_MEMORY +#if FEATURE_TASK // https://learn.microsoft.com/dotnet/api/system.io.stream.readasync#system-io-stream-readasync(system-memory((system-byte))-system-threading-cancellationtoken) public async Task ReadAsync( Memory buffer, diff --git a/PolyShim/NetCore21/StreamReader.cs b/PolyShim/NetCore21/StreamReader.cs index 3c6bda8..dc169ff 100644 --- a/PolyShim/NetCore21/StreamReader.cs +++ b/PolyShim/NetCore21/StreamReader.cs @@ -14,24 +14,6 @@ internal static partial class PolyfillExtensions { extension(StreamReader reader) { - // Signature-compatible replacement for Read(Span) - // https://learn.microsoft.com/dotnet/api/system.io.streamreader.read#system-io-streamreader-read(system-span((system-char))) - public int Read(char[] buffer) => reader.Read(buffer, 0, buffer.Length); - -#if FEATURE_TASK - // Signature-compatible replacement for ReadAsync(Memory, ...) - // https://learn.microsoft.com/dotnet/api/system.io.streamreader.readasync#system-io-streamreader-readasync(system-memory((system-char))-system-threading-cancellationtoken) - public async Task ReadAsync( - char[] buffer, - CancellationToken cancellationToken = default - ) - { - cancellationToken.ThrowIfCancellationRequested(); - return await reader.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); - } -#endif - -#if FEATURE_MEMORY // https://learn.microsoft.com/dotnet/api/system.io.streamreader.read#system-io-streamreader-read(system-span((system-char))) public int Read(Span buffer) { @@ -41,9 +23,8 @@ public int Read(Span buffer) return result; } -#endif -#if FEATURE_TASK && FEATURE_MEMORY +#if FEATURE_TASK // https://learn.microsoft.com/dotnet/api/system.io.streamreader.readasync#system-io-streamreader-readasync(system-memory((system-char))-system-threading-cancellationtoken) public async Task ReadAsync( Memory buffer, diff --git a/PolyShim/NetCore21/StreamWriter.cs b/PolyShim/NetCore21/StreamWriter.cs index 260a353..d886adc 100644 --- a/PolyShim/NetCore21/StreamWriter.cs +++ b/PolyShim/NetCore21/StreamWriter.cs @@ -14,16 +14,14 @@ internal static partial class PolyfillExtensions { extension(StreamWriter writer) { -#if FEATURE_MEMORY // https://learn.microsoft.com/dotnet/api/system.io.streamwriter.write#system-io-streamwriter-write(system-readonlyspan((system-char))) public void Write(ReadOnlySpan buffer) { var bufferArray = buffer.ToArray(); writer.Write(bufferArray, 0, bufferArray.Length); } -#endif -#if FEATURE_TASK && FEATURE_MEMORY +#if FEATURE_TASK // https://learn.microsoft.com/dotnet/api/system.io.streamwriter.writeasync#system-io-streamwriter-writeasync(system-readonlymemory((system-char))-system-threading-cancellationtoken) public async Task WriteAsync( Memory buffer, diff --git a/PolyShim/PolyShim.props b/PolyShim/PolyShim.props index b1a38e4..0e78df2 100644 --- a/PolyShim/PolyShim.props +++ b/PolyShim/PolyShim.props @@ -1,5 +1,6 @@ latest + true diff --git a/PolyShim/PolyShim.targets b/PolyShim/PolyShim.targets index fb580b5..520fb8c 100644 --- a/PolyShim/PolyShim.targets +++ b/PolyShim/PolyShim.targets @@ -18,6 +18,27 @@ + false + true + true + true + true + $(DefineConstants);FEATURE_ARRAYPOOL + false .NET 7.0 - `ArgumentNullException` - [`void ThrowIfNull(object?, string?)`](https://learn.microsoft.com/dotnet/api/system.argumentnullexception.throwifnull#system-argumentnullexception-throwifnull(system-object-system-string)) .NET 6.0 +- `ArrayPool` + - [**[class]**](https://learn.microsoft.com/dotnet/api/system.buffers.arraypool-1) .NET Core 1.0 +- `ArraySegment` + - `Span AsSpan()` .NET Core 2.1 - `byte` - [`bool TryParse(string, IFormatProvider?, out byte)`](https://learn.microsoft.com/dotnet/api/system.byte.tryparse#system-byte-tryparse(system-string-system-iformatprovider-system-byte@)) .NET 7.0 - `CallerArgumentExpressionAttribute` @@ -157,6 +161,8 @@ ___ - [`T? Min(IComparer?)`](https://learn.microsoft.com/dotnet/api/system.linq.enumerable.min#system-linq-enumerable-min-1(system-collections-generic-ienumerable((-0))-system-collections-generic-icomparer((-0)))) .NET 6.0 - [`T? MinBy(Func, IComparer?)`](https://learn.microsoft.com/dotnet/api/system.linq.enumerable.minby#system-linq-enumerable-minby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-collections-generic-icomparer((-1)))) .NET 6.0 - [`T? MinBy(Func)`](https://learn.microsoft.com/dotnet/api/system.linq.enumerable.minby#system-linq-enumerable-minby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1)))) .NET 6.0 +- `IMemoryOwner` + - [**[interface]**](https://learn.microsoft.com/dotnet/api/system.buffers.imemoryowner-1) .NET Core 2.1 - `Index` - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.index) .NET Core 3.0 - `int` @@ -193,6 +199,12 @@ ___ - [**[class]**](https://learn.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.membernotnullattribute) .NET 5.0 - `MemberNotNullWhenAttribute` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.membernotnullwhenattribute) .NET 5.0 +- `Memory` + - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.memory-1) .NET Core 2.1 +- `MemoryExtensions` + - [**[class]**](https://learn.microsoft.com/dotnet/api/system.memoryextensions) .NET Core 2.1 +- `MemoryPool` + - [**[class]**](https://learn.microsoft.com/dotnet/api/system.buffers.memorypool-1) .NET Core 2.1 - `ModuleInitializerAttribute` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.moduleinitializerattribute) .NET 5.0 - `NotNullAttribute` @@ -247,13 +259,18 @@ ___ - [`long NextInt64(long)`](https://learn.microsoft.com/dotnet/api/system.random.nextint64#system-random-nextint64(system-int64)) .NET 6.0 - [`Random Shared`](https://learn.microsoft.com/dotnet/api/system.random.shared) .NET 6.0 - [`string GetHexString(int, bool)`](https://learn.microsoft.com/dotnet/api/system.random.gethexstring#system-random-gethexstring(system-int32-system-boolean)) .NET 10.0 - - [`string GetString(char[], int)`](https://learn.microsoft.com/dotnet/api/system.random.getstring) .NET 10.0 - [`string GetString(ReadOnlySpan, int)`](https://learn.microsoft.com/dotnet/api/system.random.getstring) .NET 10.0 - [`T[] GetItems(T[], int)`](https://learn.microsoft.com/dotnet/api/system.random.getitems#system-random-getitems-1(-0()-system-int32)) .NET 8.0 - [`void NextBytes(Span)`](https://learn.microsoft.com/dotnet/api/system.random.nextbytes#system-random-nextbytes(system-span((system-byte)))) .NET Core 2.1 - [`void Shuffle(T[])`](https://learn.microsoft.com/dotnet/api/system.random.shuffle#system-random-shuffle-1(-0())) .NET 8.0 - `Range` - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.range) .NET Core 3.0 +- `ReadOnlyMemory` + - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.readonlymemory-1) .NET Core 2.1 +- `ReadOnlySpan` + - `bool Contains(T)` .NET Core 2.1 + - `bool SequenceEqual(ReadOnlySpan)` .NET Core 2.1 + - `int IndexOf(T)` .NET Core 2.1 - `Regex` - [`int Count(string, string, RegexOptions, TimeSpan)`](https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.regex.count#system-text-regularexpressions-regex-count(system-string-system-string-system-text-regularexpressions-regexoptions-system-timespan)) .NET 7.0 - [`int Count(string, string, RegexOptions)`](https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.regex.count#system-text-regularexpressions-regex-count(system-string-system-string-system-text-regularexpressions-regexoptions)) .NET 7.0 @@ -281,32 +298,27 @@ ___ - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.skiplocalsinitattribute) .NET 5.0 - `SortedSet` - [`bool TryGetValue(T, out T?)`](https://learn.microsoft.com/dotnet/api/system.collections.generic.sortedset-1.trygetvalue) .NET Core 2.0 +- `Span` + - `bool Contains(T)` .NET Core 2.1 + - `bool SequenceEqual(ReadOnlySpan)` .NET Core 2.1 + - `int IndexOf(T)` .NET Core 2.1 + - `void Reverse()` .NET Core 2.1 - `Stream` - - [`int Read(byte[])`](https://learn.microsoft.com/dotnet/api/system.io.stream.read#system-io-stream-read(system-span((system-byte)))) .NET Core 2.1 - [`int Read(Span)`](https://learn.microsoft.com/dotnet/api/system.io.stream.read#system-io-stream-read(system-span((system-byte)))) .NET Core 2.1 - - [`int ReadAtLeast(byte[], int, bool)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readatleast) .NET 7.0 - [`int ReadAtLeast(Span, int, bool)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readatleast) .NET 7.0 - [`Task CopyToAsync(Stream, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.copytoasync#system-io-stream-copytoasync(system-io-stream-system-threading-cancellationtoken)) .NET Core 2.1 - - [`Task ReadExactlyAsync(byte[], CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readexactlyasync#system-io-stream-readexactlyasync(system-memory((system-byte))-system-threading-cancellationtoken)) .NET 7.0 - [`Task ReadExactlyAsync(byte[], int, int, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readexactlyasync#system-io-stream-readexactlyasync(system-byte()-system-int32-system-int32-system-threading-cancellationtoken)) .NET 7.0 - [`Task ReadExactlyAsync(Memory, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readexactlyasync#system-io-stream-readexactlyasync(system-memory((system-byte))-system-threading-cancellationtoken)) .NET 7.0 - - [`Task WriteAsync(byte[], CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.writeasync#system-io-stream-writeasync(system-readonlymemory((system-byte))-system-threading-cancellationtoken)) .NET Core 2.1 - [`Task WriteAsync(ReadOnlyMemory, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.writeasync#system-io-stream-writeasync(system-readonlymemory((system-byte))-system-threading-cancellationtoken)) .NET Core 2.1 - - [`Task ReadAsync(byte[], CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readasync#system-io-stream-readasync(system-memory((system-byte))-system-threading-cancellationtoken)) .NET Core 2.1 - [`Task ReadAsync(Memory, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readasync#system-io-stream-readasync(system-memory((system-byte))-system-threading-cancellationtoken)) .NET Core 2.1 - - [`Task ReadAtLeastAsync(byte[], int, bool, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readatleastasync) .NET 7.0 - [`Task ReadAtLeastAsync(Memory, int, bool, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readatleastasync) .NET 7.0 - [`void CopyTo(Stream, int)`](https://learn.microsoft.com/dotnet/api/system.io.stream.copyto#system-io-stream-copyto(system-io-stream-system-int32)) .NET Core 1.0 - [`void CopyTo(Stream)`](https://learn.microsoft.com/dotnet/api/system.io.stream.copyto#system-io-stream-copyto(system-io-stream)) .NET Core 1.0 - [`void ReadExactly(byte[], int, int)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readexactly#system-io-stream-readexactly(system-byte()-system-int32-system-int32)) .NET 7.0 - - [`void ReadExactly(byte[])`](https://learn.microsoft.com/dotnet/api/system.io.stream.readexactly#system-io-stream-readexactly(system-span((system-byte)))) .NET 7.0 - [`void ReadExactly(Span)`](https://learn.microsoft.com/dotnet/api/system.io.stream.readexactly#system-io-stream-readexactly(system-span((system-byte)))) .NET 7.0 - - [`void Write(byte[])`](https://learn.microsoft.com/dotnet/api/system.io.stream.write#system-io-stream-write(system-readonlyspan((system-byte)))) .NET Core 2.1 - [`void Write(ReadOnlySpan)`](https://learn.microsoft.com/dotnet/api/system.io.stream.write#system-io-stream-write(system-readonlyspan((system-byte)))) .NET Core 2.1 - `StreamReader` - - [`int Read(char[])`](https://learn.microsoft.com/dotnet/api/system.io.streamreader.read#system-io-streamreader-read(system-span((system-char)))) .NET Core 2.1 - [`int Read(Span)`](https://learn.microsoft.com/dotnet/api/system.io.streamreader.read#system-io-streamreader-read(system-span((system-char)))) .NET Core 2.1 - - [`Task ReadAsync(char[], CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.streamreader.readasync#system-io-streamreader-readasync(system-memory((system-char))-system-threading-cancellationtoken)) .NET Core 2.1 - [`Task ReadAsync(Memory, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.streamreader.readasync#system-io-streamreader-readasync(system-memory((system-char))-system-threading-cancellationtoken)) .NET Core 2.1 - `StreamWriter` - [`Task WriteAsync(Memory, CancellationToken)`](https://learn.microsoft.com/dotnet/api/system.io.streamwriter.writeasync#system-io-streamwriter-writeasync(system-readonlymemory((system-char))-system-threading-cancellationtoken)) .NET Core 2.1 @@ -319,6 +331,12 @@ ___ - [`bool IsNullOrWhiteSpace(string?)`](https://learn.microsoft.com/dotnet/api/system.string.isnullorwhitespace) .NET Core 1.0 - [`bool StartsWith(char)`](https://learn.microsoft.com/dotnet/api/system.string.startswith#system-string-startswith(system-char)) .NET Core 2.0 - [`int GetHashCode(StringComparison)`](https://learn.microsoft.com/dotnet/api/system.string.gethashcode#system-string-gethashcode(system-stringcomparison)) .NET Core 2.0 + - `ReadOnlyMemory AsMemory()` .NET Core 2.1 + - `ReadOnlyMemory AsMemory(int, int)` .NET Core 2.1 + - `ReadOnlyMemory AsMemory(int)` .NET Core 2.1 + - `ReadOnlySpan AsSpan()` .NET Core 2.1 + - `ReadOnlySpan AsSpan(int, int)` .NET Core 2.1 + - `ReadOnlySpan AsSpan(int)` .NET Core 2.1 - [`string Replace(string, string?, bool, CultureInfo?)`](https://learn.microsoft.com/dotnet/api/system.string.replace#system-string-replace(system-string-system-string-system-boolean-system-globalization-cultureinfo)) .NET Core 2.0 - [`string Replace(string, string?, StringComparison)`](https://learn.microsoft.com/dotnet/api/system.string.replace#system-string-replace(system-string-system-string-system-stringcomparison)) .NET Core 2.0 - [`string ReplaceLineEndings()`](https://learn.microsoft.com/dotnet/api/system.string.replacelineendings#system-string-replacelineendings) .NET 6.0 @@ -337,6 +355,14 @@ ___ - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.versioning.supportedosplatformguardattribute) .NET 6.0 - `SystemException` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.systemexception) .NET Core 2.0 +- `T[]` + - `Memory AsMemory()` .NET Core 2.1 + - `Memory AsMemory(int, int)` .NET Core 2.1 + - `Memory AsMemory(int)` .NET Core 2.1 + - `Span AsSpan()` .NET Core 2.1 + - `Span AsSpan(int, int)` .NET Core 2.1 + - `void CopyTo(Memory)` .NET Core 2.1 + - `void CopyTo(Span)` .NET Core 2.1 - `TargetFrameworkAttribute` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.versioning.targetframeworkattribute) .NET Core 1.0 - `TargetPlatformAttribute` diff --git a/Readme.md b/Readme.md index 48a2928..035cbe5 100644 --- a/Readme.md +++ b/Readme.md @@ -132,13 +132,13 @@ Currently, **PolyShim** has integration with the following packages: - [`System.Threading.Tasks`](https://nuget.org/packages/System.Threading.Tasks) — `Task`, `Task`, etc. - [`System.Threading.Tasks.Extensions`](https://nuget.org/packages/System.Threading.Tasks.Extensions) — `ValueTask`, `ValueTask`, etc. - [`System.ValueTuple`](https://nuget.org/packages/System.ValueTuple) — `ValueTuple<...>`, etc. -- [`Microsoft.Bcl.Async`](https://nuget.org/packages/Microsoft.Bcl.Async) — `Task`, `Task`, etc (wider support than the `System.*` variant). +- [`Microsoft.Bcl.Async`](https://nuget.org/packages/Microsoft.Bcl.Async) — `Task`, `Task`, etc. (wider support than the `System.*` variant). - [`Microsoft.Bcl.AsyncInterfaces`](https://nuget.org/packages/Microsoft.Bcl.AsyncInterfaces) — `IAsyncEnumerable`, `IAsyncDisposable`, etc. - [`Microsoft.Bcl.HashCode`](https://nuget.org/packages/Microsoft.Bcl.HashCode) — `HashCode`, etc. - [`Microsoft.Bcl.Memory`](https://nuget.org/packages/Microsoft.Bcl.Memory) — `Index`, `Range`, etc. -- [`Microsoft.Net.Http`](https://nuget.org/packages/Microsoft.Net.Http) — `HttpClient`, `HttpContent`, etc (wider support than the `System.*` variant). +- [`Microsoft.Net.Http`](https://nuget.org/packages/Microsoft.Net.Http) — `HttpClient`, `HttpContent`, etc. (wider support than the `System.*` variant). -For example, adding a reference to the `System.Memory` package will enable **PolyShim**'s polyfills that offer `Span` and `Memory`-based method overloads on various built-in types, such as `Stream`: +For example, adding a reference to the `Microsoft.Bcl.AsyncInterfaces` package will enable **PolyShim**'s polyfills that work with `IAsyncEnumerable`, such as `Parallel.ForEachAsync(...)`: ```xml @@ -149,21 +149,32 @@ For example, adding a reference to the `System.Memory` package will enable **Pol - + ``` ```csharp -using System.Buffers; -using System.IO; - -using var stream = /* ... */; -using var buffer = MemoryPool.Shared.Rent(512); - -// System.Memory is referenced, so this polyfill is enabled -var bytesRead = await stream.ReadAsync(buffer.Memory); +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +async IAsyncEnumerable GenerateNumbersAsync() +{ + for (int i = 1; i <= 5; i++) + { + await Task.Delay(100); + yield return i; + } +} + +// Microsoft.Bcl.AsyncInterfaces is referenced, so this polyfill is enabled +await Parallel.ForEachAsync(GenerateNumbersAsync(), async (number, cancellationToken) => +{ + await Task.Delay(Random.Shared.Next(0, 1000), cancellationToken); + Console.WriteLine(number); +}); ``` Conversely, adding a reference to the `System.ValueTuple` package will disable **PolyShim**'s own version of `ValueTuple<...>` and related types. @@ -178,7 +189,7 @@ You can leverage this to prioritize the official implementation wherever possibl - +