Skip to content

Commit b3f7df2

Browse files
committed
Release 5.26.1
1 parent fc1ecf2 commit b3f7df2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1060
-426
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
11
Release Notes
22
====
33

4+
# 11-03-2025
5+
<a href="https://www.nuget.org/packages/dotnext/5.26.1">DotNext 5.26.1</a>
6+
* Lock upgrade logic provided by `ReaderWriterSpinLock` is adjusted according to [275](https://github.com/dotnet/dotNext/issues/275)
7+
8+
<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.26.1">DotNext.Metaprogramming 5.26.1</a>
9+
* Updated dependencies
10+
11+
<a href="https://www.nuget.org/packages/dotnext.unsafe/5.26.1">DotNext.Unsafe 5.26.1</a>
12+
* Updated dependencies
13+
14+
<a href="https://www.nuget.org/packages/dotnext.threading/5.26.1">DotNext.Threading 5.26.1</a>
15+
* Lock upgrade logic provided by `AsyncReaderWriterLock` is adjusted according to [275](https://github.com/dotnet/dotNext/issues/275)
16+
* Improved accuracy of `CancellationTokenMultiplexer.Scope.IsTimedOut` property
17+
18+
<a href="https://www.nuget.org/packages/dotnext.io/5.26.1">DotNext.IO 5.26.1</a>
19+
* Added auxiliary `MemorySegmentStream` wrapper over [Memory&lt;byte&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.memory-1) type in the form of the writable stream
20+
* Updated dependencies
21+
22+
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.26.1">DotNext.Net.Cluster 5.26.1</a>
23+
* Fixed [276](https://github.com/dotnet/dotNext/issues/276)
24+
25+
<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.26.1">DotNext.AspNetCore.Cluster 5.26.1</a>
26+
* Fixed [276](https://github.com/dotnet/dotNext/issues/276)
27+
428
# 10-22-2025
529
<a href="https://www.nuget.org/packages/dotnext/5.26.0">DotNext 5.26.0</a>
630
* Introduced `DotNext.IO.ModernStream` class that derives from [Stream](https://learn.microsoft.com/en-us/dotnet/api/system.io.stream) and implements many of the methods introduced since .NET Framework 1.1 by default in a modern way, requiring only minimal subset of core methods to be implemented by the derived class

README.md

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,33 +44,30 @@ All these things are implemented in 100% managed code on top of existing .NET AP
4444
* [NuGet Packages](https://www.nuget.org/profiles/rvsakno)
4545

4646
# What's new
47-
Release Date: 10-22-2025
47+
Release Date: 11-04-2025
4848

49-
<a href="https://www.nuget.org/packages/dotnext/5.26.0">DotNext 5.26.0</a>
50-
* Introduced `DotNext.IO.ModernStream` class that derives from [Stream](https://learn.microsoft.com/en-us/dotnet/api/system.io.stream) and implements many of the methods introduced since .NET Framework 1.1 by default in a modern way, requiring only minimal subset of core methods to be implemented by the derived class
51-
* Removed async state machine allocations for ad-hoc streams returned by `StreamSource` factory methods
49+
<a href="https://www.nuget.org/packages/dotnext/5.26.1">DotNext 5.26.1</a>
50+
* Lock upgrade logic provided by `ReaderWriterSpinLock` is adjusted according to [275](https://github.com/dotnet/dotNext/issues/275)
5251

53-
<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.26.0">DotNext.Metaprogramming 5.26.0</a>
52+
<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.26.1">DotNext.Metaprogramming 5.26.1</a>
5453
* Updated dependencies
5554

56-
<a href="https://www.nuget.org/packages/dotnext.unsafe/5.26.0">DotNext.Unsafe 5.26.0</a>
55+
<a href="https://www.nuget.org/packages/dotnext.unsafe/5.26.1">DotNext.Unsafe 5.26.1</a>
5756
* Updated dependencies
5857

59-
<a href="https://www.nuget.org/packages/dotnext.threading/5.26.0">DotNext.Threading 5.26.0</a>
60-
* Improved support of timeouts in `CancellationTokenMultiplexer`. The scope object now has explicit property to detect whether the multiplexed token source is cancelled due to timeout
58+
<a href="https://www.nuget.org/packages/dotnext.threading/5.26.1">DotNext.Threading 5.26.1</a>
59+
* Lock upgrade logic provided by `AsyncReaderWriterLock` is adjusted according to [275](https://github.com/dotnet/dotNext/issues/275)
60+
* Improved accuracy of `CancellationTokenMultiplexer.Scope.IsTimedOut` property
6161

62-
<a href="https://www.nuget.org/packages/dotnext.io/5.26.0">DotNext.IO 5.26.0</a>
63-
* Migration to `ModernStream` class
64-
65-
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.26.0">DotNext.Net.Cluster 5.26.0</a>
66-
* Migration of Raft implementation to `CancellationTokenMultiplexer`
62+
<a href="https://www.nuget.org/packages/dotnext.io/5.26.1">DotNext.IO 5.26.1</a>
63+
* Added auxiliary `MemorySegmentStream` wrapper over [Memory&lt;byte&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.memory-1) type in the form of the writable stream
64+
* Updated dependencies
6765

68-
<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.26.0">DotNext.AspNetCore.Cluster 5.26.0</a>
69-
* Migration of Raft implementation to `CancellationTokenMultiplexer`
66+
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.26.1">DotNext.Net.Cluster 5.26.1</a>
67+
* Fixed [276](https://github.com/dotnet/dotNext/issues/276)
7068

71-
<a href="https://www.nuget.org/packages/dotnext.maintenanceservices/0.5.0">DotNext.MaintenanceServices 0.7.0</a>
72-
* Migration to `System.CommandLine` release candidate
73-
* Added custom parser configuration (`ParserConfiguration` class) that can be registered in DI
69+
<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.26.1">DotNext.AspNetCore.Cluster 5.26.1</a>
70+
* Fixed [276](https://github.com/dotnet/dotNext/issues/276)
7471

7572
Changelog for previous versions located [here](./CHANGELOG.md).
7673

src/DotNext.IO/DotNext.IO.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<Authors>.NET Foundation and Contributors</Authors>
1212
<Company />
1313
<Product>.NEXT Family of Libraries</Product>
14-
<VersionPrefix>5.26.0</VersionPrefix>
14+
<VersionPrefix>5.26.1</VersionPrefix>
1515
<VersionSuffix></VersionSuffix>
1616
<AssemblyName>DotNext.IO</AssemblyName>
1717
<PackageLicenseExpression>MIT</PackageLicenseExpression>

src/DotNext.IO/ExceptionMessages.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ internal static string DirectoryNotFound(string path)
2727
internal static string ReadBufferNotEmpty => (string)Resources.Get();
2828

2929
internal static string WriteBufferNotEmpty => (string)Resources.Get();
30+
31+
internal static string StreamOverflow => (string)Resources.Get();
3032
}

src/DotNext.IO/ExceptionMessages.restext

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ DirectoryNotFound=Directory {0} doesn't exist
55
WriterInReadMode=The writer is in read-only mode. Dispose active memory manager obtained from writer
66
FileHandleClosed=The file handle is closed
77
ReadBufferNotEmpty=The internal buffer has unconsumed data to read
8-
WriteBufferNotEmpty=The internal buffer has data to flush
8+
WriteBufferNotEmpty=The internal buffer has data to flush
9+
StreamOverflow=The internal buffer is full
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using System.Diagnostics;
2+
3+
namespace DotNext.IO;
4+
5+
/// <summary>
6+
/// Represents a stream wrapper over the memory block.
7+
/// </summary>
8+
/// <param name="data">The mutable memory block.</param>
9+
public sealed class MemorySegmentStream(Memory<byte> data) : ModernStream
10+
{
11+
private int position, length = data.Length;
12+
13+
/// <summary>
14+
/// Gets the consumed part of the data.
15+
/// </summary>
16+
public Span<byte> ConsumedSpan => data.Span[..position];
17+
18+
/// <summary>
19+
/// Gets the remaining part of the data.
20+
/// </summary>
21+
public Span<byte> RemainingSpan => data.Span[position..length];
22+
23+
/// <summary>
24+
/// Gets or sets a value indicating that <see cref="Write"/> and <see cref="WriteAsync"/> must throw
25+
/// <see cref="IOException"/> if the caller is trying to write past to the end of the underlying buffer.
26+
/// </summary>
27+
/// <remarks>
28+
/// The default value is <see langword="false"/>.
29+
/// </remarks>
30+
public bool SkipOnOverflow { get; init; }
31+
32+
/// <inheritdoc/>
33+
public override void Flush()
34+
{
35+
}
36+
37+
/// <inheritdoc/>
38+
public override Task FlushAsync(CancellationToken token)
39+
=> token.IsCancellationRequested ? Task.FromCanceled(token) : Task.CompletedTask;
40+
41+
[Conditional("DEBUG")]
42+
private void AssertState()
43+
{
44+
Debug.Assert(position <= length);
45+
Debug.Assert(length <= data.Length);
46+
}
47+
48+
/// <inheritdoc/>
49+
public override int Read(Span<byte> buffer)
50+
{
51+
AssertState();
52+
53+
RemainingSpan.CopyTo(buffer, out var count);
54+
position += count;
55+
return count;
56+
}
57+
58+
/// <inheritdoc/>
59+
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken token = default)
60+
{
61+
ValueTask<int> task;
62+
if (token.IsCancellationRequested)
63+
{
64+
task = ValueTask.FromCanceled<int>(token);
65+
}
66+
else
67+
{
68+
try
69+
{
70+
task = new(Read(buffer.Span));
71+
}
72+
catch (Exception e)
73+
{
74+
task = ValueTask.FromException<int>(e);
75+
}
76+
}
77+
78+
return task;
79+
}
80+
81+
/// <inheritdoc/>
82+
public override long Seek(long offset, SeekOrigin origin)
83+
{
84+
AssertState();
85+
86+
var newPosition = origin switch
87+
{
88+
SeekOrigin.Begin => offset,
89+
SeekOrigin.Current => Position + offset,
90+
SeekOrigin.End => Length + offset,
91+
_ => throw new ArgumentOutOfRangeException(nameof(origin))
92+
};
93+
94+
if (newPosition < 0L)
95+
throw new IOException();
96+
97+
ArgumentOutOfRangeException.ThrowIfGreaterThan(newPosition, Length, nameof(offset));
98+
99+
position = (int)newPosition;
100+
return newPosition;
101+
}
102+
103+
private void SetLength(int newLength)
104+
{
105+
if (newLength > data.Length)
106+
throw new IOException(ExceptionMessages.StreamOverflow);
107+
108+
position = Math.Min(position, length = newLength);
109+
}
110+
111+
/// <inheritdoc/>
112+
public override void SetLength(long value)
113+
{
114+
ArgumentOutOfRangeException.ThrowIfGreaterThan((ulong)value, (uint)int.MaxValue, nameof(value));
115+
116+
AssertState();
117+
SetLength((int)value);
118+
}
119+
120+
/// <inheritdoc/>
121+
public override void Write(ReadOnlySpan<byte> buffer)
122+
{
123+
AssertState();
124+
125+
var remaining = RemainingSpan;
126+
if (remaining.Length >= buffer.Length)
127+
{
128+
// nothing to do
129+
}
130+
else if (SkipOnOverflow)
131+
{
132+
buffer = buffer.Slice(0, remaining.Length);
133+
}
134+
else
135+
{
136+
throw new IOException(ExceptionMessages.StreamOverflow);
137+
}
138+
139+
buffer.CopyTo(remaining);
140+
position += buffer.Length;
141+
}
142+
143+
/// <inheritdoc/>
144+
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken token = default)
145+
{
146+
ValueTask task;
147+
if (token.IsCancellationRequested)
148+
{
149+
task = ValueTask.FromCanceled(token);
150+
}
151+
else
152+
{
153+
task = ValueTask.CompletedTask;
154+
try
155+
{
156+
Write(buffer.Span);
157+
}
158+
catch (Exception e)
159+
{
160+
task = ValueTask.FromException(e);
161+
}
162+
}
163+
164+
return task;
165+
}
166+
167+
/// <inheritdoc/>
168+
public override bool CanRead => true;
169+
170+
/// <inheritdoc/>
171+
public override bool CanSeek => true;
172+
173+
/// <inheritdoc/>
174+
public override bool CanWrite => true;
175+
176+
/// <inheritdoc/>
177+
public override long Length => length;
178+
179+
/// <inheritdoc/>
180+
public override long Position
181+
{
182+
get => position;
183+
set
184+
{
185+
ArgumentOutOfRangeException.ThrowIfGreaterThan((ulong)value, (uint)length, nameof(value));
186+
187+
position = (int)value;
188+
AssertState();
189+
}
190+
}
191+
}

src/DotNext.IO/IO/SparseStream.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,16 +149,23 @@ public override async ValueTask DisposeAsync()
149149

150150
GC.SuppressFinalize(this);
151151
}
152+
153+
public static SparseStream Create<T>(T streams, bool leaveOpen)
154+
where T : struct, ITuple
155+
=> new SparseStream<T>(streams, leaveOpen);
156+
157+
public static SparseStream Create(Stream stream, ReadOnlySpan<Stream> streams, bool leaveOpen)
158+
=> new UnboundedSparseStream(stream, streams, leaveOpen);
152159
}
153160

154-
internal sealed class SparseStream<T>(T streams, bool leaveOpen) : SparseStream(leaveOpen)
161+
file sealed class SparseStream<T>(T streams, bool leaveOpen) : SparseStream(leaveOpen)
155162
where T : struct, ITuple
156163
{
157164
protected override ReadOnlySpan<Stream> Streams
158165
=> MemoryMarshal.CreateReadOnlySpan(in Unsafe.As<T, Stream>(ref Unsafe.AsRef(in streams)), streams.Length);
159166
}
160167

161-
internal sealed class UnboundedSparseStream : SparseStream
168+
file sealed class UnboundedSparseStream : SparseStream
162169
{
163170
private MemoryOwner<Stream> streams;
164171

src/DotNext.IO/IO/StreamExtensions.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@ private static Stream Combine(Stream stream, ReadOnlySpan<Stream> others, bool l
2525
=> others switch
2626
{
2727
[] => stream,
28-
[var s] => new SparseStream<(Stream, Stream)>((stream, s), leaveOpen),
29-
[var s1, var s2] => new SparseStream<(Stream, Stream, Stream)>((stream, s1, s2), leaveOpen),
30-
[var s1, var s2, var s3] => new SparseStream<(Stream, Stream, Stream, Stream)>((stream, s1, s2, s3), leaveOpen),
31-
[var s1, var s2, var s3, var s4] => new SparseStream<(Stream, Stream, Stream, Stream, Stream)>((stream, s1, s2, s3, s4), leaveOpen),
32-
[var s1, var s2, var s3, var s4, var s5] => new SparseStream<(Stream, Stream, Stream, Stream, Stream, Stream)>((stream, s1, s2, s3, s4,
28+
[var s] => SparseStream.Create<(Stream, Stream)>((stream, s), leaveOpen),
29+
[var s1, var s2] => SparseStream.Create<(Stream, Stream, Stream)>((stream, s1, s2), leaveOpen),
30+
[var s1, var s2, var s3] => SparseStream.Create<(Stream, Stream, Stream, Stream)>((stream, s1, s2, s3), leaveOpen),
31+
[var s1, var s2, var s3, var s4] => SparseStream.Create<(Stream, Stream, Stream, Stream, Stream)>((stream, s1, s2, s3, s4), leaveOpen),
32+
[var s1, var s2, var s3, var s4, var s5] => SparseStream.Create<(Stream, Stream, Stream, Stream, Stream, Stream)>((stream, s1, s2, s3, s4,
3333
s5), leaveOpen),
34-
[var s1, var s2, var s3, var s4, var s5, var s6] => new SparseStream<(Stream, Stream, Stream, Stream, Stream, Stream, Stream)>((stream, s1, s2, s3, s4,
34+
[var s1, var s2, var s3, var s4, var s5, var s6] => SparseStream.Create<(Stream, Stream, Stream, Stream, Stream, Stream, Stream)>((stream,
35+
s1, s2, s3, s4,
3536
s5, s6), leaveOpen),
3637
{ Length: int.MaxValue } => throw new InsufficientMemoryException(),
37-
_ => new UnboundedSparseStream(stream, others, leaveOpen),
38+
_ => SparseStream.Create(stream, others, leaveOpen),
3839
};
3940

4041
/// <summary>

src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<ImplicitUsings>true</ImplicitUsings>
99
<IsAotCompatible>false</IsAotCompatible>
1010
<Features>nullablePublicOnly</Features>
11-
<VersionPrefix>5.26.0</VersionPrefix>
11+
<VersionPrefix>5.26.1</VersionPrefix>
1212
<VersionSuffix></VersionSuffix>
1313
<Authors>.NET Foundation</Authors>
1414
<Product>.NEXT Family of Libraries</Product>

0 commit comments

Comments
 (0)