Skip to content

Commit

Permalink
Release 5.17.2
Browse files Browse the repository at this point in the history
  • Loading branch information
sakno committed Jan 3, 2025
1 parent 703ce02 commit 78ba6bf
Show file tree
Hide file tree
Showing 125 changed files with 782 additions and 540 deletions.
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
Release Notes
====

# 01-03-2025
<a href="https://www.nuget.org/packages/dotnext/5.17.2">DotNext 5.17.2</a>
* Improved AOT compatibility
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.17.2">DotNext.Metaprogramming 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.unsafe/5.17.2">DotNext.Unsafe 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.threading/5.17.2">DotNext.Threading 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.io/5.17.2">DotNext.IO 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.17.2">DotNext.Net.Cluster 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.17.2">DotNext.AspNetCore.Cluster 5.17.2</a>
* Fixed nullability attributes

# 12-30-2024
<a href="https://www.nuget.org/packages/dotnext.io/5.17.1">DotNext.IO 5.17.1</a>
* Fixed `EndOfStreamException` caused by async read from `PoolingBufferedStream`
Expand Down
59 changes: 23 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,42 +44,29 @@ 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: 12-29-2024

This release is aimed to improve AOT compatibility. All the examples in the repo are now AOT compatible.
<a href="https://www.nuget.org/packages/dotnext/5.17.0">DotNext 5.17.0</a>
* Fixed AOT compatibility in `TaskType` class
* Added [ISpanFormattable](https://learn.microsoft.com/en-us/dotnet/api/system.ispanformattable) and [IParsable&lt;T&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.iparsable-1) interfaces to `HttpEndPoint`
* Introduced `TryEncodeAsUtf8` extension method for `SpanWriter<T>`
* Added more factory methods to `DotNext.Buffers.Memory` class to create [ReadOnlySequence&lt;T&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.readonlysequence-1)
* `Intrinsics.KeepAlive` is introduced for value types
* Added `Synchronization.Wait()` synchronous methods for blocking wait of [value tasks](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask) without wait handles

<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.17.0">DotNext.Metaprogramming 5.17.0</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.unsafe/5.17.0">DotNext.Unsafe 5.17.0</a>
* Improved AOT support
* Fixed finalizer for unmanaged memory manager that allows to release the allocated unmanaged memory automatically by GC to avoid memory leak
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.threading/5.17.0">DotNext.Threading 5.17.0</a>
* Improved AOT support

<a href="https://www.nuget.org/packages/dotnext.io/5.17.1">DotNext.IO 5.17.1</a>
* Reduced memory consumption for applications that use `FileReader` and `FileWriter` classes. These classes are now implemented by using lazy buffer pattern. It means that the different instances can reuse the same buffer taken from the pool
* Fixed [255](https://github.com/dotnet/dotNext/issues/255)
* `PoolingBufferedStream` is introduced to replace classic [BufferedStream](https://learn.microsoft.com/en-us/dotnet/api/system.io.bufferedstream). This class supports memory pooling and implements lazy buffer pattern

<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.17.0">DotNext.Net.Cluster 5.17.0</a>
* Improved AOT support

<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.17.0">DotNext.AspNetCore.Cluster 5.17.0</a>
* Improved AOT support
* Fixed [254](https://github.com/dotnet/dotNext/issues/254)

<a href="https://www.nuget.org/packages/dotnext.maintenanceservices/0.5.0">DotNext.MaintenanceServices 0.5.0</a>
* Improved AOT support
Release Date: 01-03-2025

<a href="https://www.nuget.org/packages/dotnext/5.17.2">DotNext 5.17.2</a>
* Improved AOT compatibility
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.17.2">DotNext.Metaprogramming 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.unsafe/5.17.2">DotNext.Unsafe 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.threading/5.17.2">DotNext.Threading 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.io/5.17.2">DotNext.IO 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.17.2">DotNext.Net.Cluster 5.17.2</a>
* Fixed nullability attributes

<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.17.2">DotNext.AspNetCore.Cluster 5.17.2</a>
* Fixed nullability attributes

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

Expand Down
63 changes: 63 additions & 0 deletions src/DotNext.AotTests/Runtime/BoxedValueTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
namespace DotNext.Runtime;

[TestClass]
public class BoxedValueTests
{
[TestMethod]
public void BoxUnbox()
{
var obj = (BoxedValue<int>)42;
Assert.AreEqual(42.GetHashCode(), obj.GetHashCode());
Assert.AreEqual(42, obj.Unbox());
Assert.AreEqual(42, obj);
Assert.AreEqual(typeof(int), obj.GetType());
}

[TestMethod]
public void Unwrap()
{
object? obj = null;
Assert.IsNull(BoxedValue<int>.GetTypedReference(obj));

obj = 42;
Assert.AreEqual(42, BoxedValue<int>.GetTypedReference(obj));

obj = string.Empty;
Assert.ThrowsException<ArgumentException>(() => BoxedValue<int>.GetTypedReference(obj));
}

[TestMethod]
public void ToUntypedReference()
{
ValueType obj = BoxedValue<int>.Box(42);
Assert.AreEqual(42, obj);
}

private struct MutableStruct
{
public int Value;
}

[TestMethod]
public void BitwiseCopyImmutable()
{
var boxed1 = (BoxedValue<int>)42;
var boxed2 = boxed1.Copy();
Assert.AreNotSame(boxed1, boxed2);
Assert.AreEqual(42, boxed1);
Assert.AreEqual(42, boxed2);
}

[TestMethod]
public void BitwiseCopyMutable()
{
var boxed1 = (BoxedValue<MutableStruct>)new MutableStruct();
var boxed2 = boxed1.Copy();
Assert.AreNotSame(boxed1, boxed2);

boxed1.Unbox().Value = 42;
boxed2.Unbox().Value = 43;

Assert.AreNotEqual(boxed1.Unbox().Value, boxed2.Unbox().Value);
}
}
187 changes: 187 additions & 0 deletions src/DotNext.AotTests/Runtime/ValueReferenceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace DotNext.Runtime;

[TestClass]
public class ValueReferenceTests
{
[TestMethod]
public void MutableFieldRef()
{
var obj = new MyClass { AnotherField = string.Empty };
var reference = new ValueReference<int>(obj, ref obj.Field);

obj.Field = 20;
Assert.AreEqual(obj.Field, reference.Value);

reference.Value = 42;
Assert.AreEqual(obj.Field, reference.Value);
Assert.IsTrue(string.IsNullOrEmpty(obj.AnotherField));
}

[TestMethod]
public void ImmutableFieldRef()
{
var obj = new MyClass { AnotherField = string.Empty };
var reference = new ReadOnlyValueReference<int>(obj, in obj.Field);

obj.Field = 20;
Assert.AreEqual(obj.Field, reference.Value);

Assert.AreEqual(obj.Field, reference.Value);
Assert.IsTrue(string.IsNullOrEmpty(obj.AnotherField));
}

[TestMethod]
public void MutableToImmutableRef()
{
var obj = new MyClass { AnotherField = string.Empty };
var reference = new ValueReference<int>(obj, ref obj.Field);
ReadOnlyValueReference<int> roReference = reference;

obj.Field = 20;
Assert.AreEqual(roReference.Value, reference.Value);

reference.Value = 42;
Assert.AreEqual(roReference.Value, reference.Value);
}

[TestMethod]
public void MutableRefEquality()
{
var obj = new MyClass { AnotherField = string.Empty };
var reference1 = new ValueReference<int>(obj, ref obj.Field);
var reference2 = new ValueReference<int>(obj, ref obj.Field);

Assert.AreEqual(reference1, reference2);
}

[TestMethod]
public void ImmutableRefEquality()
{
var obj = new MyClass { AnotherField = string.Empty };
var reference1 = new ReadOnlyValueReference<int>(obj, in obj.Field);
var reference2 = new ReadOnlyValueReference<int>(obj, in obj.Field);

Assert.AreEqual(reference1, reference2);
}

[TestMethod]
public void ReferenceToArray()
{
var array = new string[1];
var reference = new ValueReference<string>(array, 0)
{
Value = "Hello, world!"
};

Assert.AreSame(array[0], reference.Value);
Assert.AreSame(array[0], reference.ToString());
}

[TestMethod]
public void MutableEmptyRef()
{
var reference = default(ValueReference<string>);
Assert.IsTrue(reference.IsEmpty);
Assert.IsNull(reference.ToString());

Span<string> span = reference;
Assert.IsTrue(span.IsEmpty);

Assert.ThrowsException<NullReferenceException>((Func<string>)reference);
Assert.ThrowsException<NullReferenceException>(((Action<string>)reference).Bind(string.Empty));
}

[TestMethod]
public void ImmutableEmptyRef()
{
var reference = default(ReadOnlyValueReference<string>);
Assert.IsTrue(reference.IsEmpty);
Assert.IsNull(reference.ToString());

ReadOnlySpan<string> span = reference;
Assert.IsTrue(span.IsEmpty);

Assert.ThrowsException<NullReferenceException>((Func<string>)reference);
}

[TestMethod]
public void AnonymousValue()
{
var reference = new ValueReference<int>(42);
Assert.AreEqual(42, reference.Value);

((Action<int>)reference).Invoke(52);
Assert.AreEqual(52, ToFunc<ValueReference<int>, int>(reference).Invoke());

ReadOnlyValueReference<int> roRef = reference;
Assert.AreEqual(52, roRef.Value);
Assert.AreEqual(52, ToFunc<ReadOnlyValueReference<int>, int>(reference).Invoke());
}

private static Func<T> ToFunc<TSupplier, T>(TSupplier supplier)
where TSupplier : ISupplier<T>
=> supplier.ToDelegate();

[TestMethod]
public void IncorrectReference()
{
byte[] empty = [];
Assert.ThrowsException<ArgumentOutOfRangeException>(() => new ValueReference<byte>(empty, ref MemoryMarshal.GetArrayDataReference(empty)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => new ReadOnlyValueReference<byte>(empty, ref MemoryMarshal.GetArrayDataReference(empty)));
}

[TestMethod]
public void ReferenceSize()
{
Assert.AreEqual(Unsafe.SizeOf<ValueReference<float>>(), nint.Size + nint.Size);
}

[TestMethod]
public void BoxedValueInterop()
{
var boxedInt = BoxedValue<int>.Box(42);
ValueReference<int> reference = boxedInt;

boxedInt.Unbox() = 56;
Assert.AreEqual(boxedInt, reference.Value);
}

[TestMethod]
public void ArrayCovariance()
{
string[] array = ["a", "b"];
Assert.ThrowsException<ArrayTypeMismatchException>(() => new ValueReference<object>(array, 0));

var roRef = new ReadOnlyValueReference<object>(array, 1);
Assert.AreEqual("b", roRef.Value);
}

[TestMethod]
public void SpanInterop()
{
var reference = new ValueReference<int>(42);
Span<int> span = reference;
Assert.AreEqual(1, span.Length);

Assert.IsTrue(Unsafe.AreSame(in reference.Value, in span[0]));
}

[TestMethod]
public void ReadOnlySpanInterop()
{
ReadOnlyValueReference<int> reference = new ValueReference<int>(42);
ReadOnlySpan<int> span = reference;
Assert.AreEqual(1, span.Length);

Assert.IsTrue(Unsafe.AreSame(in reference.Value, in span[0]));
}

private record MyClass
{
internal int Field;
internal string? AnotherField;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public async ValueTask<long> ReadAsync<TEntryImpl, TList>(TList entries, long? s
where TList : notnull, IReadOnlyList<TEntryImpl>
{
var result = 0L;
foreach (var entry in entries)
for (var i = 0; i < entries.Count; i++)
{
using var buffer = await entry.ToMemoryAsync();
using var buffer = await entries[i].ToMemoryAsync(token: token);
result += buffer.Length;
}

Expand Down
6 changes: 3 additions & 3 deletions src/DotNext.IO/Buffers/BufferWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public static int Write(this ref BufferWriterSlim<byte> writer, scoped ReadOnlyS
}

private static bool TryFormat<T>(IBufferWriter<byte> writer, T value, Span<char> buffer, in EncodingContext context, LengthFormat? lengthFormat, ReadOnlySpan<char> format, IFormatProvider? provider, out long bytesWritten)
where T : notnull, ISpanFormattable
where T : ISpanFormattable
{
if (!value.TryFormat(buffer, out var charsWritten, format, provider))
{
Expand Down Expand Up @@ -228,7 +228,7 @@ private static bool TryFormat<T>(IBufferWriter<byte> writer, T value, Span<char>
/// <returns>The number of written bytes.</returns>
[SkipLocalsInit]
public static long Format<T>(this IBufferWriter<byte> writer, T value, in EncodingContext context, LengthFormat? lengthFormat, ReadOnlySpan<char> format = default, IFormatProvider? provider = null, MemoryAllocator<char>? allocator = null)
where T : notnull, ISpanFormattable
where T : ISpanFormattable
{
// attempt to allocate char buffer on the stack
Span<char> charBuffer = stackalloc char[SpanOwner<char>.StackallocThreshold];
Expand Down Expand Up @@ -259,7 +259,7 @@ public static long Format<T>(this IBufferWriter<byte> writer, T value, in Encodi
/// <returns>The number of written bytes.</returns>
/// <exception cref="InsufficientMemoryException"><paramref name="writer"/> has not enough free space to place UTF-8 bytes.</exception>
public static int Format<T>(this IBufferWriter<byte> writer, T value, LengthFormat? lengthFormat, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
where T : notnull, IUtf8SpanFormattable
where T : IUtf8SpanFormattable
{
var expectedLengthSize = lengthFormat switch
{
Expand Down
Loading

0 comments on commit 78ba6bf

Please sign in to comment.