diff --git a/src/libraries/Common/src/Polyfills/BigIntegerPolyfills.cs b/src/libraries/Common/src/Polyfills/BigIntegerPolyfills.cs new file mode 100644 index 00000000000000..1f5fee7e88a281 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/BigIntegerPolyfills.cs @@ -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; + +/// Provides downlevel polyfills for instance methods on . +internal static class BigIntegerPolyfills +{ + 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; + } + } +} diff --git a/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.cs b/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.cs new file mode 100644 index 00000000000000..502e9b28948539 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.cs @@ -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; + +/// Provides downlevel polyfills for static methods on . +internal static class BinaryPrimitivesPolyfills +{ + extension(BinaryPrimitives) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ReadHalfBigEndian(ReadOnlySpan source) + { + return BitConverter.IsLittleEndian + ? BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read(source)) + : MemoryMarshal.Read(source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteHalfBigEndian(Span 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 source) + { + return BitConverter.IsLittleEndian + ? BitConverter.Int32BitsToSingle(BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read(source))) + : MemoryMarshal.Read(source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteSingleBigEndian(Span 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 source) + { + return BitConverter.IsLittleEndian + ? BitConverter.Int64BitsToDouble(BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read(source))) + : MemoryMarshal.Read(source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteDoubleBigEndian(Span destination, double value) + { + if (BitConverter.IsLittleEndian) + { + long tmp = BinaryPrimitives.ReverseEndianness(BitConverter.DoubleToInt64Bits(value)); + MemoryMarshal.Write(destination, ref tmp); + } + else + { + MemoryMarshal.Write(destination, ref value); + } + } + } +} diff --git a/src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs b/src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs index dd418d32f94406..befcdcd49f5e75 100644 --- a/src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs @@ -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; } } } } diff --git a/src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs b/src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs index 1dcbae615775ad..600e89d4934d81 100644 --- a/src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs @@ -3,12 +3,19 @@ namespace System; -/// Provides downlevel polyfills for instance members on . +/// Provides downlevel polyfills for members on . 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); + } } diff --git a/src/libraries/Common/src/Polyfills/DecimalPolyfills.cs b/src/libraries/Common/src/Polyfills/DecimalPolyfills.cs new file mode 100644 index 00000000000000..2eef0ab0727297 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/DecimalPolyfills.cs @@ -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; + +/// Provides downlevel polyfills for static methods on . +internal static class DecimalPolyfills +{ + extension(decimal) + { + public static void GetBits(decimal d, Span destination) + { + decimal.GetBits(d).CopyTo(destination); + } + } +} diff --git a/src/libraries/Common/src/Polyfills/StackPolyfills.cs b/src/libraries/Common/src/Polyfills/StackPolyfills.cs new file mode 100644 index 00000000000000..1f786d4d60cb14 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/StackPolyfills.cs @@ -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; + +/// Provides downlevel polyfills for instance methods on . +internal static class StackPolyfills +{ + extension(Stack stack) + { + public bool TryPop([MaybeNullWhen(false)] out T result) + { + if (stack.Count > 0) + { + result = stack.Pop(); + return true; + } + + result = default; + return false; + } + } +} diff --git a/src/libraries/Common/src/System/StringPolyfills.cs b/src/libraries/Common/src/System/StringPolyfills.cs index da679b268204f4..08b423087040ff 100644 --- a/src/libraries/Common/src/System/StringPolyfills.cs +++ b/src/libraries/Common/src/System/StringPolyfills.cs @@ -16,6 +16,19 @@ public static bool EndsWith(this string s, char value) => public static bool Contains(this string s, char value) => s.IndexOf(value) >= 0; + + internal delegate void SpanAction(Span span, TArg arg); + + extension(string) + { + public static string Create(int length, TState state, SpanAction action) + { + char[] arr = new char[length]; + action(arr, state); + + return new string(arr); + } + } } } diff --git a/src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csproj b/src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csproj index 04dfc7a44cf491..6988d80473b6a0 100644 --- a/src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csproj +++ b/src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csproj @@ -38,20 +38,25 @@ System.Formats.Cbor.CborWriter + + + + - - - + - - + + + + + - + diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborHelpers.netcoreapp.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborHelpers.netcoreapp.cs deleted file mode 100644 index 95dc9bde882ba4..00000000000000 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborHelpers.netcoreapp.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers; -using System.Buffers.Binary; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace System.Formats.Cbor -{ - internal static partial class CborHelpers - { - public static readonly DateTimeOffset UnixEpoch = DateTimeOffset.UnixEpoch; - - public static BigInteger CreateBigIntegerFromUnsignedBigEndianBytes(byte[] bytes) - => new BigInteger(bytes, isUnsigned: true, isBigEndian: true); - - public static byte[] CreateUnsignedBigEndianBytesFromBigInteger(BigInteger value) - => value.ToByteArray(isUnsigned: true, isBigEndian: true); - - public static void GetBitsFromDecimal(decimal d, Span destination) - => decimal.GetBits(d, destination); - - public static string BuildStringFromIndefiniteLengthTextString(int length, TState state, SpanAction action) - => string.Create(length, state, action); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Half ReadHalfBigEndian(ReadOnlySpan source) - => BinaryPrimitives.ReadHalfBigEndian(source); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe float ReadSingleBigEndian(ReadOnlySpan source) - => BinaryPrimitives.ReadSingleBigEndian(source); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double ReadDoubleBigEndian(ReadOnlySpan source) - => BinaryPrimitives.ReadDoubleBigEndian(source); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteSingleBigEndian(Span destination, float value) - => BinaryPrimitives.WriteSingleBigEndian(destination, value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteDoubleBigEndian(Span destination, double value) - => BinaryPrimitives.WriteDoubleBigEndian(destination, value); - } -} diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborHelpers.netstandard.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborHelpers.netstandard.cs deleted file mode 100644 index adb2eb362c0308..00000000000000 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborHelpers.netstandard.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers.Binary; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Formats.Cbor -{ - internal static partial class CborHelpers - { - private const long UnixEpochTicks = 719162L /*Number of days from 1/1/0001 to 12/31/1969*/ * 10000 * 1000 * 60 * 60 * 24; /* Ticks per day.*/ - - public static readonly DateTimeOffset UnixEpoch = new DateTimeOffset(UnixEpochTicks, TimeSpan.Zero); - - public static BigInteger CreateBigIntegerFromUnsignedBigEndianBytes(byte[] bigEndianBytes) - { - if (bigEndianBytes.Length == 0) - { - return new BigInteger(bigEndianBytes); - } - - byte[] temp; - if ((bigEndianBytes[0] & 0x80) != 0) // Is negative? - { - // To prevent positive values from being misinterpreted as negative values, - // you can add a zero-byte value to the most significant side of the array. - // Right in this case as it is Big-endian. - var bytesPlusOne = new byte[bigEndianBytes.Length + 1]; - bigEndianBytes.CopyTo(bytesPlusOne.AsSpan(1)); - temp = bytesPlusOne; - } - else - { - temp = bigEndianBytes; - } - - // Reverse endianness - temp.AsSpan().Reverse(); - - return new BigInteger(temp); - } - - public static byte[] CreateUnsignedBigEndianBytesFromBigInteger(BigInteger value) - { - byte[] littleEndianBytes = value.ToByteArray(); - - if (littleEndianBytes.Length == 1) - { - return littleEndianBytes; - } - - Span bytesAsSpan = littleEndianBytes; - bytesAsSpan.Reverse(); - - int start = 0; - for (int i = 0; i < bytesAsSpan.Length; i++) - { - if (bytesAsSpan[i] == 0x00) - { - start++; - } - else - { - break; - } - } - - Debug.Assert(start <= 1); // If there is a case where we trim more than one byte, we want to add it to our tests. - - return start == 0 ? littleEndianBytes : bytesAsSpan.Slice(start).ToArray(); - } - - public static void GetBitsFromDecimal(decimal d, Span destination) - { - decimal.GetBits(d).CopyTo(destination); - } - - public delegate void SpanAction(Span span, TArg arg); - - public static string BuildStringFromIndefiniteLengthTextString(int length, TState state, SpanAction action) - { - char[] arr = new char[length]; - action(arr, state); - return new string(arr); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort ReadHalfBigEndian(ReadOnlySpan source) - { - ushort value = BitConverter.IsLittleEndian ? - BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read(source)) : - MemoryMarshal.Read(source); - - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteHalfBigEndian(Span 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 source) - { - return BitConverter.IsLittleEndian ? - Int32BitsToSingle(BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read(source))) : - MemoryMarshal.Read(source); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteSingleBigEndian(Span destination, float value) - { - if (BitConverter.IsLittleEndian) - { - int tmp = BinaryPrimitives.ReverseEndianness(SingleToInt32Bits(value)); - MemoryMarshal.Write(destination, ref tmp); - } - else - { - MemoryMarshal.Write(destination, ref value); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double ReadDoubleBigEndian(ReadOnlySpan source) - { - return BitConverter.IsLittleEndian ? - BitConverter.Int64BitsToDouble(BinaryPrimitives.ReverseEndianness(MemoryMarshal.Read(source))) : - MemoryMarshal.Read(source); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteDoubleBigEndian(Span destination, double value) - { - if (BitConverter.IsLittleEndian) - { - long tmp = BinaryPrimitives.ReverseEndianness(BitConverter.DoubleToInt64Bits(value)); - MemoryMarshal.Write(destination, ref tmp); - } - else - { - MemoryMarshal.Write(destination, ref value); - } - } - - internal static uint SingleToUInt32Bits(float value) - => (uint)SingleToInt32Bits(value); - - internal static unsafe int SingleToInt32Bits(float value) - => *((int*)&value); - - internal static float UInt32BitsToSingle(uint value) - => Int32BitsToSingle((int)value); - - internal static unsafe float Int32BitsToSingle(int value) - => *((float*)&value); - } - - internal static class StackExtensions - { - public static bool TryPop(this Stack stack, [MaybeNullWhen(false)] out T result) - { - if (stack.Count > 0) - { - result = stack.Pop(); - return true; - } - - result = default; - return false; - } - } -} diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netstandard.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.cs similarity index 91% rename from src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netstandard.cs rename to src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.cs index c4d191be9ad54c..618da7820f635a 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netstandard.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.cs @@ -6,8 +6,15 @@ namespace System.Formats.Cbor { - internal static partial class HalfHelpers + internal static class HalfHelpers { +#if NET + public static float HalfToFloat(Half value) + => (float)value; + + public static double HalfToDouble(Half value) + => (double)value; +#else // Half constants private const ushort HalfExponentMask = 0x7C00; private const ushort HalfExponentShift = 10; @@ -53,7 +60,7 @@ public static float HalfToFloat(ushort value) { if (sig == 0) { - return CborHelpers.UInt32BitsToSingle(sign ? FloatSignMask : 0); // Positive / Negative zero + return BitConverter.UInt32BitsToSingle(sign ? FloatSignMask : 0); // Positive / Negative zero } (exp, sig) = NormSubnormalF16Sig(sig); exp -= 1; @@ -62,7 +69,7 @@ public static float HalfToFloat(ushort value) return CreateSingle(sign, (byte)(exp + 0x70), sig << 13); static float CreateSingle(bool sign, byte exp, uint sig) - => CborHelpers.Int32BitsToSingle((int)(((sign ? 1U : 0U) << FloatSignShift) + ((uint)exp << FloatExponentShift) + sig)); + => BitConverter.Int32BitsToSingle((int)(((sign ? 1U : 0U) << FloatSignShift) + ((uint)exp << FloatExponentShift) + sig)); } public static bool HalfIsNaN(ushort value) @@ -119,7 +126,7 @@ private static float CreateSingleNaN(bool sign, ulong significand) uint signInt = (sign ? 1U : 0U) << FloatSignShift; uint sigInt = (uint)(significand >> 41); - return CborHelpers.UInt32BitsToSingle(signInt | NaNBits | sigInt); + return BitConverter.UInt32BitsToSingle(signInt | NaNBits | sigInt); } #endregion @@ -128,7 +135,7 @@ public static ushort FloatToHalf(float value) { const int SingleMaxExponent = 0xFF; - uint floatInt = CborHelpers.SingleToUInt32Bits(value); + uint floatInt = BitConverter.SingleToUInt32Bits(value); bool sign = (floatInt & FloatSignMask) >> FloatSignShift != 0; int exp = (int)(floatInt & FloatExponentMask) >> FloatExponentShift; uint sig = floatInt & FloatSignificandMask; @@ -201,5 +208,6 @@ private static ushort RoundPackToHalf(bool sign, short exp, ushort sig) private static uint ShiftRightJam(uint i, int dist) => dist < 31 ? (i >> dist) | (i << (-dist & 31) != 0 ? 1U : 0U) : (i != 0 ? 1U : 0U); #endregion +#endif } } diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netcoreapp.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netcoreapp.cs deleted file mode 100644 index 96514354547b24..00000000000000 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netcoreapp.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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.Formats.Cbor -{ - internal static partial class HalfHelpers - { - public static unsafe float HalfToFloat(Half value) - => (float)value; - - public static unsafe double HalfToDouble(Half value) - => (double)value; - } -} diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Simple.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Simple.cs index 228ae0bb78d9d4..9eaaafed5b4244 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Simple.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Simple.cs @@ -29,14 +29,14 @@ public float ReadSingle() { case CborAdditionalInfo.Additional16BitData: EnsureReadCapacity(buffer, 1 + sizeof(ushort)); - result = HalfHelpers.HalfToFloat(CborHelpers.ReadHalfBigEndian(buffer.Slice(1))); + result = HalfHelpers.HalfToFloat(BinaryPrimitives.ReadHalfBigEndian(buffer.Slice(1))); AdvanceBuffer(1 + sizeof(ushort)); AdvanceDataItemCounters(); return result; case CborAdditionalInfo.Additional32BitData: EnsureReadCapacity(buffer, 1 + sizeof(float)); - result = CborHelpers.ReadSingleBigEndian(buffer.Slice(1)); + result = BinaryPrimitives.ReadSingleBigEndian(buffer.Slice(1)); AdvanceBuffer(1 + sizeof(float)); AdvanceDataItemCounters(); return result; @@ -70,21 +70,21 @@ public double ReadDouble() { case CborAdditionalInfo.Additional16BitData: EnsureReadCapacity(buffer, 1 + sizeof(short)); - result = HalfHelpers.HalfToDouble(CborHelpers.ReadHalfBigEndian(buffer.Slice(1))); + result = HalfHelpers.HalfToDouble(BinaryPrimitives.ReadHalfBigEndian(buffer.Slice(1))); AdvanceBuffer(1 + sizeof(short)); AdvanceDataItemCounters(); return result; case CborAdditionalInfo.Additional32BitData: EnsureReadCapacity(buffer, 1 + sizeof(float)); - result = CborHelpers.ReadSingleBigEndian(buffer.Slice(1)); + result = BinaryPrimitives.ReadSingleBigEndian(buffer.Slice(1)); AdvanceBuffer(1 + sizeof(float)); AdvanceDataItemCounters(); return result; case CborAdditionalInfo.Additional64BitData: EnsureReadCapacity(buffer, 1 + sizeof(double)); - result = CborHelpers.ReadDoubleBigEndian(buffer.Slice(1)); + result = BinaryPrimitives.ReadDoubleBigEndian(buffer.Slice(1)); AdvanceBuffer(1 + sizeof(double)); AdvanceDataItemCounters(); return result; diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.String.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.String.cs index 8ddb9986212974..64dfe490620423 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.String.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.String.cs @@ -387,7 +387,7 @@ private string ReadIndefiniteLengthTextStringConcatenated() } // build the string using range data - string output = CborHelpers.BuildStringFromIndefiniteLengthTextString(concatenatedStringSize, (ranges, _data.Slice(_offset), utf8Encoding), BuildString); + string output = string.Create(concatenatedStringSize, (ranges, _data.Slice(_offset), utf8Encoding), BuildString); AdvanceBuffer(encodingLength); AdvanceDataItemCounters(); diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Tag.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Tag.cs index 2dd4c4d1d9abe1..5ea7e9950baac9 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Tag.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Tag.cs @@ -125,7 +125,7 @@ public DateTimeOffset ReadUnixTimeSeconds() } TimeSpan timespan = TimeSpan.FromSeconds(seconds); - return CborHelpers.UnixEpoch + timespan; + return DateTimeOffset.UnixEpoch + timespan; default: throw new CborContentException(SR.Cbor_Reader_InvalidUnixTimeEncoding); @@ -175,7 +175,7 @@ public BigInteger ReadBigInteger() } byte[] unsignedBigEndianEncoding = ReadByteString(); - BigInteger unsignedValue = CborHelpers.CreateBigIntegerFromUnsignedBigEndianBytes(unsignedBigEndianEncoding); + BigInteger unsignedValue = CreateBigIntegerFromUnsignedBigEndianBytes(unsignedBigEndianEncoding); return isNegative ? -1 - unsignedValue : unsignedValue; } catch @@ -291,5 +291,29 @@ private CborTag PeekTagCore(out int bytesRead) return result; } + + private static BigInteger CreateBigIntegerFromUnsignedBigEndianBytes(byte[] bigEndianBytes) +#if NET + => new BigInteger(bigEndianBytes, isUnsigned: true, isBigEndian: true); +#else + { + if (bigEndianBytes.Length == 0) + return BigInteger.Zero; + + byte[] littleEndianBytes = (byte[])bigEndianBytes.Clone(); + Array.Reverse(littleEndianBytes); + + // BigInteger(byte[]) expects little-endian signed (two's complement). + // If the high bit is set, append a 0x00 so BigInteger doesn't interpret it as negative. + if ((littleEndianBytes[littleEndianBytes.Length - 1] & 0x80) != 0) + { + byte[] extended = new byte[littleEndianBytes.Length + 1]; + Array.Copy(littleEndianBytes, extended, littleEndianBytes.Length); + littleEndianBytes = extended; + } + + return new BigInteger(littleEndianBytes); + } +#endif } } diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Simple.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Simple.cs index 1b7e8dd00074be..72345928dc2d0f 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Simple.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Simple.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Runtime.CompilerServices; namespace System.Formats.Cbor @@ -63,7 +64,7 @@ private void WriteSingleCore(float value) { EnsureWriteCapacity(1 + sizeof(float)); WriteInitialByte(new CborInitialByte(CborMajorType.Simple, CborAdditionalInfo.Additional32BitData)); - CborHelpers.WriteSingleBigEndian(_buffer.AsSpan(_offset), value); + BinaryPrimitives.WriteSingleBigEndian(_buffer.AsSpan(_offset), value); _offset += sizeof(float); AdvanceDataItemCounters(); } @@ -72,7 +73,7 @@ private void WriteDoubleCore(double value) { EnsureWriteCapacity(1 + sizeof(double)); WriteInitialByte(new CborInitialByte(CborMajorType.Simple, CborAdditionalInfo.Additional64BitData)); - CborHelpers.WriteDoubleBigEndian(_buffer.AsSpan(_offset), value); + BinaryPrimitives.WriteDoubleBigEndian(_buffer.AsSpan(_offset), value); _offset += sizeof(double); AdvanceDataItemCounters(); } diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Simple.netstandard.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Simple.netstandard.cs index f257ce1f4b4e02..9ca15fc591d0df 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Simple.netstandard.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Simple.netstandard.cs @@ -27,7 +27,7 @@ private void WriteHalf(ushort value) } else { - CborHelpers.WriteHalfBigEndian(_buffer.AsSpan(_offset), value); + BinaryPrimitives.WriteHalfBigEndian(_buffer.AsSpan(_offset), value); } _offset += sizeof(ushort); AdvanceDataItemCounters(); @@ -37,7 +37,7 @@ private void WriteHalf(ushort value) internal static bool TryConvertSingleToHalf(float value, out ushort result) { result = HalfHelpers.FloatToHalf(value); - return float.IsNaN(value) || CborHelpers.SingleToInt32Bits(HalfHelpers.HalfToFloat(result)) == CborHelpers.SingleToInt32Bits(value); + return float.IsNaN(value) || BitConverter.SingleToInt32Bits(HalfHelpers.HalfToFloat(result)) == BitConverter.SingleToInt32Bits(value); } } } diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.cs index a3b4cd4470e6f1..594fcf66edf8c3 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.cs @@ -89,7 +89,7 @@ public void WriteBigInteger(BigInteger value) { bool isUnsigned = value.Sign >= 0; BigInteger unsignedValue = isUnsigned ? value : -1 - value; - byte[] unsignedBigEndianEncoding = CborHelpers.CreateUnsignedBigEndianBytesFromBigInteger(unsignedValue); + byte[] unsignedBigEndianEncoding = unsignedValue.ToByteArray(isUnsigned: true, isBigEndian: true); WriteTag(isUnsigned ? CborTag.UnsignedBigNum : CborTag.NegativeBigNum); WriteByteString(unsignedBigEndianEncoding); @@ -143,8 +143,7 @@ internal static class DecimalHelpers public static void Deconstruct(decimal value, out decimal mantissa, out byte scale) { Span buf = stackalloc int[4]; - CborHelpers.GetBitsFromDecimal(value, buf); - + decimal.GetBits(value, buf); int flags = buf[3]; bool isNegative = (flags & SignMask) == SignMask; mantissa = new decimal(lo: buf[0], mid: buf[1], hi: buf[2], isNegative: isNegative, scale: 0); @@ -155,8 +154,7 @@ public static void Deconstruct(decimal value, out decimal mantissa, out byte sca private static decimal ReconstructFromNegativeScale(decimal mantissa, byte scale) { Span buf = stackalloc int[4]; - CborHelpers.GetBitsFromDecimal(mantissa, buf); - + decimal.GetBits(mantissa, buf); int flags = buf[3]; bool isNegative = (flags & SignMask) == SignMask; Debug.Assert((flags & ScaleMask) == 0, "mantissa argument should be integral.");