Skip to content
Draft
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
49 changes: 49 additions & 0 deletions src/libraries/Common/src/Polyfills/BigIntegerPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Numerics;

/// <summary>Provides downlevel polyfills for instance methods on <see cref="BigInteger"/>.</summary>
internal static class BigIntegerPolyfills
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a lot of local-type-name collisions, but I wouldn't be surprised if we end up with some.

It'd be more defensive to do Common/src/Polyfills/System/Numerics/BigIntegerPolyfills.cs, and that better matches our file naming conventions:

The source code file should be named .cs and should be placed in a directory structure that matches its namespace relative to its project directory. Ex. System\IO\Stream.cs

{
extension(BigInteger self)
{
public byte[] ToByteArray(bool isUnsigned, bool isBigEndian)
{
if (isUnsigned && self.Sign < 0)
throw new OverflowException();

byte[] littleEndianBytes = self.ToByteArray();

if (!isUnsigned && !isBigEndian)
return littleEndianBytes;

int length = littleEndianBytes.Length;

// For unsigned, trim a single most-significant 0x00 sign-extension byte
// from the end of the little-endian array.
if (isUnsigned && length > 1 && littleEndianBytes[length - 1] == 0x00)
length--;

if (isBigEndian)
{
byte[] result = new byte[length];

for (int i = 0; i < length; i++)
{
result[i] = littleEndianBytes[length - 1 - i];
}

return result;
}

if (length == littleEndianBytes.Length)
return littleEndianBytes;

byte[] trimmed = new byte[length];
Array.Copy(littleEndianBytes, trimmed, length);

return trimmed;
}
}
}
80 changes: 80 additions & 0 deletions src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Buffers.Binary;

/// <summary>Provides downlevel polyfills for static methods on <see cref="BinaryPrimitives"/>.</summary>
internal static class BinaryPrimitivesPolyfills
{
extension(BinaryPrimitives)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ReadHalfBigEndian(ReadOnlySpan<byte> source)
{
return BitConverter.IsLittleEndian
? BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read<ushort>(source))
: MemoryMarshal.Read<ushort>(source);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteHalfBigEndian(Span<byte> destination, ushort value)
{
if (BitConverter.IsLittleEndian)
{
ushort tmp = BinaryPrimitives.ReverseEndianness(value);
MemoryMarshal.Write(destination, ref tmp);
}
else
{
MemoryMarshal.Write(destination, ref value);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ReadSingleBigEndian(ReadOnlySpan<byte> source)
{
return BitConverter.IsLittleEndian
? BitConverter.Int32BitsToSingle(BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read<int>(source)))
: MemoryMarshal.Read<float>(source);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteSingleBigEndian(Span<byte> destination, float value)
{
if (BitConverter.IsLittleEndian)
{
int tmp = BinaryPrimitives.ReverseEndianness(BitConverter.SingleToInt32Bits(value));
MemoryMarshal.Write(destination, ref tmp);
}
else
{
MemoryMarshal.Write(destination, ref value);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double ReadDoubleBigEndian(ReadOnlySpan<byte> source)
{
return BitConverter.IsLittleEndian
? BitConverter.Int64BitsToDouble(BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read<long>(source)))
: MemoryMarshal.Read<double>(source);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteDoubleBigEndian(Span<byte> destination, double value)
{
if (BitConverter.IsLittleEndian)
{
long tmp = BinaryPrimitives.ReverseEndianness(BitConverter.DoubleToInt64Bits(value));
MemoryMarshal.Write(destination, ref tmp);
}
else
{
MemoryMarshal.Write(destination, ref value);
}
}
}
}
25 changes: 17 additions & 8 deletions src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,29 @@ internal static class BitConverterPolyfills
{
extension(BitConverter)
{
public static int SingleToInt32Bits(float value)
{
unsafe { return *(int*)&value; }
}

public static float Int32BitsToSingle(int value)
{
unsafe { return *(float*)&value; }
}

public static uint SingleToUInt32Bits(float value)
{
unsafe
{
return *(uint*)&value;
}
unsafe { return *(uint*)&value; }
}

public static float UInt32BitsToSingle(uint value)
{
unsafe { return *(float*)&value; }
}

public static ulong DoubleToUInt64Bits(double value)
{
unsafe
{
return *(ulong*)&value;
}
unsafe { return *(ulong*)&value; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@

namespace System;

/// <summary>Provides downlevel polyfills for instance members on <see cref="DateTimeOffset"/>.</summary>
/// <summary>Provides downlevel polyfills for members on <see cref="DateTimeOffset"/>.</summary>
internal static class DateTimeOffsetPolyfills
{
private const long UnixEpochTicks = 719162L * 10000 * 1000 * 60 * 60 * 24;

extension(DateTimeOffset value)
{
public int TotalOffsetMinutes =>
(int)(value.Offset.Ticks / TimeSpan.TicksPerMinute);
}

extension(DateTimeOffset)
{
public static DateTimeOffset UnixEpoch => new DateTimeOffset(UnixEpochTicks, TimeSpan.Zero);
}
}
16 changes: 16 additions & 0 deletions src/libraries/Common/src/Polyfills/DecimalPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System;

/// <summary>Provides downlevel polyfills for static methods on <see cref="decimal"/>.</summary>
internal static class DecimalPolyfills
{
extension(decimal)
{
public static void GetBits(decimal d, Span<int> destination)
{
decimal.GetBits(d).CopyTo(destination);
}
}
}
25 changes: 25 additions & 0 deletions src/libraries/Common/src/Polyfills/StackPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;

namespace System.Collections.Generic;

/// <summary>Provides downlevel polyfills for instance methods on <see cref="Stack{T}"/>.</summary>
internal static class StackPolyfills
{
extension<T>(Stack<T> stack)
{
public bool TryPop([MaybeNullWhen(false)] out T result)
{
if (stack.Count > 0)
{
result = stack.Pop();
return true;
}

result = default;
return false;
}
}
}
13 changes: 13 additions & 0 deletions src/libraries/Common/src/System/StringPolyfills.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@

public static bool Contains(this string s, char value) =>
s.IndexOf(value) >= 0;

internal delegate void SpanAction<T, in TArg>(Span<T> span, TArg arg);

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build linux-x64 debug Libraries_WithPackages)

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build linux-x64 debug Libraries_WithPackages)

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build linux-x64 debug Libraries_WithPackages)

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build Source-Build (Linux_x64))

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build Source-Build (Linux_x64))

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 20 in src/libraries/Common/src/System/StringPolyfills.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop

src/libraries/Common/src/System/StringPolyfills.cs#L20

src/libraries/Common/src/System/StringPolyfills.cs(20,55): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)

extension(string)
{
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
{
char[] arr = new char[length];
action(arr, state);

return new string(arr);
}
Comment on lines 18 to +30
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StringPolyfills now defines SpanAction/string.Create(...), which introduces a Span<T> dependency. This file is also included by some non-.NETCoreApp projects without a System.Memory reference (e.g., src/libraries/System.Data.Odbc/src/System.Data.Odbc.csproj links StringPolyfills.cs but doesn’t reference System.Memory), so this change is likely to break netframework/netstandard builds. Consider moving the string.Create polyfill into the existing Span-polyfills include path (so it’s only compiled when IncludeSpanPolyfills/System.Memory is present), or otherwise guarding it so projects that only need the simple string extensions don’t require Span<T>.

Copilot uses AI. Check for mistakes.
}
}
}

Expand Down
17 changes: 11 additions & 6 deletions src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,25 @@ System.Formats.Cbor.CborWriter</PackageDescription>
<Compile Include="System\Formats\Cbor\CborContentException.cs" />
</ItemGroup>

<ItemGroup>
<Compile Include="System\Formats\Cbor\HalfHelpers.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<Compile Include="System\Formats\Cbor\CborHelpers.netcoreapp.cs" />
<Compile Include="System\Formats\Cbor\HalfHelpers.netcoreapp.cs" />
<Compile Include="System\Formats\Cbor\Reader\CborReader.Simple.netcoreapp.cs" />
<Compile Include="System\Formats\Cbor\Writer\CborWriter.Simple.netcoreapp.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<Compile Include="System\Formats\Cbor\CborHelpers.netstandard.cs" />
<Compile Include="System\Formats\Cbor\HalfHelpers.netstandard.cs" />
<Compile Include="System\Formats\Cbor\Writer\CborWriter.Simple.netstandard.cs" />
<Compile Include="$(CommonPath)Polyfills\ArrayPolyfills.cs" Link="Common\Polyfills\ArrayPolyfills.cs" />
<Compile Include="$(CommonPath)Polyfills\BigIntegerPolyfills.cs" Link="Common\Polyfills\BigIntegerPolyfills.cs" />
<Compile Include="$(CommonPath)Polyfills\BinaryPrimitivesPolyfills.cs" Link="Common\Polyfills\BinaryPrimitivesPolyfills.cs" />
<Compile Include="$(CommonPath)Polyfills\BitConverterPolyfills.cs" Link="Common\Polyfills\BitConverterPolyfills.cs" />
<Compile Include="$(CommonPath)Polyfills\DateTimeOffsetPolyfills.cs" Link="Common\Polyfills\DateTimeOffsetPolyfills.cs" />
<Compile Include="$(CommonPath)Polyfills\DecimalPolyfills.cs" Link="Common\Polyfills\DecimalPolyfills.cs" />
<Compile Include="$(CommonPath)Polyfills\HashCodePolyfills.cs" Link="Common\Polyfills\HashCodePolyfills.cs" />
<Compile Include="$(CommonPath)Polyfills\ArrayPolyfills.cs" Link="Common\Polyfills\ArrayPolyfills.cs" />
<Compile Include="$(CommonPath)Polyfills\StackPolyfills.cs" Link="Common\Polyfills\StackPolyfills.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
Expand Down

This file was deleted.

Loading
Loading