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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Orleans.Serialization/Cloning/IDeepCopier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ internal static class ShallowCopyableTypes
[typeof(UInt128)] = true,
[typeof(Int128)] = true,
#endif
[typeof(System.Numerics.BigInteger)] = true,
#if NET5_0_OR_GREATER
[typeof(Half)] = true,
#endif
Expand Down
84 changes: 84 additions & 0 deletions src/Orleans.Serialization/Codecs/BigIntegerCodec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using Orleans.Serialization.Buffers;
using Orleans.Serialization.WireProtocol;

namespace Orleans.Serialization.Codecs;

/// <summary>
/// Serializer for <see cref="BigInteger"/>.
/// </summary>
[RegisterSerializer]
public sealed class BigIntegerCodec : IFieldCodec<BigInteger>
{
/// <inheritdoc/>
void IFieldCodec<BigInteger>.WriteField<TBufferWriter>(ref Writer<TBufferWriter> writer, uint fieldIdDelta,
Type expectedType, BigInteger value)
{
ReferenceCodec.MarkValueField(writer.Session);
writer.WriteFieldHeader(fieldIdDelta, expectedType, typeof(BigInteger), WireType.LengthPrefixed);

WriteField(ref writer, value);
}

/// <summary>
/// Writes a field without type info (expected type is statically known).
/// </summary>
/// <typeparam name="TBufferWriter">The buffer writer type.</typeparam>
/// <param name="writer">The writer.</param>
/// <param name="fieldIdDelta">The field identifier delta.</param>
/// <param name="value">The value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteField<TBufferWriter>(ref Writer<TBufferWriter> writer, uint fieldIdDelta, BigInteger value) where TBufferWriter : IBufferWriter<byte>
{
Comment thread
galvesribeiro marked this conversation as resolved.
ReferenceCodec.MarkValueField(writer.Session);
writer.WriteFieldHeaderExpected(fieldIdDelta, WireType.LengthPrefixed);

WriteField(ref writer, value);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WriteField<TBufferWriter>(ref Writer<TBufferWriter> writer, BigInteger value)
where TBufferWriter : IBufferWriter<byte>
{
var byteCount = value.GetByteCount();
writer.WriteVarUInt32((uint)byteCount);

writer.EnsureContiguous(byteCount);
if (value.TryWriteBytes(writer.WritableSpan, out var bytesWritten))
{
writer.AdvanceSpan(bytesWritten);
}
else
{
writer.Write(value.ToByteArray());
}
}

/// <inheritdoc/>
BigInteger IFieldCodec<BigInteger>.ReadValue<TInput>(ref Reader<TInput> reader, Field field) => ReadValue(ref reader, field);

/// <summary>
/// Reads a value.
/// </summary>
/// <typeparam name="TInput">The reader input type.</typeparam>
/// <param name="reader">The reader.</param>
/// <param name="field">The field.</param>
/// <returns>The value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BigInteger ReadValue<TInput>(ref Reader<TInput> reader, Field field)
{
ReferenceCodec.MarkValueField(reader.Session);

if (field.WireType != WireType.LengthPrefixed)
{
throw new UnexpectedLengthPrefixValueException(nameof(BigInteger), 0, 0);
}

var length = reader.ReadVarUInt32();
var bytes = reader.ReadBytes(length);
return new BigInteger(bytes);
}
}
110 changes: 108 additions & 2 deletions test/Orleans.Serialization.UnitTests/BuiltInCodecTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Numerics;
using Xunit;
using Microsoft.FSharp.Collections;
using Xunit.Abstractions;
Expand Down Expand Up @@ -50,7 +51,7 @@ public enum MyEnum : short
/// - Collection types (List, Dictionary, arrays, etc.)
/// - Nullable types
/// - Enums
///
///
/// These codecs are designed for:
/// - High performance with minimal allocations
/// - Compact binary representation
Expand Down Expand Up @@ -1339,6 +1340,111 @@ public class UInt128CopierTests(ITestOutputHelper output) : CopierTester<UInt128

protected override Action<Action<UInt128>> ValueProvider => assert => Gen.ULong.Select(Gen.ULong).Sample(value => assert(new (value.V0, value.V1)));
}

public class BigIntegerCodecTests(ITestOutputHelper output) : FieldCodecTester<BigInteger, BigIntegerCodec>(output)
{
// New behavior in .NET 9: https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/9.0/biginteger-limit#new-behavior
protected override int[] MaxSegmentSizes => [(2^31)-1];
Comment thread
galvesribeiro marked this conversation as resolved.

protected override BigInteger CreateValue()
{
var bytes = new byte[Random.Next(1, 100)];
Random.NextBytes(bytes);
return new BigInteger(bytes);
}

protected override BigInteger[] TestValues =>
[
BigInteger.Zero,
BigInteger.One,
BigInteger.MinusOne,
new BigInteger(byte.MaxValue),
new BigInteger(byte.MaxValue) + 1,
new BigInteger(ushort.MaxValue),
new BigInteger(ushort.MaxValue) + 1,
new BigInteger(uint.MaxValue),
new BigInteger(uint.MaxValue) + 1,
new BigInteger(ulong.MaxValue),
new BigInteger(ulong.MaxValue) + 1,
(BigInteger)Int128.MaxValue,
(BigInteger)Int128.MaxValue + 1,
(BigInteger)UInt128.MaxValue,
(BigInteger)UInt128.MaxValue + 1,
-new BigInteger(byte.MaxValue),
-new BigInteger(ushort.MaxValue),
-new BigInteger(uint.MaxValue),
-new BigInteger(ulong.MaxValue),
-(BigInteger)Int128.MaxValue,
-(BigInteger)UInt128.MaxValue,
BigInteger.Parse("123456789012345678901234567890123456789012345678901234567890"),
BigInteger.Parse("-123456789012345678901234567890123456789012345678901234567890"),
];

protected override Action<Action<BigInteger>> ValueProvider => assert =>
{
Gen.Byte.Array.Sample(bytes =>
{
if (bytes.Length > 0)
{
assert(new BigInteger(bytes));
}
});
};
}

public class BigIntegerCopierTests(ITestOutputHelper output) : CopierTester<BigInteger, ShallowCopier<BigInteger>>(output)
{
protected override bool IsImmutable => true;

protected override BigInteger CreateValue()
{
var bytes = new byte[Random.Next(1, 100)];
Random.NextBytes(bytes);
return new BigInteger(bytes);
}

protected override BigInteger[] TestValues =>
[
BigInteger.Zero,
BigInteger.One,
BigInteger.MinusOne,
new BigInteger(byte.MaxValue),
new BigInteger(byte.MaxValue) + 1,
new BigInteger(ushort.MaxValue),
new BigInteger(ushort.MaxValue) + 1,
new BigInteger(uint.MaxValue),
new BigInteger(uint.MaxValue) + 1,
new BigInteger(ulong.MaxValue),
new BigInteger(ulong.MaxValue) + 1,
#if NET7_0_OR_GREATER
(BigInteger)Int128.MaxValue,
(BigInteger)Int128.MaxValue + 1,
(BigInteger)UInt128.MaxValue,
(BigInteger)UInt128.MaxValue + 1,
#endif
-new BigInteger(byte.MaxValue),
-new BigInteger(ushort.MaxValue),
-new BigInteger(uint.MaxValue),
-new BigInteger(ulong.MaxValue),
#if NET7_0_OR_GREATER
-(BigInteger)Int128.MaxValue,
-(BigInteger)UInt128.MaxValue,
#endif
BigInteger.Parse("123456789012345678901234567890123456789012345678901234567890"),
BigInteger.Parse("-123456789012345678901234567890123456789012345678901234567890"),
];

protected override Action<Action<BigInteger>> ValueProvider => assert =>
{
Gen.Byte.Array.Sample(bytes =>
{
if (bytes.Length > 0)
{
assert(new BigInteger(bytes));
}
});
};
}
#endif

public class UInt64CodecTests(ITestOutputHelper output) : FieldCodecTester<ulong, UInt64Codec>(output)
Expand Down Expand Up @@ -3913,4 +4019,4 @@ public class CancellationTokenCopierTests(ITestOutputHelper output) : CopierTest
new CancellationToken(true)
];
}
}
}
Loading