diff --git a/CHANGELOG.md b/CHANGELOG.md
index 74e741719..155da0f47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,32 @@
Release Notes
====
+# 10-13-2024
+DotNext 5.14.0
+* Added helpers to `DelegateHelpers` class to convert delegates with synchronous signature to their asynchronous counterparts
+* Added support of async enumerator to `SingletonList`
+* Fixed exception propagation in `DynamicTaskAwaitable`
+* Added support of [ConfigureAwaitOptions](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.configureawaitoptions) to `DynamicTaskAwaitable`
+
+DotNext.Metaprogramming 5.14.0
+* Updated dependencies
+
+DotNext.Unsafe 5.14.0
+* Updated dependencies
+
+DotNext.Threading 5.14.0
+* Updated dependencies
+
+DotNext.IO 5.14.0
+* Updated dependencies
+
+DotNext.Net.Cluster 5.14.0
+* Fixed graceful shutdown of Raft TCP listener
+* Updated vulnerable dependencies
+
+DotNext.AspNetCore.Cluster 5.14.0
+* Updated vulnerable dependencies
+
# 08-30-2024
DotNext 5.13.0
* Improved interoperability of `DotNext.Runtime.ValueReference` and `DotNext.Runtime.ReadOnlyValueReference` with .NEXT ecosystem
diff --git a/README.md b/README.md
index f536b72a5..6df156b15 100644
--- a/README.md
+++ b/README.md
@@ -44,35 +44,32 @@ All these things are implemented in 100% managed code on top of existing .NET AP
* [NuGet Packages](https://www.nuget.org/profiles/rvsakno)
# What's new
-Release Date: 08-30-2024
+Release Date: 10-13-2024
-DotNext 5.13.0
-* Improved interoperability of `DotNext.Runtime.ValueReference` and `DotNext.Runtime.ReadOnlyValueReference` with .NEXT ecosystem
-* Fixed [249](https://github.com/dotnet/dotNext/issues/249)
-* Improved codegen quality for ad-hoc enumerator types
+DotNext 5.14.0
+* Added helpers to `DelegateHelpers` class to convert delegates with synchronous signature to their asynchronous counterparts
+* Added support of async enumerator to `SingletonList`
+* Fixed exception propagation in `DynamicTaskAwaitable`
+* Added support of [ConfigureAwaitOptions](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.configureawaitoptions) to `DynamicTaskAwaitable`
-DotNext.Metaprogramming 5.13.0
+DotNext.Metaprogramming 5.14.0
* Updated dependencies
-DotNext.Unsafe 5.13.0
+DotNext.Unsafe 5.14.0
* Updated dependencies
-DotNext.Threading 5.13.0
-* Redesigned `AsyncEventHub` to improve overall performance and reduce memory allocation
-* Improved codegen quality for ad-hoc enumerator types
-
-DotNext.IO 5.13.0
-* Improved codegen quality for ad-hoc enumerator types
-
-DotNext.Net.Cluster 5.13.0
+DotNext.Threading 5.14.0
* Updated dependencies
-DotNext.AspNetCore.Cluster 5.13.0
+DotNext.IO 5.14.0
* Updated dependencies
-DotNext.MaintenanceServices 0.4.0
-* Added [gc refresh-mem-limit](https://learn.microsoft.com/en-us/dotnet/api/system.gc.refreshmemorylimit) maintenance command
-* Updated dependencies
+DotNext.Net.Cluster 5.14.0
+* Fixed graceful shutdown of Raft TCP listener
+* Updated vulnerable dependencies
+
+DotNext.AspNetCore.Cluster 5.14.0
+* Updated vulnerable dependencies
Changelog for previous versions located [here](./CHANGELOG.md).
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index f0b9c57e2..33f98518b 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -14,20 +14,20 @@
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
diff --git a/src/DotNext.IO/DotNext.IO.csproj b/src/DotNext.IO/DotNext.IO.csproj
index 97d13b014..9a1bb78e5 100644
--- a/src/DotNext.IO/DotNext.IO.csproj
+++ b/src/DotNext.IO/DotNext.IO.csproj
@@ -11,7 +11,7 @@
.NET Foundation and Contributors
.NEXT Family of Libraries
- 5.13.0
+ 5.14.0
DotNext.IO
MIT
diff --git a/src/DotNext.IO/IO/FileWriter.Binary.cs b/src/DotNext.IO/IO/FileWriter.Binary.cs
index 69ed18d58..9d9cd599d 100644
--- a/src/DotNext.IO/IO/FileWriter.Binary.cs
+++ b/src/DotNext.IO/IO/FileWriter.Binary.cs
@@ -47,7 +47,7 @@ private ValueTask WriteAsync(T arg, SpanAction writer, int length, C
[AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))]
private async ValueTask WriteBufferedAsync(T arg, SpanAction writer, int length, CancellationToken token)
{
- await FlushCoreAsync(token).ConfigureAwait(false);
+ await FlushAsync(token).ConfigureAwait(false);
writer(BufferSpan, arg);
Debug.Assert(bufferOffset is 0);
@@ -143,7 +143,7 @@ private int WriteLength(int length, LengthFormat lengthFormat)
public async ValueTask WriteAsync(ReadOnlyMemory input, LengthFormat lengthFormat, CancellationToken token = default)
{
if (FreeCapacity < SevenBitEncodedInt.MaxSize)
- await FlushCoreAsync(token).ConfigureAwait(false);
+ await FlushAsync(token).ConfigureAwait(false);
WriteLength(input.Length, lengthFormat);
await WriteAsync(input, token).ConfigureAwait(false);
@@ -165,7 +165,7 @@ public async ValueTask EncodeAsync(ReadOnlyMemory chars, EncodingCon
if (lengthFormat.HasValue)
{
if (FreeCapacity < SevenBitEncodedInt.MaxSize)
- await FlushCoreAsync(token).ConfigureAwait(false);
+ await FlushAsync(token).ConfigureAwait(false);
result = WriteLength(context.Encoding.GetByteCount(chars.Span), lengthFormat.GetValueOrDefault());
}
@@ -182,7 +182,7 @@ public async ValueTask EncodeAsync(ReadOnlyMemory chars, EncodingCon
for (int charsUsed, bytesUsed; !chars.IsEmpty; chars = chars.Slice(charsUsed), result += bytesUsed)
{
if (FreeCapacity < maxByteCount)
- await FlushCoreAsync(token).ConfigureAwait(false);
+ await FlushAsync(token).ConfigureAwait(false);
Convert(encoder, chars.Span, BufferSpan, maxByteCount, chars.Length, out charsUsed, out bytesUsed);
Produce(bytesUsed);
@@ -285,7 +285,7 @@ private bool TryFormat(T value, LengthFormat? lengthFormat, ReadOnlySpan FormatSlowAsync(T value, LengthFormat? lengthFormat, string? format, IFormatProvider? provider, CancellationToken token)
where T : notnull, IUtf8SpanFormattable
{
- await FlushCoreAsync(token).ConfigureAwait(false);
+ await FlushAsync(token).ConfigureAwait(false);
if (!TryFormat(value, lengthFormat, format, provider, out var bytesWritten))
{
const int maxBufferSize = int.MaxValue / 2;
diff --git a/src/DotNext.IO/IO/FileWriter.cs b/src/DotNext.IO/IO/FileWriter.cs
index 4de20e4e4..ef1ee3c54 100644
--- a/src/DotNext.IO/IO/FileWriter.cs
+++ b/src/DotNext.IO/IO/FileWriter.cs
@@ -91,9 +91,9 @@ public FileWriter(FileStream destination, int bufferSize = 4096, MemoryAllocator
public int MaxBufferSize => buffer.Length;
///
- /// Marks the specified number of bytes in the buffer as consumed.
+ /// Marks the specified number of bytes in the buffer as produced.
///
- /// The number of consumed bytes.
+ /// The number of produced bytes.
/// is larger than the length of .
public void Produce(int bytes)
{
@@ -140,10 +140,10 @@ public long FilePosition
///
public long WritePosition => fileOffset + bufferOffset;
- private ValueTask FlushCoreAsync(CancellationToken token)
+ private ValueTask FlushAsync(CancellationToken token)
=> Submit(RandomAccess.WriteAsync(handle, WrittenBuffer, fileOffset, token), writeCallback);
- private void FlushCore()
+ private void Flush()
{
RandomAccess.Write(handle, WrittenBuffer.Span, fileOffset);
fileOffset += bufferOffset;
@@ -165,7 +165,7 @@ public ValueTask WriteAsync(CancellationToken token = default)
if (token.IsCancellationRequested)
return ValueTask.FromCanceled(token);
- return HasBufferedData ? FlushCoreAsync(token) : ValueTask.CompletedTask;
+ return HasBufferedData ? FlushAsync(token) : ValueTask.CompletedTask;
}
///
@@ -191,7 +191,7 @@ public void Write()
ObjectDisposedException.ThrowIf(IsDisposed, this);
if (HasBufferedData)
- FlushCore();
+ Flush();
}
///
diff --git a/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj b/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj
index 52be6e9bc..c295a09d7 100644
--- a/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj
+++ b/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj
@@ -8,7 +8,7 @@
true
false
nullablePublicOnly
- 5.13.0
+ 5.14.0
.NET Foundation
.NEXT Family of Libraries
diff --git a/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs b/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs
index add7d2e59..8dac05303 100644
--- a/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs
+++ b/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs
@@ -1,4 +1,6 @@
+using System.Numerics;
using System.Text;
+using DotNext.Buffers.Binary;
using static System.Globalization.CultureInfo;
namespace DotNext.Buffers;
@@ -313,4 +315,24 @@ public static void Rendering()
writer.Format(CompositeFormat.Parse("{0}, {1}!"), ["Hello", "world"]);
Equal("Hello, world!", writer.ToString());
}
+
+ [Fact]
+ public static void WriteBlittable()
+ {
+ var writer = new BufferWriterSlim(stackalloc byte[16]);
+ writer.Write>(new() { Value = 42 });
+
+ var reader = new SpanReader(writer.WrittenSpan);
+ Equal(42, reader.Read>().Value);
+ }
+
+ [Fact]
+ public static void ReadWriteBigInteger()
+ {
+ var expected = (BigInteger)100500;
+ var writer = new BufferWriterSlim(stackalloc byte[16]);
+ Equal(3, writer.Write(expected));
+
+ Equal(expected, new BigInteger(writer.WrittenSpan));
+ }
}
\ No newline at end of file
diff --git a/src/DotNext.Tests/Buffers/BufferWriterTests.cs b/src/DotNext.Tests/Buffers/BufferWriterTests.cs
index 4f4e3d592..83163677e 100644
--- a/src/DotNext.Tests/Buffers/BufferWriterTests.cs
+++ b/src/DotNext.Tests/Buffers/BufferWriterTests.cs
@@ -226,11 +226,12 @@ public static void WriteInterpolatedStringToBufferWriter(int x, int y)
[InlineData(int.MaxValue, int.MinValue)]
public static async Task WriteInterpolatedStringToBufferWriterAsync(int x, int y)
{
- var xt = Task.FromResult(x);
- var yt = Task.FromResult(y);
+ var xt = Task.FromResult(x);
+ var yt = Task.FromResult(y);
using var buffer = new PoolingArrayBufferWriter();
- buffer.Interpolate($"{await xt,4:X} = {await yt,-3:X}");
+ var actualCount = buffer.Interpolate($"{await xt,4:X} = {await yt,-3:X}");
+ Equal(buffer.WrittenCount, actualCount);
Equal($"{x,4:X} = {y,-3:X}", buffer.ToString());
}
@@ -334,4 +335,46 @@ public static void Rendering()
writer.Format(CompositeFormat.Parse("{0}, {1}!"), ["Hello", "world"]);
Equal("Hello, world!", writer.WrittenSpan.ToString());
}
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(16)]
+ [InlineData(128)]
+ [InlineData(124)]
+ public static void WriteStringBuilder(int stringLength)
+ {
+ var str = Random.Shared.NextString("abcdefghijklmnopqrstuvwxyz", stringLength);
+
+ var builder = new StringBuilder();
+ for (var i = 0; i < 3; i++)
+ {
+ builder.Append(str);
+ }
+
+ var writer = new BufferWriterSlim();
+
+ writer.Write(builder);
+ Equal(builder.ToString(), writer.WrittenSpan);
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(16)]
+ [InlineData(128)]
+ [InlineData(124)]
+ public static void WriteStringBuilder2(int stringLength)
+ {
+ var str = Random.Shared.NextString("abcdefghijklmnopqrstuvwxyz", stringLength);
+
+ var builder = new StringBuilder();
+ for (var i = 0; i < 3; i++)
+ {
+ builder.Append(str);
+ }
+
+ var writer = new ArrayBufferWriter();
+
+ writer.Write(builder);
+ Equal(builder.ToString(), writer.WrittenSpan);
+ }
}
\ No newline at end of file
diff --git a/src/DotNext.Tests/Buffers/ChunkSequenceTests.cs b/src/DotNext.Tests/Buffers/ChunkSequenceTests.cs
index 630736785..f3818d040 100644
--- a/src/DotNext.Tests/Buffers/ChunkSequenceTests.cs
+++ b/src/DotNext.Tests/Buffers/ChunkSequenceTests.cs
@@ -1,4 +1,5 @@
using System.Buffers;
+using System.Text;
namespace DotNext.Buffers;
@@ -93,4 +94,22 @@ public static void CopyFromSequence()
Equal(10, writtenCount);
Equal(sequence.Slice(0, 10).ToArray(), dest.ToArray());
}
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(16)]
+ [InlineData(128)]
+ [InlineData(124)]
+ public static void StringBuilderToSequence(int stringLength)
+ {
+ var str = Random.Shared.NextString("abcdefghijklmnopqrstuvwxyz", stringLength);
+
+ var builder = new StringBuilder();
+ for (var i = 0; i < 3; i++)
+ {
+ builder.Append(str);
+ }
+
+ Equal(builder.ToString(), builder.ToReadOnlySequence().ToString());
+ }
}
\ No newline at end of file
diff --git a/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs b/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs
index c4ee61987..797104c59 100644
--- a/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs
+++ b/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs
@@ -378,4 +378,26 @@ public static void Rendering()
True(writer.TryFormat(CompositeFormat.Parse("{0}, {1}!"), ["Hello", "world"]));
Equal("Hello, world!", writer.WrittenSpan.ToString());
}
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(16)]
+ [InlineData(128)]
+ [InlineData(124)]
+ public static void WriteStringBuilder(int stringLength)
+ {
+ var str = Random.Shared.NextString("abcdefghijklmnopqrstuvwxyz", stringLength);
+
+ var builder = new StringBuilder();
+ for (var i = 0; i < 3; i++)
+ {
+ builder.Append(str);
+ }
+
+ var chars = new char[builder.Length];
+ var writer = new SpanWriter(chars);
+
+ writer.Write(builder);
+ Equal(builder.ToString(), writer.WrittenSpan);
+ }
}
\ No newline at end of file
diff --git a/src/DotNext.Tests/Collections/Generic/AsyncEnumerableTests.cs b/src/DotNext.Tests/Collections/Generic/AsyncEnumerableTests.cs
index 2ebb718aa..6ed4ad29f 100644
--- a/src/DotNext.Tests/Collections/Generic/AsyncEnumerableTests.cs
+++ b/src/DotNext.Tests/Collections/Generic/AsyncEnumerableTests.cs
@@ -97,4 +97,11 @@ public static async Task SkipNullsTestAsync()
True(Array.Exists(array, "a".Equals));
True(Array.Exists(array, "b".Equals));
}
+
+ [Fact]
+ public static void Singleton()
+ {
+ var enumerable = AsyncEnumerable.Singleton(42);
+ Equal(42, Single(enumerable));
+ }
}
\ No newline at end of file
diff --git a/src/DotNext.Tests/DelegateHelpersTests.cs b/src/DotNext.Tests/DelegateHelpersTests.cs
index 8c6ba50df..14ced299d 100644
--- a/src/DotNext.Tests/DelegateHelpersTests.cs
+++ b/src/DotNext.Tests/DelegateHelpersTests.cs
@@ -521,6 +521,50 @@ public static void ToAsync2()
func = new Action(static _ => throw new Exception()).ToAsync();
True(func.Invoke(42, new(canceled: false)).IsFaulted);
}
+
+ [Fact]
+ public static void ToAsync3()
+ {
+ var func = new Action(static (_, _) => { }).ToAsync();
+ True(func.Invoke(42, 42, new(canceled: false)).IsCompletedSuccessfully);
+ True(func.Invoke(42, 42, new(canceled: true)).IsCanceled);
+
+ func = new Action(static (_, _) => throw new Exception()).ToAsync();
+ True(func.Invoke(42, 42, new(canceled: false)).IsFaulted);
+ }
+
+ [Fact]
+ public static async Task ToAsync4()
+ {
+ var func = new Func(static (x, y) => x + y).ToAsync();
+ Equal(84, await func.Invoke(42, 42, new(canceled: false)));
+ True(func.Invoke(42, 42, new(canceled: true)).IsCanceled);
+
+ func = new Func(static (_, _) => throw new Exception()).ToAsync();
+ await ThrowsAsync(func.Invoke(42, 42, new(canceled: false)).AsTask);
+ }
+
+ [Fact]
+ public static async Task ToAsync5()
+ {
+ var func = Func.Identity().ToAsync();
+ Equal(42, await func.Invoke(42, new(canceled: false)));
+ True(func.Invoke(42, new(canceled: true)).IsCanceled);
+
+ func = new Func(static _ => throw new Exception()).ToAsync();
+ await ThrowsAsync(func.Invoke(42, new(canceled: false)).AsTask);
+ }
+
+ [Fact]
+ public static async Task ToAsync6()
+ {
+ var func = Func.Constant(42).ToAsync();
+ Equal(42, await func.Invoke(new(canceled: false)));
+ True(func.Invoke(new(canceled: true)).IsCanceled);
+
+ func = new Func(static () => throw new Exception()).ToAsync();
+ await ThrowsAsync(func.Invoke(new(canceled: false)).AsTask);
+ }
[Fact]
public static void HideReturnValue1()
diff --git a/src/DotNext.Tests/IO/FileReaderTests.cs b/src/DotNext.Tests/IO/FileReaderTests.cs
index 6b294a433..582fa06d6 100644
--- a/src/DotNext.Tests/IO/FileReaderTests.cs
+++ b/src/DotNext.Tests/IO/FileReaderTests.cs
@@ -148,10 +148,12 @@ public static void ReadLargeData()
public static async Task ReadSequentially()
{
var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
- using var handle = File.OpenHandle(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.Asynchronous);
- using var reader = new FileReader(handle, bufferSize: 32);
+ await using var fs = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.Asynchronous);
+ using var reader = new FileReader(fs, bufferSize: 32);
var bytes = RandomBytes(1024);
- await RandomAccess.WriteAsync(handle, bytes, 0L);
+
+ await fs.WriteAsync(bytes);
+ await fs.FlushAsync();
using var ms = new MemoryStream(1024);
await foreach (var chunk in reader)
diff --git a/src/DotNext.Tests/IO/FileWriterTests.cs b/src/DotNext.Tests/IO/FileWriterTests.cs
index bfd7a086e..3d00484e7 100644
--- a/src/DotNext.Tests/IO/FileWriterTests.cs
+++ b/src/DotNext.Tests/IO/FileWriterTests.cs
@@ -112,8 +112,8 @@ public static void WriteWithOverflow()
public static void WriteUsingBufferWriter()
{
var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
- using var handle = File.OpenHandle(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.None);
- using var writer = new FileWriter(handle, bufferSize: 64);
+ using var fs = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.None);
+ using var writer = new FileWriter(fs, bufferSize: 64);
False(writer.HasBufferedData);
Equal(0L, writer.FilePosition);
@@ -126,7 +126,7 @@ public static void WriteUsingBufferWriter()
Equal(expected.Length, writer.FilePosition);
var actual = new byte[expected.Length];
- RandomAccess.Read(handle, actual, 0L);
+ fs.ReadExactly(actual);
Equal(expected, actual);
}
diff --git a/src/DotNext.Tests/IO/SequenceBinaryReaderTests.cs b/src/DotNext.Tests/IO/SequenceBinaryReaderTests.cs
index 3b9df3de5..ddbdc5a9f 100644
--- a/src/DotNext.Tests/IO/SequenceBinaryReaderTests.cs
+++ b/src/DotNext.Tests/IO/SequenceBinaryReaderTests.cs
@@ -1,5 +1,6 @@
using System.Buffers;
using System.IO.Pipelines;
+using System.Numerics;
using System.Text;
namespace DotNext.IO;
@@ -174,4 +175,16 @@ public static void TryRead2()
True(reader.IsEmpty);
False(reader.TryRead(4, out actual));
}
+
+ [Fact]
+ public static void ReadWriteBigInteger()
+ {
+ var expected = (BigInteger)100500;
+ var writer = new ArrayBufferWriter();
+
+ Equal(3, writer.Write(expected));
+
+ var reader = IAsyncBinaryReader.Create(writer.WrittenMemory);
+ Equal(expected, new BigInteger(reader.ReadToEnd().FirstSpan));
+ }
}
\ No newline at end of file
diff --git a/src/DotNext.Tests/Threading/Tasks/ConversionTests.cs b/src/DotNext.Tests/Threading/Tasks/ConversionTests.cs
index 170ab9394..037c18904 100644
--- a/src/DotNext.Tests/Threading/Tasks/ConversionTests.cs
+++ b/src/DotNext.Tests/Threading/Tasks/ConversionTests.cs
@@ -27,7 +27,7 @@ public static async Task DynamicTask()
Equal("Hello", result);
//check for caching
result = await Task.CompletedTask.AsDynamic();
- Equal(Missing.Value, result);
+ Same(Missing.Value, result);
result = await Task.FromResult("Hello2").AsDynamic();
Equal("Hello2", result);
await ThrowsAnyAsync(async () => await Task.FromCanceled(new CancellationToken(true)).AsDynamic());
@@ -47,4 +47,20 @@ public static async Task SuspendException()
await Task.FromException(new Exception()).SuspendException();
await ValueTask.FromException(new Exception()).SuspendException();
}
+
+ [Fact]
+ public static async Task SuspendException2()
+ {
+ var t = Task.FromException(new Exception());
+ var result = await t.AsDynamic().ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ContinueOnCapturedContext);
+ Same(result, Missing.Value);
+ }
+
+ [Fact]
+ public static async Task SuspendExceptionParametrized()
+ {
+ await Task.FromException(new Exception()).SuspendException(42, (_, i) => i is 42);
+ await ValueTask.FromException(new Exception()).SuspendException(42, (_, i) => i is 42);
+ await ThrowsAsync(async () => await Task.FromException(new Exception()).SuspendException(43, (_, i) => i is 42));
+ }
}
\ No newline at end of file
diff --git a/src/DotNext.Threading/DotNext.Threading.csproj b/src/DotNext.Threading/DotNext.Threading.csproj
index 43bb9173f..97e8fa52f 100644
--- a/src/DotNext.Threading/DotNext.Threading.csproj
+++ b/src/DotNext.Threading/DotNext.Threading.csproj
@@ -7,7 +7,7 @@
true
true
nullablePublicOnly
- 5.13.0
+ 5.14.0
.NET Foundation and Contributors
.NEXT Family of Libraries
diff --git a/src/DotNext.Threading/Threading/Tasks/TaskQueue.cs b/src/DotNext.Threading/Threading/Tasks/TaskQueue.cs
index 18e92f1ff..3c1aa4f7e 100644
--- a/src/DotNext.Threading/Threading/Tasks/TaskQueue.cs
+++ b/src/DotNext.Threading/Threading/Tasks/TaskQueue.cs
@@ -178,7 +178,7 @@ public async ValueTask EnqueueAsync(T task, CancellationToken token = default)
}
}
- private T? TryPeekOrDequeue(out int head, out Task enqueueTask, out bool completed)
+ private T? TryPeekOrDequeue(out int head, out Task enqueueTask)
{
T? result;
lock (array)
@@ -187,7 +187,7 @@ public async ValueTask EnqueueAsync(T task, CancellationToken token = default)
{
result = this[head = this.head];
enqueueTask = Task.CompletedTask;
- if (completed = result is { IsCompleted: true })
+ if (result is { IsCompleted: true })
{
MoveNext(ref head);
ChangeCount(increment: false);
@@ -197,7 +197,6 @@ public async ValueTask EnqueueAsync(T task, CancellationToken token = default)
{
head = default;
result = null;
- completed = default;
signal ??= new();
enqueueTask = signal.Task;
}
@@ -258,17 +257,17 @@ public bool TryDequeue([NotNullWhen(true)] out T? task)
/// The operation has been canceled.
public async ValueTask DequeueAsync(CancellationToken token = default)
{
- for (var filter = token.CanBeCanceled ? null : Predicate.Constant(true);;)
+ for (;;)
{
- if (TryPeekOrDequeue(out var expectedHead, out var enqueueTask, out var completed) is not { } task)
+ if (TryPeekOrDequeue(out var expectedHead, out var enqueueTask) is not { } task)
{
await enqueueTask.WaitAsync(token).ConfigureAwait(false);
continue;
}
- if (!completed)
+ if (!task.IsCompleted)
{
- await task.WaitAsync(token).SuspendException(filter ??= token.SuspendAllExceptCancellation).ConfigureAwait(false);
+ await task.WaitAsync(token).SuspendException(token, SuspendAllExceptCancellation).ConfigureAwait(false);
if (!TryDequeue(expectedHead, task))
continue;
@@ -286,12 +285,12 @@ public async ValueTask DequeueAsync(CancellationToken token = default)
/// The operation has been canceled.
public async ValueTask TryDequeueAsync(CancellationToken token = default)
{
- for (var filter = token.CanBeCanceled ? null : Predicate.Constant(true);;)
+ for (;;)
{
T? task;
- if ((task = TryPeekOrDequeue(out var expectedHead, out _, out var completed)) is not null && !completed)
+ if ((task = TryPeekOrDequeue(out var expectedHead, out _)) is not null && !task.IsCompleted)
{
- await task.WaitAsync(token).SuspendException(filter ??= token.SuspendAllExceptCancellation).ConfigureAwait(false);
+ await task.WaitAsync(token).SuspendException(token, SuspendAllExceptCancellation).ConfigureAwait(false);
if (!TryDequeue(expectedHead, task))
continue;
@@ -311,12 +310,11 @@ public async ValueTask DequeueAsync(CancellationToken token = default)
/// The enumerator over completed tasks.
public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken token)
{
- for (var filter = token.CanBeCanceled ? null : Predicate.Constant(true);
- TryPeekOrDequeue(out var expectedHead, out _, out var completed) is { } task;)
+ while (TryPeekOrDequeue(out var expectedHead, out _) is { } task)
{
- if (!completed)
+ if (!task.IsCompleted)
{
- await task.WaitAsync(token).SuspendException(filter ??= token.SuspendAllExceptCancellation).ConfigureAwait(false);
+ await task.WaitAsync(token).SuspendException(token, SuspendAllExceptCancellation).ConfigureAwait(false);
if (!TryDequeue(expectedHead, task))
continue;
}
@@ -325,6 +323,9 @@ public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken token)
}
}
+ private static bool SuspendAllExceptCancellation(Exception e, CancellationToken token)
+ => e is not OperationCanceledException canceledEx || token != canceledEx.CancellationToken;
+
///
/// Clears the queue.
///
@@ -343,10 +344,4 @@ public void Clear()
void IResettable.Reset() => Clear();
private sealed class Signal() : TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
-}
-
-file static class CancellationTokenExtensions
-{
- internal static bool SuspendAllExceptCancellation(this object token, Exception e)
- => e is not OperationCanceledException canceledEx || !canceledEx.CancellationToken.Equals(token);
}
\ No newline at end of file
diff --git a/src/DotNext.Unsafe/DotNext.Unsafe.csproj b/src/DotNext.Unsafe/DotNext.Unsafe.csproj
index 1a7f9db28..2cbe4dd42 100644
--- a/src/DotNext.Unsafe/DotNext.Unsafe.csproj
+++ b/src/DotNext.Unsafe/DotNext.Unsafe.csproj
@@ -7,7 +7,7 @@
enable
true
true
- 5.13.0
+ 5.14.0
nullablePublicOnly
.NET Foundation and Contributors
diff --git a/src/DotNext/Buffers/ByteBuffer.cs b/src/DotNext/Buffers/ByteBuffer.cs
index b0e8584b1..71abe2692 100644
--- a/src/DotNext/Buffers/ByteBuffer.cs
+++ b/src/DotNext/Buffers/ByteBuffer.cs
@@ -76,8 +76,8 @@ public static int WriteBigEndian(this IBufferWriter writer, T value)
///
/// The buffer writer.
/// The value to be written as a sequence of bytes.
- /// to use unsigned encoding; otherwise, .
- /// to write the bytes in a big-endian byte order; otherwise, .
+ /// to write the bytes in a big-endian byte order; otherwise, .
+ /// to use unsigned encoding; otherwise, .
/// The number of bytes written.
/// has not enough space to place .
public static int Write(this IBufferWriter writer, in BigInteger value, bool isBigEndian = false, bool isUnsigned = false)
diff --git a/src/DotNext/Buffers/Memory.ReadOnlySequence.cs b/src/DotNext/Buffers/Memory.ReadOnlySequence.cs
index 79ff9e2b2..1d6ba732c 100644
--- a/src/DotNext/Buffers/Memory.ReadOnlySequence.cs
+++ b/src/DotNext/Buffers/Memory.ReadOnlySequence.cs
@@ -1,6 +1,7 @@
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
namespace DotNext.Buffers;
@@ -94,10 +95,9 @@ public static ReadOnlySequence ToReadOnlySequence(this IEnumerable.Empty;
- if (ReferenceEquals(head, tail))
- return new(head.Memory);
-
- return Chunk.CreateSequence(head, tail);
+ return ReferenceEquals(head, tail)
+ ? new(head.Memory)
+ : Chunk.CreateSequence(head, tail);
static void ToReadOnlySequence(ReadOnlySpan strings, ref Chunk? head, ref Chunk? tail)
{
@@ -118,6 +118,31 @@ static void ToReadOnlySequenceSlow(IEnumerable strings, ref Chunk
}
}
+ ///
+ /// Gets a sequence of characters written to the builder.
+ ///
+ /// A string builder.
+ /// A sequence of characters written to the builder.
+ public static ReadOnlySequence ToReadOnlySequence(this StringBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ Chunk? head = null, tail = null;
+
+ foreach (var chunk in builder.GetChunks())
+ {
+ if (chunk.IsEmpty is false)
+ Chunk.AddChunk(chunk, ref head, ref tail);
+ }
+
+ if (head is null || tail is null)
+ return ReadOnlySequence.Empty;
+
+ return ReferenceEquals(head, tail)
+ ? new(head.Memory)
+ : Chunk.CreateSequence(head, tail);
+ }
+
///
/// Converts two memory blocks to data type.
///
diff --git a/src/DotNext/Buffers/MemoryOwner.cs b/src/DotNext/Buffers/MemoryOwner.cs
index d45b32776..9601ed8de 100644
--- a/src/DotNext/Buffers/MemoryOwner.cs
+++ b/src/DotNext/Buffers/MemoryOwner.cs
@@ -1,5 +1,6 @@
using System.Buffers;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -38,7 +39,7 @@ internal MemoryOwner(ArrayPool? pool, T[] array, int length)
this.length = length;
}
- internal MemoryOwner(ArrayPool pool, int length, bool exactSize)
+ internal MemoryOwner(ArrayPool pool, int length, [ConstantExpected] bool exactSize)
{
Debug.Assert(pool is not null);
diff --git a/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs b/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs
index 6b3f868c0..17e2b0501 100644
--- a/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs
+++ b/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs
@@ -10,15 +10,10 @@ namespace DotNext.Buffers;
file sealed class ReadOnlySequencePartitioner : OrderablePartitioner
{
- private sealed class SegmentProvider : IEnumerable>
+ private sealed class SegmentProvider(in ReadOnlySequence sequence) : IEnumerable>
{
private long runningIndex;
- private ReadOnlySequence.Enumerator enumerator;
-
- internal SegmentProvider(ReadOnlySequence sequence)
- {
- enumerator = sequence.GetEnumerator();
- }
+ private ReadOnlySequence.Enumerator enumerator = sequence.GetEnumerator();
[MethodImpl(MethodImplOptions.Synchronized)]
private ReadOnlyMemory NextSegment(out long startIndex)
diff --git a/src/DotNext/Buffers/Text/Base64Decoder.cs b/src/DotNext/Buffers/Text/Base64Decoder.cs
index 87bab6508..efa9467b7 100644
--- a/src/DotNext/Buffers/Text/Base64Decoder.cs
+++ b/src/DotNext/Buffers/Text/Base64Decoder.cs
@@ -74,7 +74,7 @@ private readonly ReadOnlySpan BufferedBytes
{
Debug.Assert((uint)reservedBufferSize <= sizeof(ulong));
- return MemoryMarshal.CreateReadOnlySpan(in ChangeType(in reservedBuffer), reservedBufferSize);
+ return MemoryMarshal.CreateReadOnlySpan(in InToRef(in reservedBuffer), reservedBufferSize);
}
}
}
\ No newline at end of file
diff --git a/src/DotNext/Collections/Generic/AsyncEnumerable.cs b/src/DotNext/Collections/Generic/AsyncEnumerable.cs
index 5eb5bcdf2..50fead5c8 100644
--- a/src/DotNext/Collections/Generic/AsyncEnumerable.cs
+++ b/src/DotNext/Collections/Generic/AsyncEnumerable.cs
@@ -219,4 +219,13 @@ public static IAsyncEnumerable Throw(Exception e)
return new ThrowingEnumerator(e);
}
+
+ ///
+ /// Constructs read-only sequence with a single item in it.
+ ///
+ /// An item to be placed into list.
+ /// Type of list items.
+ /// Read-only list containing single item.
+ public static IAsyncEnumerable Singleton(T item)
+ => new Specialized.SingletonList { Item = item };
}
\ No newline at end of file
diff --git a/src/DotNext/Collections/Specialized/SingletonList.cs b/src/DotNext/Collections/Specialized/SingletonList.cs
index 53b9613fa..4e98514b6 100644
--- a/src/DotNext/Collections/Specialized/SingletonList.cs
+++ b/src/DotNext/Collections/Specialized/SingletonList.cs
@@ -12,7 +12,7 @@ namespace DotNext.Collections.Specialized;
///
/// The type of the element in the list.
[StructLayout(LayoutKind.Auto)]
-public struct SingletonList : IReadOnlyList, IList, ITuple, IReadOnlySet
+public struct SingletonList : IReadOnlyList, IList, ITuple, IReadOnlySet, IAsyncEnumerable
{
///
/// Represents an enumerator over the collection containing a single element.
@@ -145,6 +145,10 @@ readonly IEnumerator IEnumerable.GetEnumerator()
readonly IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator().ToClassicEnumerator();
+ ///
+ readonly IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token)
+ => GetEnumerator().ToAsyncEnumerator(token);
+
///
/// Converts a value to the read-only list.
///
diff --git a/src/DotNext/DelegateHelpers.cs b/src/DotNext/DelegateHelpers.cs
index 657137d58..aa65a3b24 100644
--- a/src/DotNext/DelegateHelpers.cs
+++ b/src/DotNext/DelegateHelpers.cs
@@ -126,6 +126,43 @@ private static ValueTask Invoke(this Action action, T arg, CancellationTok
return task;
}
+ ///
+ /// Converts function to async delegate.
+ ///
+ /// Synchronous function.
+ /// The type of the argument to be passed to the action.
+ /// The type of the return value.
+ /// The asynchronous function that wraps .
+ /// is .
+ public static Func> ToAsync(this Func action)
+ {
+ ArgumentNullException.ThrowIfNull(action);
+
+ return action.Invoke;
+ }
+
+ private static ValueTask Invoke(this Func action, T arg, CancellationToken token)
+ {
+ ValueTask task;
+ if (token.IsCancellationRequested)
+ {
+ task = ValueTask.FromCanceled(token);
+ }
+ else
+ {
+ try
+ {
+ task = new(action.Invoke(arg));
+ }
+ catch (Exception e)
+ {
+ task = ValueTask.FromException(e);
+ }
+ }
+
+ return task;
+ }
+
///
/// Converts action to async delegate.
///
@@ -161,6 +198,118 @@ private static ValueTask Invoke(this Action action, CancellationToken token)
return task;
}
+
+ ///
+ /// Converts function to async delegate.
+ ///
+ /// The type of the return value.
+ /// Synchronous function.
+ /// The asynchronous function that wraps .
+ /// is .
+ public static Func> ToAsync(this Func func)
+ {
+ ArgumentNullException.ThrowIfNull(func);
+
+ return func.Invoke;
+ }
+
+ private static ValueTask Invoke(this Func func, CancellationToken token)
+ {
+ ValueTask task;
+ if (token.IsCancellationRequested)
+ {
+ task = ValueTask.FromCanceled(token);
+ }
+ else
+ {
+ try
+ {
+ task = new(func.Invoke());
+ }
+ catch (Exception e)
+ {
+ task = ValueTask.FromException(e);
+ }
+ }
+
+ return task;
+ }
+
+ ///
+ /// Converts action to async delegate.
+ ///
+ /// The type of the first argument to be passed to the action.
+ /// The type of the second argument to be passed to the action.
+ /// Synchronous action.
+ /// The asynchronous function that wraps .
+ /// is .
+ public static Func ToAsync(this Action action)
+ {
+ ArgumentNullException.ThrowIfNull(action);
+
+ return action.Invoke;
+ }
+
+ private static ValueTask Invoke(this Action action, T1 arg1, T2 arg2, CancellationToken token)
+ {
+ ValueTask task;
+ if (token.IsCancellationRequested)
+ {
+ task = ValueTask.FromCanceled(token);
+ }
+ else
+ {
+ task = ValueTask.CompletedTask;
+ try
+ {
+ action.Invoke(arg1, arg2);
+ }
+ catch (Exception e)
+ {
+ task = ValueTask.FromException(e);
+ }
+ }
+
+ return task;
+ }
+
+ ///
+ /// Converts function to async delegate.
+ ///
+ /// The type of the first argument to be passed to the action.
+ /// The type of the second argument to be passed to the action.
+ /// The type of the return value.
+ /// Synchronous action.
+ /// The asynchronous function that wraps .
+ /// is .
+ public static Func> ToAsync(this Func func)
+ {
+ ArgumentNullException.ThrowIfNull(func);
+
+ return func.Invoke;
+ }
+
+ private static ValueTask Invoke(this Func func, T1 arg1, T2 arg2, CancellationToken token)
+ {
+ ValueTask task;
+ if (token.IsCancellationRequested)
+ {
+ task = ValueTask.FromCanceled(token);
+ }
+ else
+ {
+ try
+ {
+ task = new(func.Invoke(arg1, arg2));
+ }
+ catch (Exception e)
+ {
+ task = ValueTask.FromException(e);
+ }
+ }
+
+ return task;
+ }
///
/// Creates a delegate that hides the return value.
diff --git a/src/DotNext/DotNext.csproj b/src/DotNext/DotNext.csproj
index 2e4e5b6de..e46855216 100644
--- a/src/DotNext/DotNext.csproj
+++ b/src/DotNext/DotNext.csproj
@@ -11,7 +11,7 @@
.NET Foundation and Contributors
.NEXT Family of Libraries
- 5.13.0
+ 5.14.0
DotNext
MIT
diff --git a/src/DotNext/Dynamic/TaskResultBinder.cs b/src/DotNext/Dynamic/TaskResultBinder.cs
index 10fa2fa41..23b08fc00 100644
--- a/src/DotNext/Dynamic/TaskResultBinder.cs
+++ b/src/DotNext/Dynamic/TaskResultBinder.cs
@@ -11,9 +11,6 @@ namespace DotNext.Dynamic;
[RequiresDynamicCode("DLR is required to resolve underlying task type at runtime")]
internal sealed class TaskResultBinder : CallSiteBinder
{
- private const string ResultPropertyName = nameof(Task.Result);
- private const BindingFlags ResultPropertyFlags = BindingFlags.Public | BindingFlags.Instance;
-
private static Expression BindProperty(PropertyInfo resultProperty, Expression target, out Expression restrictions)
{
Debug.Assert(resultProperty.DeclaringType is not null);
@@ -27,7 +24,10 @@ private static Expression BindProperty(PropertyInfo resultProperty, Expression t
private static Expression Bind(object targetValue, Expression target, LabelTarget returnLabel)
{
- PropertyInfo? property = targetValue.GetType().GetProperty(ResultPropertyName, ResultPropertyFlags);
+ const string resultPropertyName = nameof(Task.Result);
+ const BindingFlags resultPropertyFlags = BindingFlags.Public | BindingFlags.Instance;
+
+ var property = targetValue.GetType().GetProperty(resultPropertyName, resultPropertyFlags);
Debug.Assert(property is not null);
target = BindProperty(property, target, out var restrictions);
diff --git a/src/DotNext/Func.cs b/src/DotNext/Func.cs
index 7b8515e03..3dddebb5f 100644
--- a/src/DotNext/Func.cs
+++ b/src/DotNext/Func.cs
@@ -94,15 +94,15 @@ public static Func Constant(T obj)
// slow path - allocates a new delegate
return obj is null
- ? Default!
+ ? Default!
: typeof(T).IsValueType
- ? new BoxedConstant() { Value = obj }.GetValue
- : Unsafe.As(ref obj).UnboxRefType;
-
- static T? Default() => default;
+ ? new BoxedConstant(obj).GetValue
+ : Unsafe.As(ref obj).ReinterpretRefType;
}
+
+ internal static T? Default() => default;
- private static T UnboxRefType(this object obj)
+ private static T ReinterpretRefType(this object obj)
=> Unsafe.As